forked from unom/punktfunk
`VaapiDecoder::new` and `VulkanDecoder::new` create a hwdevice and then do several more fallible things with it — resolve `vkWaitSemaphores`, find the decoder, alloc the context, open it — and every one of those branches unref'd the device by hand, with a `Drop` doing it once more. Six hand-written unrefs between them, each one a line somebody has to remember when adding a step. `video_libav::AvBuffer` owns it instead, so an early `bail!` releases it and the branches carry nothing. It is a deliberate second copy of `pf-encode`'s type: the crates do not depend on each other (host encode and client decode share no code path), and this one needs something the host's does not — see below — so hoisting it to a shared crate would mean giving that crate an ffmpeg dependency and both sets of semantics to save about twenty lines. That extra piece is `into_raw`. `pick_vulkan` hands its frames context to the codec (`(*ctx).hw_frames_ctx = fr` — the codec unrefs it when the context closes), so the wrapper must give up ownership rather than drop. Making the transfer explicit is the point: dropping an `AvBuffer` there too would be exactly the double-unref this type exists to prevent. Two `av_buffer_unref` calls survive in `video_vulkan.rs` on purpose. One runs before ownership is taken (the `av_hwdevice_ctx_init` failure, ahead of `from_raw`); the other releases the codec's OWN pre-existing frames ctx before we replace it, which was never ours to model. Field order preserved: `hw_device` stays declared after `ctx`, so it still releases after each `Drop` frees packet/frame/context — the order the hand-written unref had. Verified on .21 (CachyOS, FFmpeg 62): `cargo check --workspace --all-targets` clean at exit 0 with zero errors — the workspace-wide check owed since the AvBuffer commit — plus pf-client-core 34 passed / 0 failed and pf-encode 33 passed / 0 failed. The decoders themselves still need real VAAPI/Vulkan playback to exercise.