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

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里存的的指针指向_convertedFrameBufferPtrffmpeg.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);}//将最佳流的格式参数传递给codecContextffmpeg.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通过硬件解码后放入_receivedFrameffmpeg.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.mzph.cn/news/36099.shtml

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

相关文章

时序预测 | MATLAB实现EEMD-LSTM、LSTM集合经验模态分解结合长短期记忆神经网络时间序列预测对比

时序预测 | MATLAB实现EEMD-LSTM、LSTM集合经验模态分解结合长短期记忆神经网络时间序列预测对比 目录 时序预测 | MATLAB实现EEMD-LSTM、LSTM集合经验模态分解结合长短期记忆神经网络时间序列预测对比效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 时序预测 | …

小龟带你妙写排序之快速排序

快速排序 一. 快速排序原理二. 题目三. 快速排序的思路分析&#xff08;图文结合&#xff09;四.代码 一. 快速排序原理 先从数据序列中选一个元素&#xff0c;并将序列中所有比该元素小的元素都放到它的右边或左边&#xff0c;再对左右两边分别用同样的方法处之直到每一个待处…

runtime error: member access within misaligned address(力扣最常见错误之一)

runtime error: member access within misaligned address&#xff08;力扣最常见错误之一&#xff09; 前言原因和解决办法总结 前言 最近博主在刷力扣时&#xff0c;明明代码逻辑都没问题&#xff0c;但总是报下面这个错误&#xff1a; runtime error: member access within…

django实现登录和登录的鉴权

1、创建数据库的管理员表 在models.py 中定义admin表&#xff0c;为了简单&#xff0c;表里只有用户名和密码还有默认加的id 三个字段 from django.db import models# Create your models here.class Admin(models.Model):username models.CharField(verbose_name"用户…

Kafka:安装和配置

producer&#xff1a;发布消息的对象&#xff0c;称为消息产生者 &#xff08;Kafka topic producer&#xff09; topic&#xff1a;Kafka将消息分门别类&#xff0c;每一个消息称为一个主题&#xff08;topic&#xff09; consumer&#xff1a;订阅消息并处理发布消息的对象…

Java课题笔记~ ServletContext

单个Servlet的配置对象 web.xml <servlet><servlet-name>FirstServlet</servlet-name><servlet-class>com.ambow.test.FirstServlet</servlet-class><init-param><param-name>charset</param-name><param-value>utf-8&…

centos自动同步北京时间

1、安装ntpdate服务 yum -y install ntpdate 2、加入自动任务计划 查找ntpdate的路径&#xff1a; which ntpdate 复制这个路径。 编辑自动任务计划并加入ntpdate&#xff1a; crontab -e # 每小时第30分钟同步AD域控时间 30 * * * * /usr/sbin/ntpdate -u 192.168.2.8 > …

用户端Web自动化测试-L1

目录&#xff1a; Web自动化测试价值与体系环境安装与使用自动化用例录制自动化测试用例结构分析web浏览器控制常见控件定位方法强制等待与隐式等待常见控件交互方法自动化测试定位策略搜索功能自动化测试用户端Web自动化测试 1.Web自动化测试价值与体系 功能测试场景: UI 自…

AUTOSAR规范与ECU软件开发(基础篇)2.5 AUTOSAR方法论

前言 AUTOSAR方法论(AUTOSAR Methodology) 中车用控制器软件的开发涉及系统级、 ECU级和软件组件级。 系统级主要考虑系统功能需求、 硬件资源、 系统约束, 然后建立系统架构; ECU级根据抽象后的信息对ECU进行配置; 系统级和ECU级设计的同时, 伴随着软件组件级的开发。 上…

Sql server还原失败(数据库正在使用,无法获得对数据库的独占访问权)

一.Sql server还原失败(数据库正在使用,无法获得对数据库的独占访问权) 本次测试使用数据库实例SqlServer2008r2版 错误详细&#xff1a; 标题: Microsoft SQL Server Management Studio ------------------------------ 还原数据库“Mvc_HNHZ”时失败。 (Microsoft.SqlServer.…

