首页 > 编程语言 >C# 使用FFmpeg.Autogen对byte[]进行编解码

C# 使用FFmpeg.Autogen对byte[]进行编解码

时间:2023-08-09 11:11:24浏览次数:89  
标签:编解码 FFmpeg Autogen private pCodecContext av var new ffmpeg

C# 使用FFmpeg.Autogen对byte[]进行编解码,参考:https://github.com/vanjoge/CSharpVideoDemo

入口调用类:

using System;
using System.IO;
using System.Drawing;
using System.Runtime.InteropServices;
using FFmpeg.AutoGen;

namespace FFmpegAnalyzer
{
    public class FFmpegWrapper
    {
        /// <summary>
        /// 默认的编码格式
        /// </summary>
        public AVCodecID DefaultCodecFormat { get; set; } = AVCodecID.AV_CODEC_ID_H264;

        /// <summary>
        /// 注册FFmpeg
        /// </summary>
        public static void RegisterFFmpeg()
        {
            FFmpegBinariesHelper.RegisterFFmpegBinaries();

            // 初始化注册ffmpeg相关的编码器
            ffmpeg.av_register_all();
            ffmpeg.avcodec_register_all();
            ffmpeg.avformat_network_init();
        }

        /// <summary>
        /// 注册日志
        /// <exception cref="NotSupportedException">.NET Framework 不支持日志注册</exception>
        /// </summary>
        private unsafe void RegisterFFmpegLogger()
        {
            // 设置记录ffmpeg日志级别
            ffmpeg.av_log_set_level(ffmpeg.AV_LOG_VERBOSE);
            av_log_set_callback_callback logCallback = (p0, level, format, vl) =>
            {
                if (level > ffmpeg.av_log_get_level()) return;

                var lineSize = 1024;
                var lineBuffer = stackalloc byte[lineSize];
                var printPrefix = 1;
                ffmpeg.av_log_format_line(p0, level, format, vl, lineBuffer, lineSize, &printPrefix);
                var line = Marshal.PtrToStringAnsi((IntPtr)lineBuffer);
                Console.Write(line);
            };
            ffmpeg.av_log_set_callback(logCallback);
        }

        #region 编码器

        /// <summary>
        /// 创建编码器
        /// </summary>
        /// <param name="frameSize">编码前一帧原始数据的大小</param>
        /// <param name="isRgb">rgb数据</param>
        public void CreateEncoder(Size frameSize, bool isRgb = true)
        {
            _fFmpegEncoder = new FFmpegEncoder(frameSize, isRgb);
            _fFmpegEncoder.CreateEncoder(DefaultCodecFormat);
        }

        /// <summary>
        /// 编码
        /// </summary>
        /// <param name="frameBytes">编码帧数据</param>
        /// <returns></returns>
        public byte[] EncodeFrames(byte[] frameBytes)
        {
            return _fFmpegEncoder.EncodeFrames(frameBytes);
        }

        /// <summary>
        /// 释放编码器
        /// </summary>
        public void DisposeEncoder()
        {
            _fFmpegEncoder.Dispose();
        }
        #endregion

        #region 解码器

        /// <summary>
        /// 创建解码器
        /// </summary>
        /// <param name="decodedFrameSize">解码后数据的大小</param>
        /// <param name="isRgb">Rgb数据</param>
        public void CreateDecoder(Size decodedFrameSize, bool isRgb = true)
        {
            _fFmpegDecoder = new FFmpegDecoder(decodedFrameSize, isRgb);
            _fFmpegDecoder.CreateDecoder(DefaultCodecFormat);
        }

        /// <summary>
        /// 解码
        /// </summary>
        /// <param name="frameBytes">解码帧数据</param>
        /// <returns></returns>
        public byte[] DecodeFrames(byte[] frameBytes)
        {
            return _fFmpegDecoder.DecodeFrames(frameBytes);
        }

        /// <summary>
        /// 释放解码器
        /// </summary>
        public void DisposeDecoder()
        {
            _fFmpegDecoder.Dispose();
        }
        #endregion

        /// <summary>编码器</summary>
        private FFmpegEncoder _fFmpegEncoder;

        /// <summary>解码器</summary>
        private FFmpegDecoder _fFmpegDecoder;
    }
}

其它业务类:

using System;
using System.IO;
using System.Runtime.InteropServices;

