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

news2024/10/5 16:22:30

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);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/861069.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

云原生是什么

目录 1. 云原生是什么1.1. 微服务1.2. DevOps1.3. 持续交付1.4. 容器化 2. 什么是云原生2.1. 云原生的诞生2.2. 云原生基金会2.3. 主要区别: 云原生与传统企业应用 1. 云原生是什么 云原生是面向"云"而设计的应用, 因此技术部分依赖于传统云计算的 3 层概念, 基础设…

二叉树题目:根据二叉树创建字符串

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;根据二叉树创建字符串 出处&#xff1a;606. 根据二叉树创建字符串 难度 3 级 题目描述 要求 给你二叉树的根结…

Git 代码分支规范

目的 俗话说&#xff1a;没有规矩&#xff0c;不成方圆。遵循一个好的规章制度能让你的工作事半功倍。同时也可以展现出你做事的认真的态度以及你的专业性&#xff0c;不会显得杂乱无章&#xff0c;管理困难。Git分支规范也是一样。当遵循了某种约定的Git分支&#xff0c;在代…

欧拉公式的证明-泰勒展开法

欧拉公式 欧拉公式在理工科有着广泛的应用和影响。 特别地&#xff0c;当时&#xff0c;&#xff0c;巧妙地将自然对数底数 &#xff0c;圆周率 &#xff0c;虚数单位 &#xff0c; 写进一个公式。 证明 由泰勒公式&#xff1a; 即 提取奇偶次项&#xff1a; 即 补充&#xf…

Michael.W基于Foundry精读Openzeppelin第23期——ERC165Checker.sol

Michael.W基于Foundry精读Openzeppelin第23期——ERC165Checker.sol 0. 版本0.1 ERC165Checker.sol 1. 目标合约2. 代码精读2.1 supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId)2.2 supportsERC165(address account)2.3 supportsInterface(address acc…

Docker一键部署项目,无需登录XShell

文章目录 一键部署项目Docker手动部署SpringBoot项目编写docker部署的脚本文件script.sh 脚本内容 特别注意&#xff01;编写dockerfiledockerfile 文件内容 上传后端服务的jar包到服务器中执行 script 脚本部署后端服务 自动部署SpringBoot项目引入jsch依赖编写jsch工具类执行…

C# byte[]与Bitmap互转

首先先观察一下本地bmp图像结构(参考&#xff1a; https://blog.csdn.net/qq_37872392/article/details/124710600)&#xff1a; 可以看到bmp图像结构除了纯图像像素点位信息&#xff0c;还有一块未用空间(OffSet)。 所以如果需要得到图像所有数据进行转换&#xff0c;则可以使…

MySQL_SQL性能分析

SQL执行频次 语法&#xff1a; SHOW GLOBAL STATUS LIKE COM_类型; COM_insert; 插入次数 com_delete; 删除次数 com_update; 更新次数 com_select; 查询次数 com_______; 注意&#xff1a;通过语法&#xff0c;可以查询到数据库的实际状态&#xff0c;就可以知道数据库是以增删…

macos搭建appium-iOS自动化测试环境

目录 准备工作 安装必需的软件 安装appium 安装XCode 下载WDA工程 配置WDA工程 搭建appiumwda自动化环境 第一步&#xff1a;启动通过xcodebuild命令启动wda服务 分享一下如何在mac电脑上搭建一个完整的appium自动化测试环境 准备工作 前期需要准备的设备和账号&…

【独立版】新零售社区团购电商系统生鲜水果商城兴盛优选十荟团源码

【独立版】新零售社区团购电商系统生鲜水果商城兴盛优选十荟团源码

DNSlog注入(利用DNSlog平台将SQL盲注变成回显注入)

前言什么是UNC什么是DNSlog注入DNSlog注入的条件防止DNSlog注入的几个措施 sqli-labs试验 前言 前几天面试的时候&#xff0c;面试官问我知不知道OOB&#xff08;带外数据&#xff09;。 当时我蒙了&#xff0c;确实没听说过这个东西&#xff0c;然后面试官告诉我原来dnslog注入…

机器人CPP编程基础-04输入Input

机器人CPP编程基础-03变量类型Variables Types ……AI…… C #include<iostream> // 引入iostream库&#xff0c;这个库包含了对输入/输出进行操作所需的函数和对象 using namespace std; // 使用命名空间std&#xff0c;这样我们就可以直接使用std中的名字&#xff0c…

设定嵌入式linux系统的用户名和密码

遇到一个问题&#xff0c;板子上电后&#xff0c;串口可以正常输入命令行&#xff0c;而且不需要密码&#xff0c;用户名就是root &#xff0c;因为没有设置密码&#xff0c;但是SSH登录时用户名输入root&#xff0c;密码直接敲回车键也就是不输入密码竟然是错误的&#xff0c;…

一文带你入门Nacos:从安装到实例分析

目录 一、安装和配置 1.1 下载安装包 1.2 解压 1.3 端口配置 1.4 启动 1.5 访问 二、服务注册到nacos 2.1 引入依赖 2.2 配置nacos地址 2.3 重启 三、服务分级存储模型 3.1 给user-service配置集群 3.2 同集群优先的负载均衡 1&#xff09;给order-service配…

勘探开发人工智能技术:机器学习(3)

0 提纲 4.1 logistic回归 4.2 支持向量机(SVM) 4.3 PCA 1 logistic回归 用超平面分割正负样本, 考虑所有样本导致的损失. 1.1 线性分类器 logistic 回归是使用超平面将空间分开, 一边是正样本, 另一边是负样本. 因此, 它是一个线性分类器. 如图所示, 若干样本由两个特征描…

竞赛项目 深度学习疲劳驾驶检测 opencv python

文章目录 0 前言1 课题背景2 实现目标3 当前市面上疲劳驾驶检测的方法4 相关数据集5 基于头部姿态的驾驶疲劳检测5.1 如何确定疲劳状态5.2 算法步骤5.3 打瞌睡判断 6 基于CNN与SVM的疲劳检测方法6.1 网络结构6.2 疲劳图像分类训练6.3 训练结果 7 最后 0 前言 &#x1f525; 优…

Python爬虫 爬取图片

在我们日常上网浏览网页的时候&#xff0c;经常会看到一些好看的图片&#xff0c;我们就希望把这些图片保存下载&#xff0c;或者用户用来做桌面壁纸&#xff0c;或者用来做设计的素材。 我们最常规的做法就是通过鼠标右键&#xff0c;选择另存为。但有些图片鼠标右键的时候并没…

竞赛项目 深度学习的视频多目标跟踪实现

文章目录 1 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的视频多目标跟踪实现 …

LabVIEW对并行机器人结构进行建模仿真

LabVIEW对并行机器人结构进行建模仿真 为了对复杂机器人结构的数学模型进行建模、搜索、动画和验证&#xff0c;在工业机器人动态行为实验室中&#xff0c;设计并实现了具有五个自由度的单臂型机器人。在研究台上可以区分以下元素&#xff1a;带有直流电机和编码器的机器人;稳…

竞赛项目 深度学习花卉识别 - python 机器视觉 opencv

文章目录 0 前言1 项目背景2 花卉识别的基本原理3 算法实现3.1 预处理3.2 特征提取和选择3.3 分类器设计和决策3.4 卷积神经网络基本原理 4 算法实现4.1 花卉图像数据4.2 模块组成 5 项目执行结果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &a…