《甲午》观后感——GPT-3.5所写

《甲午》是一部令人深思的纪录片&#xff0c;通过生动的画面和真实的故事&#xff0c;向观众展示了中国历史上的一段重要时期。观看这部纪录片&#xff0c;我深受触动&#xff0c;对历史的认识也得到了深化。 首先&#xff0c;这部纪录片通过精心搜集的历史资料和珍贵的影像资料…

低成本搭建NAS,利用HFS进行内网穿透,实现公网访问

通过HFS低成本搭建NAS&#xff0c;并内网穿透实现公网访问 文章目录 通过HFS低成本搭建NAS&#xff0c;并内网穿透实现公网访问前言1.下载安装cpolar1.1 设置HFS访客1.2 虚拟文件系统 2. 使用cpolar建立一条内网穿透数据隧道2.1 保留隧道2.2 隧道名称2.3 成功使用cpolar创建二级…

[保研/考研机试] KY103 2的幂次方 上海交通大学复试上机题 C++实现

题目链接&#xff1a; KY103 2的幂次方 https://www.nowcoder.com/share/jump/437195121691999575955 描述 Every positive number can be presented by the exponential form.For example, 137 2^7 2^3 2^0。 Lets present a^b by the form a(b).Then 137 is present…

MySQL8安装和删除教程 保姆级(Windows)

下载 官网: mysql官网点击Downloads->MySQL Community(GPL) Downloads->MySQL Community Server(或者点击MySQL installer for Windows) Windows下有两种安装方式 在线安装 一般带有 web字样 这个需要联网离线安装 一般没有web字样 安装 下载好之后,版本号可以不一样&…

Postman中,既想传递文件,还想传递多个参数(后端)

需求:既想传文件又想传多个参数可以用以下方式实现

做了这件事,精准拿捏企业资产管理!

资产管理系统是一种为组织和个人提供管理各类资产的重要工具。无论是金融资产还是实物资产&#xff0c;这些都构成了一个实体或个人财务状况的重要组成部分。 无论是企业寻求优化其固定资产维护&#xff0c;还是个人希望更好地管理他们的投资组合&#xff0c;资产管理系统在现代…

NZ系列工具NZ02:VBA读取PDF使用说明

【分享成果&#xff0c;随喜正能量】时光绽放并蒂莲&#xff0c;更是一份殷殷嘱托&#xff0c;更是一份诚挚祝福&#xff0c;是一份时光馈赠&#xff0c;又是一份时光陪伴。。 我的教程一共九套及VBA汉英手册一部&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解…

uniapp+uview封装小程序请求

提要&#xff1a; uniapp项目引入uview库 此步骤不再阐述 1.创建环境文件 env.js&#xff1a; let BASE_URL;if (process.env.NODE_ENV development) {// 开发环境BASE_URL 请求地址; } else {// 生产环境BASE_URL 请求地址; }export default BASE_URL; 2.创建请求文件 该…

QLExpress动态脚本引擎解析工具

介绍 QLExpress脚本引擎 1、线程安全&#xff0c;引擎运算过程中的产生的临时变量都是threadlocal类型。 2、高效执行&#xff0c;比较耗时的脚本编译过程可以缓存在本地机器&#xff0c;运行时的临时变量创建采用了缓冲池的技术&#xff0c;和groovy性能相当。 3、弱类型脚本…

广西Geotrust单位多域名https证书推荐

Geotrust是国际知名CA认证机构&#xff0c;根证书是Digicert&#xff0c;还有RapidSSL、QuickSSL等子品牌&#xff0c;拥有多种类型的多域名https证书&#xff0c;比如OV企业型https证书和EV增强型多域名https证书。那么&#xff0c;哪种多域名https证书更适合企事业单位使用呢…