namespace FFmpegAnalyzer
{
    internal class FFmpegBinariesHelper
    {
        private const string LD_LIBRARY_PATH = "LD_LIBRARY_PATH";

        internal static void RegisterFFmpegBinaries()
        {
            switch (Environment.OSVersion.Platform)
            {
                case PlatformID.Win32NT:
                case PlatformID.Win32S:
                case PlatformID.Win32Windows:
                    var current = AppDomain.CurrentDomain.BaseDirectory;
                    var probe = $"FFmpeg/bin/{(Environment.Is64BitProcess ? @"x64" : @"x86")}";
                    while (current != null)
                    {
                        var ffmpegDirectory = Path.Combine(current, probe);
                        if (Directory.Exists(ffmpegDirectory))
                        {
                            Console.WriteLine($"FFmpeg binaries found in: {ffmpegDirectory}");
                            RegisterLibrariesSearchPath(ffmpegDirectory);
                            return;
                        }
                        current = Directory.GetParent(current)?.FullName;
                    }
                    break;
                case PlatformID.Unix:
                case PlatformID.MacOSX:
                    var libraryPath = Environment.GetEnvironmentVariable(LD_LIBRARY_PATH);
                    RegisterLibrariesSearchPath(libraryPath);
                    break;
            }
        }
        private static void RegisterLibrariesSearchPath(string path)
        {
            switch (Environment.OSVersion.Platform)
            {
                case PlatformID.Win32NT:
                case PlatformID.Win32S:
                case PlatformID.Win32Windows:
                    SetDllDirectory(path);
                    break;
                case PlatformID.Unix:
                case PlatformID.MacOSX:
                    string currentValue = Environment.GetEnvironmentVariable(LD_LIBRARY_PATH);
                    if (string.IsNullOrWhiteSpace(currentValue) == false && currentValue.Contains(path) == false)
                    {
                        string newValue = currentValue + Path.PathSeparator + path;
                        Environment.SetEnvironmentVariable(LD_LIBRARY_PATH, newValue);
                    }
                    break;
            }
        }
        
        [DllImport("kernel32", SetLastError = true)]
        private static extern bool SetDllDirectory(string lpPathName);
    }
}
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using FFmpeg.AutoGen;

namespace FFmpegAnalyzer
{
    /// <summary>
    /// 解码器
    /// </summary>
    internal unsafe class FFmpegDecoder
    {
        /// <param name="decodedFrameSize">解码后数据的大小</param>
        /// <param name="isRgb">Rgb数据</param>
        public FFmpegDecoder(Size decodedFrameSize, bool isRgb = true)
        {
            _decodedFrameSize = decodedFrameSize;
            _isRgb = isRgb;
        }

        /// <summary>
        /// 创建解码器
        /// </summary>
        /// <param name="codecFormat">解码格式</param>
        public void CreateDecoder(AVCodecID codecFormat)
        {
            var originPixelFormat = AVPixelFormat.AV_PIX_FMT_YUV420P;
            var destinationPixelFormat = _isRgb ? AVPixelFormat.AV_PIX_FMT_RGB24 : AVPixelFormat.AV_PIX_FMT_BGRA; 

            //获取解码器
            _pDecodec = ffmpeg.avcodec_find_decoder(codecFormat);
            if (_pDecodec == null) throw new InvalidOperationException("Codec not found.");

            _pDecodecContext = ffmpeg.avcodec_alloc_context3(_pDecodec);
            _pDecodecContext->width = _decodedFrameSize.Width;
            _pDecodecContext->height = _decodedFrameSize.Height;
            _pDecodecContext->time_base = new AVRational { num = 1, den = 30 };
            _pDecodecContext->pix_fmt = AVPixelFormat.AV_PIX_FMT_YUV420P;
            _pDecodecContext->framerate = new AVRational { num = 30, den = 1 };                        
            _pDecodecContext->gop_size = 30;
            // 设置预测算法
            _pDecodecContext->flags |= ffmpeg.AV_CODEC_FLAG_PSNR;
            _pDecodecContext->flags2 |= ffmpeg.AV_CODEC_FLAG2_FAST;
            _pDecodecContext->max_b_frames = 0;
            ffmpeg.av_opt_set(_pDecodecContext->priv_data, "preset", "veryfast", 0);
            ffmpeg.av_opt_set(_pDecodecContext->priv_data, "tune", "zerolatency", 0);
            //打开解码器
            ffmpeg.avcodec_open2(_pDecodecContext, _pDecodec, null);
            _pConvertContext = ffmpeg.sws_getContext(
                _decodedFrameSize.Width,
                _decodedFrameSize.Height,
                originPixelFormat,
                _decodedFrameSize.Width,
                _decodedFrameSize.Height,
                destinationPixelFormat,
               ffmpeg.SWS_FAST_BILINEAR,
                null, null, null);
            if (_pConvertContext == null)
                throw new ApplicationException("Could not initialize the conversion context.");

            var convertedFrameBufferSize = ffmpeg.av_image_get_buffer_size(destinationPixelFormat, _decodedFrameSize.Width, _decodedFrameSize.Height, 1);
            _convertedFrameBufferPtr = Marshal.AllocHGlobal(convertedFrameBufferSize);
            _dstData = new byte_ptrArray4();
            _dstLineSize = new int_array4();

            ffmpeg.av_image_fill_arrays(ref _dstData, ref _dstLineSize, (byte*)_convertedFrameBufferPtr, destinationPixelFormat,
                _decodedFrameSize.Width, _decodedFrameSize.Height, 1);
            _isCodecRunning = true;
        }

