### Initialize FFmpeg Bindings Dynamically Source: https://context7.com/ruslan-b/ffmpeg.autogen/llms.txt Installs NuGet packages and configures the FFmpeg library path for dynamic loading. Ensure the correct DLLs are present in the specified path before initialization. ```csharp // NuGet packages: // // using FFmpeg.AutoGen.Abstractions; using FFmpeg.AutoGen.Bindings.DynamicallyLoaded; using System.Runtime.InteropServices; // Point to the directory containing avcodec-61.dll, avutil-59.dll, etc. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { DynamicallyLoadedBindings.LibrariesPath = @"C:\ffmpeg\bin\x64"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { DynamicallyLoadedBindings.LibrariesPath = "/usr/lib/x86_64-linux-gnu"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { DynamicallyLoadedBindings.LibrariesPath = "/opt/homebrew/lib"; } // Resolve all function pointers from the native shared libraries DynamicallyLoadedBindings.Initialize(); // Verify FFmpeg is loaded correctly Console.WriteLine($"FFmpeg version: {ffmpeg.av_version_info()}"); // Output: FFmpeg version: 7.1.1 ``` -------------------------------- ### Configure FFmpeg Logging Callback Source: https://context7.com/ruslan-b/ffmpeg.autogen/llms.txt Sets the global log verbosity and installs a managed delegate as the FFmpeg log sink. The callback processes raw format strings and va_list, rendering messages using av_log_format_line. ```csharp using System.Runtime.InteropServices; using FFmpeg.AutoGen.Abstractions; unsafe void SetupLogging() { ffmpeg.av_log_set_level(ffmpeg.AV_LOG_WARNING); av_log_set_callback_callback logCallback = (p0, level, format, vl) => { if (level > ffmpeg.av_log_get_level()) return; const int bufSize = 1024; var buf = stackalloc byte[bufSize]; var printPrefix = 1; ffmpeg.av_log_format_line(p0, level, format, vl, buf, bufSize, &printPrefix); var line = Marshal.PtrToStringAnsi((IntPtr)buf); Console.Error.Write(line); }; ffmpeg.av_log_set_callback(logCallback); } ``` -------------------------------- ### Create a new feature branch Source: https://github.com/ruslan-b/ffmpeg.autogen/blob/main/CONTRIBUTING.md Use this command to create a new branch for your feature development, starting from the main branch. ```bash git checkout -b feature/my-new-feature ``` -------------------------------- ### Install FFmpeg on macOS Source: https://github.com/ruslan-b/ffmpeg.autogen/blob/main/README.md Install FFmpeg on macOS using Homebrew. You may need to set the static ffmpeg.RootPath to the full path of the FFmpeg libraries. ```bash brew install ffmpeg ``` -------------------------------- ### Build the project Source: https://github.com/ruslan-b/ffmpeg.autogen/blob/main/CONTRIBUTING.md Compile the project in Release mode using the .NET CLI. ```bash dotnet build -c Release ``` -------------------------------- ### Build and test the project Source: https://github.com/ruslan-b/ffmpeg.autogen/blob/main/CONTRIBUTING.md Build the project in Release configuration and run tests to ensure changes are functional and do not introduce regressions. ```bash dotnet build -c Release ``` ```bash dotnet test -c Release ``` -------------------------------- ### Initialize and Use VideoStreamDecoder Source: https://context7.com/ruslan-b/ffmpeg.autogen/llms.txt Demonstrates how to instantiate VideoStreamDecoder to open a media URL and decode video frames one by one. Hardware acceleration can be optionally enabled during initialization. Ensure FFmpeg libraries are correctly set up. ```csharp using System; using System.Drawing; using FFmpeg.AutoGen.Abstractions; public sealed unsafe class VideoStreamDecoder : IDisposable { private readonly AVCodecContext* _pCodecContext; private readonly AVFormatContext* _pFormatContext; private readonly AVFrame* _pFrame; private readonly AVPacket* _pPacket; private readonly AVFrame* _receivedFrame; private readonly int _streamIndex; public string CodecName { get; } public Size FrameSize { get; } public AVPixelFormat PixelFormat { get; } public VideoStreamDecoder(string url, AVHWDeviceType hwDevice = AVHWDeviceType.AV_HWDEVICE_TYPE_NONE) { _pFormatContext = ffmpeg.avformat_alloc_context(); _receivedFrame = ffmpeg.av_frame_alloc(); var pFmtCtx = _pFormatContext; ffmpeg.avformat_open_input(&pFmtCtx, url, null, null).ThrowExceptionIfError(); ffmpeg.avformat_find_stream_info(_pFormatContext, null).ThrowExceptionIfError(); AVCodec* codec = null; _streamIndex = ffmpeg .av_find_best_stream(_pFormatContext, AVMediaType.AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0) .ThrowExceptionIfError(); _pCodecContext = ffmpeg.avcodec_alloc_context3(codec); if (hwDevice != AVHWDeviceType.AV_HWDEVICE_TYPE_NONE) ffmpeg.av_hwdevice_ctx_create(&_pCodecContext->hw_device_ctx, hwDevice, null, null, 0) .ThrowExceptionIfError(); ffmpeg.avcodec_parameters_to_context(_pCodecContext, _pFormatContext->streams[_streamIndex]->codecpar).ThrowExceptionIfError(); ffmpeg.avcodec_open2(_pCodecContext, codec, null).ThrowExceptionIfError(); CodecName = ffmpeg.avcodec_get_name(codec->id); FrameSize = new Size(_pCodecContext->width, _pCodecContext->height); PixelFormat = _pCodecContext->pix_fmt; _pPacket = ffmpeg.av_packet_alloc(); _pFrame = ffmpeg.av_frame_alloc(); } public bool TryDecodeNextFrame(out AVFrame frame) { ffmpeg.av_frame_unref(_pFrame); ffmpeg.av_frame_unref(_receivedFrame); int error; do { try { do { ffmpeg.av_packet_unref(_pPacket); error = ffmpeg.av_read_frame(_pFormatContext, _pPacket); if (error == ffmpeg.AVERROR_EOF) { frame = *_pFrame; return false; } error.ThrowExceptionIfError(); } while (_pPacket->stream_index != _streamIndex); ffmpeg.avcodec_send_packet(_pCodecContext, _pPacket).ThrowExceptionIfError(); } finally { ffmpeg.av_packet_unref(_pPacket); } error = ffmpeg.avcodec_receive_frame(_pCodecContext, _pFrame); } while (error == ffmpeg.AVERROR(ffmpeg.EAGAIN)); error.ThrowExceptionIfError(); if (_pCodecContext->hw_device_ctx != null) { ffmpeg.av_hwframe_transfer_data(_receivedFrame, _pFrame, 0).ThrowExceptionIfError(); frame = *_receivedFrame; } else frame = *_pFrame; return true; } public void Dispose() { var pFrame = _pFrame; ffmpeg.av_frame_free(&pFrame); var pPacket = _pPacket; ffmpeg.av_packet_free(&pPacket); AVCodecContext** avctx = stackalloc[] { _pCodecContext }; ffmpeg.avcodec_free_context(avctx); var pFmtCtx = _pFormatContext; ffmpeg.avformat_close_input(&pFmtCtx); } } // --- Usage --- var url = "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_1MB.mp4"; using var decoder = new VideoStreamDecoder(url, AVHWDeviceType.AV_HWDEVICE_TYPE_NONE); Console.WriteLine($"Codec: {decoder.CodecName}, Size: {decoder.FrameSize}"); // Output: Codec: h264, Size: {Width=1920, Height=1080} int n = 0; while (decoder.TryDecodeNextFrame(out var frame)) Console.WriteLine($"Decoded frame #{n++}, pts={frame.pts}"); ``` -------------------------------- ### Run project tests Source: https://github.com/ruslan-b/ffmpeg.autogen/blob/main/CONTRIBUTING.md Execute all tests in the project using the .NET CLI to verify functionality. ```bash dotnet test -c Release ``` -------------------------------- ### Download FFmpeg binaries Source: https://github.com/ruslan-b/ffmpeg.autogen/blob/main/CONTRIBUTING.md Execute this PowerShell script to download the necessary FFmpeg DLLs for development. ```powershell .\FFmpeg\download-ffmpeg.ps1 ``` -------------------------------- ### Enumerate and Create HW Decode Contexts Source: https://context7.com/ruslan-b/ffmpeg.autogen/llms.txt Call `av_hwdevice_iterate_types` to discover supported hardware acceleration types on the host system. Pass the chosen `AVHWDeviceType` to the decoder constructor. Defaults to DXVA2 on Windows, falling back to the first available type. ```csharp using System; using System.Collections.Generic; using FFmpeg.AutoGen.Abstractions; static AVHWDeviceType ChooseHWDecoder() { var available = new Dictionary(); var type = AVHWDeviceType.AV_HWDEVICE_TYPE_NONE; int n = 0; while ((type = ffmpeg.av_hwdevice_iterate_types(type)) != AVHWDeviceType.AV_HWDEVICE_TYPE_NONE) { Console.WriteLine($" [{++n}] {type}"); available[n] = type; } if (available.Count == 0) return AVHWDeviceType.AV_HWDEVICE_TYPE_NONE; // Default: prefer DXVA2 on Windows, fallback to first available var preferred = available .Where(kv => kv.Value == AVHWDeviceType.AV_HWDEVICE_TYPE_DXVA2) .Select(kv => kv.Value) .FirstOrDefault(); return preferred != AVHWDeviceType.AV_HWDEVICE_TYPE_NONE ? preferred : available[1]; } // Map HW device type to its surface pixel format static AVPixelFormat HWPixelFormat(AVHWDeviceType dev) => dev switch { AVHWDeviceType.AV_HWDEVICE_TYPE_CUDA => AVPixelFormat.AV_PIX_FMT_CUDA, AVHWDeviceType.AV_HWDEVICE_TYPE_VAAPI => AVPixelFormat.AV_PIX_FMT_VAAPI, AVHWDeviceType.AV_HWDEVICE_TYPE_DXVA2 => AVPixelFormat.AV_PIX_FMT_NV12, AVHWDeviceType.AV_HWDEVICE_TYPE_D3D11VA => AVPixelFormat.AV_PIX_FMT_NV12, AVHWDeviceType.AV_HWDEVICE_TYPE_VIDEOTOOLBOX => AVPixelFormat.AV_PIX_FMT_VIDEOTOOLBOX, AVHWDeviceType.AV_HWDEVICE_TYPE_QSV => AVPixelFormat.AV_PIX_FMT_QSV, AVHWDeviceType.AV_HWDEVICE_TYPE_OPENCL => AVPixelFormat.AV_PIX_FMT_OPENCL, _ => AVPixelFormat.AV_PIX_FMT_NONE }; // Example: decode with the best available HW accelerator var hwType = ChooseHWDecoder(); using var decoder = new VideoStreamDecoder("input.mp4", hwType); var srcFmt = hwType == AVHWDeviceType.AV_HWDEVICE_TYPE_NONE ? decoder.PixelFormat : HWPixelFormat(hwType); ``` -------------------------------- ### Rescale and Convert Pixel Formats Between AVFrames Source: https://context7.com/ruslan-b/ffmpeg.autogen/llms.txt `sws_getContext` creates a software scaler context for resizing and converting pixel formats. `sws_scale` applies the transformation into a pre-allocated output buffer. This class handles the context creation, buffer allocation, and scaling operations. ```csharp using System; using System.Drawing; using System.Runtime.InteropServices; using FFmpeg.AutoGen.Abstractions; public sealed unsafe class VideoFrameConverter : IDisposable { private readonly IntPtr _bufferPtr; private readonly Size _dstSize; private readonly byte_ptr4 _dstData; private readonly int4 _dstLinesize; private readonly SwsContext* _pSws; public VideoFrameConverter(Size srcSize, AVPixelFormat srcFmt, Size dstSize, AVPixelFormat dstFmt) { _dstSize = dstSize; _pSws = ffmpeg.sws_getContext( srcSize.Width, srcSize.Height, srcFmt, dstSize.Width, dstSize.Height, dstFmt, (int)SwsFlags.SWS_FAST_BILINEAR, null, null, null); if (_pSws == null) throw new ApplicationException("Could not initialize sws context."); var bufferSize = ffmpeg.av_image_get_buffer_size(dstFmt, dstSize.Width, dstSize.Height, 1); _bufferPtr = Marshal.AllocHGlobal(bufferSize); _dstData = new byte_ptr4(); _dstLinesize = new int4(); ffmpeg.av_image_fill_arrays(ref _dstData, ref _dstLinesize, (byte*)_bufferPtr, dstFmt, dstSize.Width, dstSize.Height, 1); } public AVFrame Convert(AVFrame src) { ffmpeg.sws_scale(_pSws, src.data, src.linesize, 0, src.height, _dstData, _dstLinesize); var data = new byte_ptr8(); data.UpdateFrom(_dstData); var linesize = new int8(); linesize.UpdateFrom(_dstLinesize); return new AVFrame { data = data, linesize = linesize, width = _dstSize.Width, height = _dstSize.Height }; } public void Dispose() { Marshal.FreeHGlobal(_bufferPtr); ffmpeg.sws_freeContext(_pSws); } } // --- Usage: decode and extract BGRA frames --- using var decoder = new VideoStreamDecoder("clip.mp4"); using var converter = new VideoFrameConverter( decoder.FrameSize, decoder.PixelFormat, decoder.FrameSize, AVPixelFormat.@AV_PIX_FMT_BGRA); while (decoder.TryDecodeNextFrame(out var raw)) { var bgra = converter.Convert(raw); // bgra.data[0] now points to BGRA pixel rows } ``` -------------------------------- ### Populate AVFrame Data and Linesize with Fixed-Size Arrays Source: https://context7.com/ruslan-b/ffmpeg.autogen/llms.txt Manually populate `AVFrame`'s `data` and `linesize` using `byte_ptr8` and `int8`. These types implement `IFixedArray` for indexer access and conversion to managed arrays. Ensure correct buffer calculations for YUV420P planes. ```csharp using FFmpeg.AutoGen.Abstractions; // Manually populate an AVFrame data/linesize for a YUV420P frame unsafe void FillFrameFromBuffer(byte* yuvBuffer, int width, int height) { var data = new byte_ptr8(); var linesize = new int8(); // Plane 0: Y, Plane 1: U, Plane 2: V data[0] = yuvBuffer; data[1] = yuvBuffer + width * height; data[2] = yuvBuffer + width * height * 5 / 4; linesize[0] = width; linesize[1] = width / 2; linesize[2] = width / 2; var frame = new AVFrame { data = data, linesize = linesize, width = width, height = height, format = (int)AVPixelFormat.AV_PIX_FMT_YUV420P }; // Copy data/linesize out as managed arrays byte*[] dataArray = data.ToArray(); // length 8 int[] linesizeArray = linesize.ToArray(); // length 8 Console.WriteLine($"Y linesize: {linesizeArray[0]}"); // 1920 } // Bridging byte_ptr4 ↔ byte_ptr8 (used by VideoFrameConverter) var src4 = new byte_ptr4(); var dst8 = new byte_ptr8(); dst8.UpdateFrom(src4); // copies first 4 pointers ``` -------------------------------- ### Decode Video URL and Save Frames as JPEG Source: https://context7.com/ruslan-b/ffmpeg.autogen/llms.txt This snippet shows how to initialize FFmpeg bindings, decode frames from a video URL, convert them to BGRA format, and save them as JPEG images. Ensure FFmpeg libraries are accessible and the output directory 'frames' exists. ```csharp using System; using System.IO; using FFmpeg.AutoGen.Abstractions; using FFmpeg.AutoGen.Bindings.DynamicallyLoaded; using SkiaSharp; // 1. Bootstrap DynamicallyLoadedBindings.LibrariesPath = "/usr/lib/x86_64-linux-gnu"; DynamicallyLoadedBindings.Initialize(); ffmpeg.av_log_set_level(ffmpeg.AV_LOG_WARNING); Directory.CreateDirectory("frames"); // 2. Decode var url = "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_1MB.mp4"; using var decoder = new VideoStreamDecoder(url); Console.WriteLine($"Codec: {decoder.CodecName}, {decoder.FrameSize.Width}×{decoder.FrameSize.Height}"); // 3. Convert YUV → BGRA using var converter = new VideoFrameConverter( decoder.FrameSize, decoder.PixelFormat, decoder.FrameSize, AVPixelFormat.@AV_PIX_FMT_BGRA); // 4. Save frames as JPEG int i = 0; while (decoder.TryDecodeNextFrame(out var raw) && i < 250) { unsafe { var bgra = converter.Convert(raw); var info = new SKImageInfo(bgra.width, bgra.height, SKColorType.Bgra8888, SKAlphaType.Opaque); using var bmp = new SKBitmap(); bmp.InstallPixels(info, (IntPtr)bgra.data[0]); using var fs = File.Create($"frames/frame.{i:D8}.jpg"); bmp.Encode(fs, SKEncodedImageFormat.Jpeg, 90); } Console.WriteLine($"Saved frame {i++}"); } // Output: frames/frame.00000000.jpg … frames/frame.00000249.jpg ``` -------------------------------- ### Generate Bindings with CppSharp Source: https://github.com/ruslan-b/ffmpeg.autogen/blob/main/README.md Steps to regenerate the *.g.cs files using the FFmpeg.AutoGen.CppSharpUnsafeGenerator. Requires Visual Studio 2022 with C# and C++ workloads. ```csharp FFmpeg.AutoGen.CppSharpUnsafeGenerator; ``` -------------------------------- ### Iterate container metadata tags using C# Source: https://context7.com/ruslan-b/ffmpeg.autogen/llms.txt Iterates through key/value metadata tags embedded in container formats using `av_dict_get`. Requires `AV_DICT_IGNORE_SUFFIX` flag and an empty key to walk all entries. ```csharp using System; using System.Collections.Generic; using System.Runtime.InteropServices; using FFmpeg.AutoGen.Abstractions; static unsafe IReadOnlyDictionary GetMetadata(AVFormatContext* pFmtCtx) { var result = new Dictionary(); AVDictionaryEntry* tag = null; while ((tag = ffmpeg.av_dict_get( pFmtCtx->metadata, "", tag, ffmpeg.AV_DICT_IGNORE_SUFFIX)) != null) { var key = Marshal.PtrToStringAnsi((IntPtr)tag->key); var value = Marshal.PtrToStringAnsi((IntPtr)tag->value); result[key] = value; } return result; } // Example output for a typical MP4: // title = Big Buck Bunny // artist = Blender Foundation // date = 2008 // encoder = Lavf61.7.100 ``` -------------------------------- ### Resolve FFmpeg Function Pointers Dynamically Source: https://context7.com/ruslan-b/ffmpeg.autogen/llms.txt Dynamically load FFmpeg shared libraries at runtime and resolve native function pointers using `FunctionResolverBase.GetFunctionDelegate`. This process respects library dependencies and caches function pointers. Initialization can use a custom library path. ```csharp // This is handled automatically by DynamicallyLoadedBindings.Initialize(), // but the resolver can also be used directly for custom scenarios. using FFmpeg.AutoGen.Bindings.DynamicallyLoaded; // Library dependency order is managed automatically: // avutil → swresample → avcodec → avformat → avfilter → avdevice Console.WriteLine("Dependency map:"); foreach (var (lib, deps) in FunctionResolverBase.LibraryDependenciesMap) Console.WriteLine($" {lib,-14} depends on: [{string.Join(", ", deps)}]"); // Platform detection var platform = FunctionResolverFactory.GetPlatformId(); Console.WriteLine($"Platform: {platform}"); // Output on Linux: Platform: Unix // Initialize with a custom path DynamicallyLoadedBindings.LibrariesPath = "/opt/custom-ffmpeg/lib"; DynamicallyLoadedBindings.Initialize(); // All ffmpeg.* calls now dispatch through the custom build ``` -------------------------------- ### Encode AVFrames to H.264 NAL units using C# Source: https://context7.com/ruslan-b/ffmpeg.autogen/llms.txt Configures the libx264 encoder and drives the `avcodec_send_frame` / `avcodec_receive_packet` loop. Call `Drain()` after all frames are submitted to flush the encoder's internal buffer. ```csharp using System; using System.Drawing; using System.IO; using FFmpeg.AutoGen.Abstractions; public sealed unsafe class H264VideoStreamEncoder : IDisposable { private readonly AVCodec* _pCodec; private readonly AVCodecContext* _pCodecContext; private readonly Stream _output; private readonly Size _frameSize; public H264VideoStreamEncoder(Stream output, int fps, Size frameSize) { _output = output; _frameSize = frameSize; _pCodec = ffmpeg.avcodec_find_encoder(AVCodecID.AV_CODEC_ID_H264); if (_pCodec == null) throw new InvalidOperationException("H.264 encoder not found."); _pCodecContext = ffmpeg.avcodec_alloc_context3(_pCodec); _pCodecContext->width = frameSize.Width; _pCodecContext->height = frameSize.Height; _pCodecContext->time_base = new AVRational { num = 1, den = fps }; _pCodecContext->pix_fmt = AVPixelFormat.AV_PIX_FMT_YUV420P; ffmpeg.av_opt_set(_pCodecContext->priv_data, "preset", "veryslow", 0); ffmpeg.avcodec_open2(_pCodecContext, _pCodec, null).ThrowExceptionIfError(); } public void Encode(AVFrame frame) { var pkt = ffmpeg.av_packet_alloc(); try { ffmpeg.avcodec_send_frame(_pCodecContext, &frame).ThrowExceptionIfError(); int res; do { ffmpeg.av_packet_unref(pkt); res = ffmpeg.avcodec_receive_packet(_pCodecContext, pkt); if (res == 0) { using var ms = new UnmanagedMemoryStream(pkt->data, pkt->size); ms.CopyTo(_output); } } while (res == 0); } finally { ffmpeg.av_packet_free(&pkt); } } public void Drain() { var pkt = ffmpeg.av_packet_alloc(); try { ffmpeg.avcodec_send_frame(_pCodecContext, null).ThrowExceptionIfError(); int res; do { ffmpeg.av_packet_unref(pkt); res = ffmpeg.avcodec_receive_packet(_pCodecContext, pkt); if (res == 0) { using var ms = new UnmanagedMemoryStream(pkt->data, pkt->size); ms.CopyTo(_output); } } while (res == 0); } finally { ffmpeg.av_packet_free(&pkt); } } public void Dispose() => ffmpeg.av_free(_pCodecContext); } // --- Usage: transcode JPEG frames to H.264 raw stream --- var frames = Directory.GetFiles("./frames", "frame.*.jpg").OrderBy(x => x).ToArray(); using var outFile = File.Open("output.h264", FileMode.Create); using var encoder = new H264VideoStreamEncoder(outFile, fps: 25, frameSize: new Size(1920, 1080)); int n = 0; foreach (var jpg in frames) { // Load BGRA bitmap, convert to YUV420P via VideoFrameConverter, then encode var frame = LoadAndConvertFrame(jpg); // returns AVFrame in YUV420P frame.pts = n++ * 25; encoder.Encode(frame); } encoder.Drain(); Console.WriteLine("Encoding complete → output.h264"); ``` -------------------------------- ### ffmpeg.av_dict_get Source: https://context7.com/ruslan-b/ffmpeg.autogen/llms.txt Iterates through container metadata tags. This function is used to retrieve key-value pairs of metadata embedded within media container formats. ```APIDOC ## `ffmpeg.av_dict_get` — Iterate container metadata tags ### Description Container formats (MP4, MKV, etc.) embed metadata as key/value dictionaries on `AVFormatContext::metadata`. Iterate using `av_dict_get` with the `AV_DICT_IGNORE_SUFFIX` flag and an empty key to walk all entries. ### Method `AVDictionaryEntry* av_dict_get(AVDictionary *m, const char *key, AVDictionaryEntry *prev, int flags)` ### Parameters #### `m` (AVDictionary *) - The dictionary to iterate over. #### `key` (const char *) - An empty string (`""`) is used to iterate through all entries. #### `prev` (AVDictionaryEntry *) - The previous entry returned, or `null` for the first entry. #### `flags` (int) - `AV_DICT_IGNORE_SUFFIX` flag is recommended for iterating all entries. ### Return Value - Returns a pointer to an `AVDictionaryEntry` for the next matching key-value pair, or `null` if no more entries are found. ### Usage Example ```csharp using System.Collections.Generic; using System.Runtime.InteropServices; using FFmpeg.AutoGen.Abstractions; static unsafe IReadOnlyDictionary GetMetadata(AVFormatContext* pFmtCtx) { var result = new Dictionary(); AVDictionaryEntry* tag = null; while ((tag = ffmpeg.av_dict_get(pFmtCtx->metadata, "", tag, ffmpeg.AV_DICT_IGNORE_SUFFIX)) != null) { var key = Marshal.PtrToStringAnsi((IntPtr)tag->key); var value = Marshal.PtrToStringAnsi((IntPtr)tag->value); result[key] = value; } return result; } ``` ### Example Output ``` title = Big Buck Bunny artist = Blender Foundation date = 2008 encoder = Lavf61.7.100 ``` ``` -------------------------------- ### FFmpeg Error Handling with ThrowExceptionIfError Source: https://context7.com/ruslan-b/ffmpeg.autogen/llms.txt Converts negative FFmpeg error codes into managed exceptions. The `av_strerror` helper provides human-readable messages for error codes. ```csharp using System; using System.Runtime.InteropServices; using FFmpeg.AutoGen.Abstractions; internal static class FFmpegHelper { public static unsafe string av_strerror(int error) { const int bufSize = 1024; var buf = stackalloc byte[bufSize]; ffmpeg.av_strerror(error, buf, (ulong)bufSize); return Marshal.PtrToStringAnsi((IntPtr)buf); } // Usage: ffmpeg.avformat_open_input(&pFmtCtx, url, null, null).ThrowExceptionIfError(); public static int ThrowExceptionIfError(this int error) { if (error < 0) throw new ApplicationException(av_strerror(error)); return error; } } // Example: handle AVERROR_EOF explicitly unsafe void ReadPackets(AVFormatContext* pFmtCtx, AVPacket* pkt) { int ret; while ((ret = ffmpeg.av_read_frame(pFmtCtx, pkt)) >= 0) { // process packet ffmpeg.av_packet_unref(pkt); } if (ret != ffmpeg.AVERROR_EOF) ret.ThrowExceptionIfError(); // throws for real errors only } ``` -------------------------------- ### H264VideoStreamEncoder Source: https://context7.com/ruslan-b/ffmpeg.autogen/llms.txt Configures the libx264 encoder and drives the avcodec_send_frame / avcodec_receive_packet loop to produce compressed packets. Call Drain() after all frames are submitted to flush the encoder's internal buffer. ```APIDOC ## `H264VideoStreamEncoder` Class ### Description This class provides functionality to encode raw video frames into H.264 NAL units using FFmpeg's `libx264` encoder. It manages the encoder context, sending frames for encoding, and receiving compressed packets. The `Drain()` method is crucial for flushing any buffered data after all frames have been sent. ### Methods #### `Encode(AVFrame frame)` Sends an `AVFrame` to the encoder for processing. #### `Drain()` Flushes the encoder's internal buffer by sending a null frame, ensuring all encoded data is retrieved. #### `Dispose()` Frees the encoder's context resources. ### Usage Example ```csharp // Initialize encoder using var outFile = File.Open("output.h264", FileMode.Create); using var encoder = new H264VideoStreamEncoder(outFile, fps: 25, frameSize: new Size(1920, 1080)); // Encode frames int n = 0; foreach (var jpg in frames) { var frame = LoadAndConvertFrame(jpg); // returns AVFrame in YUV420P frame.pts = n++ * 25; encoder.Encode(frame); } // Drain the encoder encoder.Drain(); ``` ``` -------------------------------- ### VideoStreamDecoder Class Source: https://context7.com/ruslan-b/ffmpeg.autogen/llms.txt This class decodes video frames from a given media URL. It supports optional hardware acceleration and provides properties for codec name, frame size, and pixel format. ```APIDOC ## Class: VideoStreamDecoder ### Description Wraps the `avformat` / `avcodec` open+decode loop. It selects the best video stream, optionally creates a hardware accelerator context, and exposes `TryDecodeNextFrame` which drives the `avcodec_send_packet` / `avcodec_receive_frame` API. Hardware frames are automatically transferred to system memory via `av_hwframe_transfer_data`. ### Constructor `VideoStreamDecoder(string url, AVHWDeviceType hwDevice = AVHWDeviceType.AV_HWDEVICE_TYPE_NONE)` Opens a media URL and initializes the decoder. Optionally configures hardware acceleration. ### Properties - **CodecName** (string): The name of the video codec. - **FrameSize** (Size): The dimensions (width and height) of the video frames. - **PixelFormat** (AVPixelFormat): The pixel format of the decoded frames. ### Methods - **TryDecodeNextFrame()** (out AVFrame): Attempts to decode and return the next video frame. Returns `true` if a frame is successfully decoded, `false` if the end of the stream is reached. - **Dispose()**: Releases all resources held by the decoder. ### Usage Example ```csharp var url = "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_1MB.mp4"; using var decoder = new VideoStreamDecoder(url, AVHWDeviceType.AV_HWDEVICE_TYPE_NONE); Console.WriteLine($"Codec: {decoder.CodecName}, Size: {decoder.FrameSize}"); int n = 0; while (decoder.TryDecodeNextFrame(out var frame)) Console.WriteLine($"Decoded frame #{n++}, pts={frame.pts}"); ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.