        /// <summary>
        /// 解码
        /// </summary>
        /// <param name="frameBytes"></param>
        /// <returns></returns>
        public  byte[] DecodeFrames(byte[] frameBytes)
        {
            if (!_isCodecRunning)
            {
                throw new InvalidOperationException("解码器未运行!");
            }
            var waitDecodePacket = ffmpeg.av_packet_alloc();
            var waitDecoderFrame = ffmpeg.av_frame_alloc();
            ffmpeg.av_frame_unref(waitDecoderFrame);
            fixed (byte* waitDecodeData = frameBytes)
            {
                waitDecodePacket->data = waitDecodeData;
                waitDecodePacket->size = frameBytes.Length;
                ffmpeg.av_frame_unref(waitDecoderFrame);
                try
                {
                    int error;
                    do
                    {
                        ffmpeg.avcodec_send_packet(_pDecodecContext, waitDecodePacket);
                        error = ffmpeg.avcodec_receive_frame(_pDecodecContext, waitDecoderFrame);
                    } while (error == ffmpeg.AVERROR(ffmpeg.EAGAIN));
                }
                finally
                {
                    ffmpeg.av_packet_unref(waitDecodePacket);
                }

                var decodeAfterFrame = ConvertToRgb(waitDecoderFrame);

                var length = _isRgb
                    ? decodeAfterFrame.height * decodeAfterFrame.width * 3
                    : decodeAfterFrame.height * decodeAfterFrame.width * 4;

                byte[] buffer = new byte[length];
                Marshal.Copy((IntPtr)decodeAfterFrame.data[0], buffer, 0, buffer.Length);
                return buffer;
            }
        }

        /// <summary>
        /// 释放
        /// </summary>
        public  void Dispose()
        {
            _isCodecRunning = false;
            //释放解码器
            ffmpeg.avcodec_close(_pDecodecContext);
            ffmpeg.av_free(_pDecodecContext);
            //释放转换器
            Marshal.FreeHGlobal(_convertedFrameBufferPtr);
            ffmpeg.sws_freeContext(_pConvertContext);
        }

        /// <summary>
        /// 转换成Rgb
        /// </summary>
        /// <param name="waitDecoderFrame"></param>
        /// <returns></returns>
        private  AVFrame ConvertToRgb(AVFrame* waitDecoderFrame)
        {
            ffmpeg.sws_scale(_pConvertContext, waitDecoderFrame->data, waitDecoderFrame->linesize, 0, waitDecoderFrame->height, _dstData, _dstLineSize);
            var decodeAfterData = new byte_ptrArray8();
            decodeAfterData.UpdateFrom(_dstData);
            var lineSize = new int_array8();
            lineSize.UpdateFrom(_dstLineSize);

            ffmpeg.av_frame_unref(waitDecoderFrame);
            return new AVFrame
            {
                data = decodeAfterData,
                linesize = lineSize,
                width = _decodedFrameSize.Width,
                height = _decodedFrameSize.Height
            };
        }

        //解码器
        private AVCodec* _pDecodec;
        private AVCodecContext* _pDecodecContext;
        //转换缓存区
        private IntPtr _convertedFrameBufferPtr;
        private byte_ptrArray4 _dstData;
        private int_array4 _dstLineSize;
        //格式转换
        private SwsContext* _pConvertContext;
        private Size _decodedFrameSize;
        private readonly bool _isRgb;
        //解码器正在运行
        private bool _isCodecRunning;
    }
}
using System;
using System.Runtime.InteropServices;
using System.Drawing;
using FFmpeg.AutoGen;

namespace FFmpegAnalyzer
{
    /// <summary>
    /// 编码器
    /// </summary>
    internal unsafe class FFmpegEncoder
    {
        /// <param name="frameSize">编码前一帧原始数据的大小</param>
        /// <param name="isRgb">rgb数据</param>
        public FFmpegEncoder(Size frameSize, bool isRgb = true)
        {
            _frameSize = frameSize;
            _isRgb = isRgb;
            _rowPitch = isRgb ? _frameSize.Width * 3 : _frameSize.Width * 4;
        }

        /// <summary>
        /// 创建编码器
        /// </summary>
        public  void CreateEncoder(AVCodecID codecFormat)
        {
            var originPixelFormat = _isRgb ? AVPixelFormat.AV_PIX_FMT_RGB24 : AVPixelFormat.AV_PIX_FMT_BGRA;
            var destinationPixelFormat = AVPixelFormat.AV_PIX_FMT_YUV420P;
            _pCodec = ffmpeg.avcodec_find_encoder(codecFormat);
             
            if (_pCodec == null)
                throw new InvalidOperationException("Codec not found.");
            _pCodecContext = ffmpeg.avcodec_alloc_context3(_pCodec);
            _pCodecContext->width = _frameSize.Width;
            _pCodecContext->height = _frameSize.Height;

            _pCodecContext->framerate = new AVRational { num = 30, den = 1 };
            _pCodecContext->time_base = new AVRational {num = 1, den = 30};
            _pCodecContext->gop_size = 30;
            _pCodecContext->pix_fmt = destinationPixelFormat;
            // 设置预测算法
            _pCodecContext->flags |= ffmpeg.AV_CODEC_FLAG_PSNR;
            _pCodecContext->flags2 |= ffmpeg.AV_CODEC_FLAG2_FAST;
            _pCodecContext->max_b_frames = 0;

            ffmpeg.av_opt_set(_pCodecContext->priv_data, "preset", "veryfast", 0);
            ffmpeg.av_opt_set(_pCodecContext->priv_data, "tune", "zerolatency", 0);

            //打开编码器
            ffmpeg.avcodec_open2(_pCodecContext, _pCodec, null);
            _pConvertContext = ffmpeg.sws_getContext(_frameSize.Width, _frameSize.Height, originPixelFormat, _frameSize.Width, _frameSize.Height, destinationPixelFormat,
            ffmpeg.SWS_FAST_BILINEAR, null, null, null);
            if (_pConvertContext == null)
                throw new ApplicationException("Could not initialize the conversion context.");

            var convertedFrameBufferSize = ffmpeg.av_image_get_buffer_size(destinationPixelFormat, _frameSize.Width, _frameSize.Height, 1);
            _convertedFrameBufferPtr = Marshal.AllocHGlobal(convertedFrameBufferSize);
            _dstData = new byte_ptrArray4();
            _dstLineSize = new int_array4();

            ffmpeg.av_image_fill_arrays(ref _dstData, ref _dstLineSize, (byte*)_convertedFrameBufferPtr, destinationPixelFormat, _frameSize.Width, _frameSize.Height, 1);
            _isCodecRunning = true;
        }

        /// <summary>
        /// 释放
        /// </summary>
        public  void Dispose()
        {
            if (!_isCodecRunning) return;
            _isCodecRunning = false;
            //释放编码器
            ffmpeg.avcodec_close(_pCodecContext);
            ffmpeg.av_free(_pCodecContext);
            //释放转换器
            Marshal.FreeHGlobal(_convertedFrameBufferPtr);
            ffmpeg.sws_freeContext(_pConvertContext);
        }

        /// <summary>
        /// 编码
        /// </summary>
        /// <param name="frameBytes"></param>
        /// <returns></returns>
        public  byte[] EncodeFrames(byte[] frameBytes)
        {
            if (!_isCodecRunning)
            {
                 throw new InvalidOperationException("编码器未运行!");
            }
            fixed (byte* pBitmapData = frameBytes)
            {
                var waitToYuvFrame = new AVFrame
                {
                    data = new byte_ptrArray8 { [0] = pBitmapData },
                    linesize = new int_array8 { [0] = _rowPitch },
                    height = _frameSize.Height
                };

                var rgbToYuv = ConvertToYuv(waitToYuvFrame, _frameSize.Width, _frameSize.Height);

                byte[] buffer;
                var pPacket = ffmpeg.av_packet_alloc();
                try
                {
                    int error;
                    do
                    {
                        ffmpeg.avcodec_send_frame(_pCodecContext, &rgbToYuv);
                        error = ffmpeg.avcodec_receive_packet(_pCodecContext, pPacket);
                    } while (error == ffmpeg.AVERROR(ffmpeg.EAGAIN));
                    buffer = new byte[pPacket->size];
                    Marshal.Copy(new IntPtr(pPacket->data), buffer, 0, pPacket->size);
                }
                finally
                {
                    ffmpeg.av_frame_unref(&rgbToYuv);
                    ffmpeg.av_packet_unref(pPacket);
                }

                return buffer;
            }
        }

        /// <summary>
        /// 转换成Yuv格式
        /// </summary>
        /// <param name="waitConvertYuvFrame"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        private  AVFrame ConvertToYuv(AVFrame waitConvertYuvFrame, int width, int height)
        {
            ffmpeg.sws_scale(_pConvertContext, waitConvertYuvFrame.data, waitConvertYuvFrame.linesize, 0, waitConvertYuvFrame.height, _dstData, _dstLineSize);

            var data = new byte_ptrArray8();
            data.UpdateFrom(_dstData);
            var lineSize = new int_array8();
            lineSize.UpdateFrom(_dstLineSize);
            ffmpeg.av_frame_unref(&waitConvertYuvFrame);
            return new AVFrame
            {
                data = data,
                linesize = lineSize,
                width = width,
                height = height
            };
        }

        //编码器
        private AVCodec* _pCodec;
        private AVCodecContext* _pCodecContext;
        //转换缓存区
        private IntPtr _convertedFrameBufferPtr;
        private byte_ptrArray4 _dstData;
        private int_array4 _dstLineSize;
        //格式转换
        private SwsContext* _pConvertContext;
        private Size _frameSize;
        private readonly int _rowPitch;
        private readonly bool _isRgb;

        //编码器正在运行
        private bool _isCodecRunning;
    }
}
using FFmpeg.AutoGen;
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows;

namespace FFmpegAnalyzer
{
    public sealed unsafe class VideoFrameConverter : IDisposable
    {
        private readonly IntPtr _convertedFrameBufferPtr;
        private readonly System.Drawing.Size _destinationSize;
        private readonly byte_ptrArray4 _dstData;
        private readonly int_array4 _dstLinesize;
        private readonly SwsContext* _pConvertContext;
        /// <summary>
        /// 帧格式转换
        /// </summary>
        /// <param name="sourceSize"></param>
        /// <param name="sourcePixelFormat"></param>
        /// <param name="destinationSize"></param>
        /// <param name="destinationPixelFormat"></param>
        public VideoFrameConverter(System.Drawing.Size sourceSize, AVPixelFormat sourcePixelFormat,
            System.Drawing.Size destinationSize, AVPixelFormat destinationPixelFormat)
        {
            _destinationSize = destinationSize;
            //分配并返回一个SwsContext。您需要它使用sws_scale()执行伸缩/转换操作
            //主要就是使用SwsContext进行转换!!!
            _pConvertContext = ffmpeg.sws_getContext((int)sourceSize.Width, (int)sourceSize.Height, sourcePixelFormat,
                (int)destinationSize.Width,
                (int)destinationSize.Height
                , destinationPixelFormat,
                ffmpeg.SWS_FAST_BILINEAR //默认算法 还有其他算法
                , null
                , null
                , null //额外参数 在flasgs指定的算法,而使用的参数。如果  SWS_BICUBIC  SWS_GAUSS  SWS_LANCZOS这些算法。  这里没有使用
                );
            if (_pConvertContext == null) throw new ApplicationException("Could not initialize the conversion context.");
            //获取媒体帧所需要的大小
            var convertedFrameBufferSize = ffmpeg.av_image_get_buffer_size(destinationPixelFormat
                , (int)destinationSize.Width, (int)destinationSize.Height
                , 1);
            //申请非托管内存,unsafe代码
            _convertedFrameBufferPtr = Marshal.AllocHGlobal(convertedFrameBufferSize);

            //转换帧的内存指针
            _dstData = new byte_ptrArray4();
            _dstLinesize = new int_array4();

            //挂在帧数据的内存区把_dstData里存的的指针指向_convertedFrameBufferPtr
            ffmpeg.av_image_fill_arrays(ref _dstData, ref _dstLinesize
                , (byte*)_convertedFrameBufferPtr
                , destinationPixelFormat
                , (int)destinationSize.Width, (int)destinationSize.Height
                , 1);
        }

        public void Dispose()
        {
            Marshal.FreeHGlobal(_convertedFrameBufferPtr);
            ffmpeg.sws_freeContext(_pConvertContext);
        }

        public AVFrame Convert(AVFrame sourceFrame)
        {
            //转换格式
            ffmpeg.sws_scale(_pConvertContext
                , sourceFrame.data
                , sourceFrame.linesize
                , 0, sourceFrame.height
                , _dstData, _dstLinesize);

            var data = new byte_ptrArray8();
            data.UpdateFrom(_dstData);
            var linesize = new int_array8();
            linesize.UpdateFrom(_dstLinesize);

            return new AVFrame
            {
                data = data,
                linesize = linesize,
                width = (int)_destinationSize.Width,
                height = (int)_destinationSize.Height
            };
        }
    }
}
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using FFmpeg.AutoGen;

namespace FFmpegAnalyzer
{
    public sealed unsafe class VideoStreamDecoder : IDisposable
    {
        private readonly AVCodecContext* _pCodecContext;
        private readonly AVFormatContext* _pFormatContext;
        private readonly int _streamIndex;
        //
        private readonly AVFrame* _pFrame;
        //
        private readonly AVFrame* _receivedFrame;
        private readonly AVPacket* _pPacket;
        /// <summary>
        /// 视频解码器
        /// </summary>
        /// <param name="url">视频流URL</param>
        /// <param name="HWDeviceType">硬件解码器类型(默认AVHWDeviceType.AV_HWDEVICE_TYPE_NONE)</param>
        public VideoStreamDecoder(string url, AVHWDeviceType HWDeviceType = AVHWDeviceType.AV_HWDEVICE_TYPE_NONE)
        {
            //分配一个AVFormatContext
            _pFormatContext = ffmpeg.avformat_alloc_context();
            //分配一个AVFrame
            _receivedFrame = ffmpeg.av_frame_alloc();

            var pFormatContext = _pFormatContext;
            //将源音视频流传递给ffmpeg即ffmpeg打开源视频流
            ffmpeg.avformat_open_input(&pFormatContext, url, null, null);
            //获取音视频流信息
            ffmpeg.avformat_find_stream_info(_pFormatContext, null);
            AVCodec* codec = null;
            //在源里找到最佳的流,如果指定了解码器,则根据解码器寻找流,将解码器传递给codec
            _streamIndex = ffmpeg.av_find_best_stream(_pFormatContext, AVMediaType.AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
            //根据解码器分配一个AVCodecContext ,仅仅分配工具,还没有初始化。
            _pCodecContext = ffmpeg.avcodec_alloc_context3(codec);
            //如果硬解码
            if (HWDeviceType != AVHWDeviceType.AV_HWDEVICE_TYPE_NONE)
            {
                //根据硬件编码类型创建AVHWDeviceContext,存在AVFormatContext.hw_device_ctx (_pCodecContext->hw_device_ctx)
                ffmpeg.av_hwdevice_ctx_create(&_pCodecContext->hw_device_ctx, HWDeviceType, null, null, 0);
            }
            //将最佳流的格式参数传递给codecContext
            ffmpeg.avcodec_parameters_to_context(_pCodecContext, _pFormatContext->streams[_streamIndex]->codecpar);
            //根据codec初始化pCodecContext 。与_pCodecContext = ffmpeg.avcodec_alloc_context3(codec);对应
            ffmpeg.avcodec_open2(_pCodecContext, codec, null);

            CodecName = ffmpeg.avcodec_get_name(codec->id);
            FrameSize = new System.Drawing.Size(_pCodecContext->width, _pCodecContext->height);
            PixelFormat = _pCodecContext->pix_fmt;
            //分配AVPacket
            /* AVPacket用于存储压缩的数据,分别包括有音频压缩数据,视频压缩数据和字幕压缩数据。
                       它通常在解复用操作后存储压缩数据,然后作为输入传给解码器。或者由编码器输出然后传递给复用器。
                       对于视频压缩数据,一个AVPacket通常包括一个视频帧。对于音频压缩数据,可能包括几个压缩的音频帧。
             */
            _pPacket = ffmpeg.av_packet_alloc();

            //分配AVFrame
            /*AVFrame用于存储解码后的音频或者视频数据。
                    AVFrame必须通过av_frame_alloc进行分配,通过av_frame_free释放。
            */
            _pFrame = ffmpeg.av_frame_alloc();
        }

        public string CodecName { get; }
        public System.Drawing.Size FrameSize { get; }
        public AVPixelFormat PixelFormat { get; }

        public void Dispose()
        {
            ffmpeg.av_frame_unref(_pFrame);
            ffmpeg.av_free(_pFrame);

            ffmpeg.av_packet_unref(_pPacket);
            ffmpeg.av_free(_pPacket);

            ffmpeg.avcodec_close(_pCodecContext);
            var pFormatContext = _pFormatContext;
            ffmpeg.avformat_close_input(&pFormatContext);
        }

        /// <summary>
        /// 解码下一帧帧
        /// </summary>
        /// <param name="frame">参数返回解码后的帧</param>
        /// <returns></returns>
        public bool TryDecodeNextFrame(out AVFrame frame)
        {
            //取消帧的引用。帧将不会被任何资源引用
            ffmpeg.av_frame_unref(_pFrame);
            ffmpeg.av_frame_unref(_receivedFrame);
            int error;
            do
            {


                try
                {
                    #region 读取帧忽略无效帧
                    do
                    {

                        //读取无效帧
                        error = ffmpeg.av_read_frame(_pFormatContext, _pPacket);//根据pFormatContext读取帧,返回到Packet中
                        if (error == ffmpeg.AVERROR_EOF)//如果已经是影视片流末尾则返回
                        {
                            frame = *_pFrame;
                            return false;
                        }
                    } while (_pPacket->stream_index != _streamIndex); //忽略掉音视频流里面与有效流(初始化(构造函数)时标记的_streamIndex)不一致的流
                    #endregion

                    //将帧数据放入解码器
                    ffmpeg.avcodec_send_packet(_pCodecContext, _pPacket);  //将原始数据数据(_pPacket)作为输入提供给解码器(_pCodecContext)
                }
                finally
                {
                    //消除对_pPacket的引用
                    ffmpeg.av_packet_unref(_pPacket);
                }



                //读取解码器里解码(_pCodecContext)后的帧通过参数返回(_pFrame)
                error = ffmpeg.avcodec_receive_frame(_pCodecContext, _pFrame);

            } while (error == ffmpeg.AVERROR(ffmpeg.EAGAIN));//当返回值等于 EAGAIN(再试一次),
            if (_pCodecContext->hw_device_ctx != null)//如果配置了硬件解码则调用硬件解码器解码
            {
                //将_pFrame通过硬件解码后放入_receivedFrame
                ffmpeg.av_hwframe_transfer_data(_receivedFrame, _pFrame, 0);
                frame = *_receivedFrame;
            }
            else
            {
                frame = *_pFrame;
            }
            return true;
        }

        /// <summary>
        /// 获取媒体TAG信息
        /// </summary>
        /// <returns></returns>
        public IReadOnlyDictionary<string, string> GetContextInfo()
        {
            AVDictionaryEntry* tag = null;
            var result = new Dictionary<string, string>();
            while ((tag = ffmpeg.av_dict_get(_pFormatContext->metadata, "", tag, ffmpeg.AV_DICT_IGNORE_SUFFIX)) != null)
            {
                var key = Marshal.PtrToStringAnsi((IntPtr)tag->key);
                var value = Marshal.PtrToStringAnsi((IntPtr)tag->value);
                result.Add(key, value);
            }

            return result;
        }
    }
}

需要将ffmpeg的类库复制到生成目录上(对应FFmpegBinariesHelper.RegisterFFmpegBinaries()中的生成路径)

 使用代码:

FFmpegWrapper.RegisterFFmpeg();
_ffMpegWrapper = new FFmpegWrapper();
_ffMpegWrapper.CreateEncoder(new System.Drawing.Size(1920, 1080), true);

_ffMpegWrapper1 = new FFmpegWrapper();
_ffMpegWrapper1.CreateDecoder(new System.Drawing.Size(1920, 1080), true);
var encodeFrames = _ffMpegWrapper.EncodeFrames(Data);
var decodeFrames = _ffMpegWrapper1.DecodeFrames(encodeFrames);

 

标签:编解码,FFmpeg,Autogen,private,pCodecContext,av,var,new,ffmpeg
From: https://www.cnblogs.com/log9527blog/p/17603563.html

相关文章

  • rocky linux:安装ffmpeg(ffmpeg 5.1.3/rocky linux 9.2)
    一,ffmpeg官网:网址:http://ffmpeg.org/如图:说明:最新版本出到了6.0,我们从dnf通道安装,版本可能略低二,安装rpmfusion库[root@img~]#dnfinstall--nogpgcheckhttps://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-9.noarch.rpm[root@img~]#dnfinstall......
  • clang 静态编译 ffmpeg
    文档说明:只记录关键的地方;发文时间:2023-08-06意义:静态编译ffmpeg,可自由裁剪,使用libc构建;支持macos、linux构建;生成库依赖库图环境:alpine:3.17dockerclang备注:大部分软件源代码来源于github.com,下载过程断断续续的。请自备代理借助swoole-cli已经编写好......
  • FFmpeg方法用法大全【欢迎补充】
    1.定义FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开......
  • ffmpeg视频处理
    CMakeLists.txtcmake_minimum_required(VERSION3.25)project(test)set(CMAKE_CXX_STANDARD17)set(CMAKE_CXX_STANDARD14)set(FFMPEG_DIR/usr/local/ffmpeg)set(FFMPEG_INCLUDE_DIR${FFMPEG_DIR}/include)set(FFMPEG_LIBRARY_DIR${FFMPEG_DIR}/lib)include_......
  • clion搭建ffmpeg环境
    配置链接库路径sudovim/etc/ld.so.conf.d/ffmpeg.conf/usr/local/ffmpeg/lib/编写CMakeLists.txtcmake_minimum_required(VERSION3.25)project(test)set(CMAKE_CXX_STANDARD17)set(CMAKE_CXX_STANDARD14)set(FFMPEG_DIR/usr/local/ffmpeg)set(FFMPEG_INCL......
  • ffmpeg + SDL2播放音频示例
    在网上搜罗了各种各样的样例代码,都存在各种各样的问题,调了好长时间终于能无杂音播放了由于个人场景需要本样例加了选择扬声器的功能不过有的可能还会有问题,目前ogg的文件都能播,mp3有的不行写一下网上的其他代码可能存在的问题和我的修改注:代码是C++17,如果编不过需要小改一下......
  • ffmpeg合并音频和视频
    ffmpeg合并音频和视频命令行ffmpeg-ivideo.m4s-iaudio.m4s-acodeccopy-vcodeccopyout.mp4使用ffmpeg的apiextern"C"{#include"libavformat/avformat.h"#include"libavutil/dict.h"#include"libavutil/opt.h"#include&quo......
  • ffmpeg 编译安装android和linux
    ffmpeg编译安装android和linux下载:https://github.com/FFmpeg/FFmpeghttps://www.ffmpeg.org/download.htmlenvirenmentndk:https://github.com/android/ndk/wiki/Unsupported-Downloadssudoapt-getinstallbuild-essentialpkg-configsudoapt-getintalllibx264-dev......
  • 基于opencv和ffmpeg的视频处理
    importcv2importsubprocessinput_video_path="/home/navy/Desktop/1.mp4"opencv_video_path="/home/navy/Desktop/2.mp4"new_video_path="/home/navy/Desktop/3.mp4"#打开视频文件video=cv2.VideoCapture(input_video_path)#获取视......
  • ffmpeg安装,备用,防止丢失
    #可道云提供的方式CentOS下安装yuminstallImageMagickdcrawghostscriptffmpeglibjpeglibjpeg-devellibpnglibpng-devellibtifflibtiff-devellibungiflibungif-develfreetypezlib#安装ffmpeg(视频缩略图,视频转码)curlhttp://doc.kodcloud.com/tools/psd/ins......