当前位置: 首页 > news >正文

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

相关文章:

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

C# 使用FFmpeg.Autogen对byte[]进行编解码&#xff0c;参考&#xff1a;https://github.com/vanjoge/CSharpVideoDemo 入口调用类&#xff1a; using System; using System.IO; using System.Drawing; using System.Runtime.InteropServices; using FFmpeg.AutoGen;namespace F…...

websocket是多线程的嘛

经过测试, onOpen事件的threadId和onMessage的threadId是不一样的&#xff0c;但是onMessage的threadId一直是同一个&#xff0c;就是说收消息的部分是单线程的&#xff0c;收到第一个Message后如果给它sleep较长时间&#xff0c;期间收到第二个&#xff0c;效果是它在排队&am…...

CentOS7.9 禁用22端口,使用其他端口替代

文章目录 业务场景操作步骤修改sshd配置文件修改SELinux开放给ssh使用的端口修改防火墙&#xff0c;开放新端口重启sshd生效 相关知识点介绍sshd服务SELinux服务firewall.service服务 业务场景 我们在某市实施交通信控平台项目&#xff0c;我们申请了一台服务器&#xff0c;用…...

2023国赛 高教社杯数学建模ABCDE题思路汇总分析

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 全国大学生数学建模…...

【网络层+数据链路层】深入理解IP协议和MAC帧协议的基本原理

文章目录 前言一、IP协议二、MAC帧协议 1.以太网2.以太网帧&#xff08;MAC帧&#xff09;格式报头3.基于协议讲解局域网转发的原理总结 前言 为什么经常将TCP/IP放在一起呢&#xff1f;这是因为IP层的核心工作就是通过IP地址来定位主机的&#xff0c;具有将一个数据报从A主机…...

银行家算法【学习算法】

银行家算法【学习算法】 前言版权推荐银行家算法7.避免死锁7.1 系统安全状态7.2 利用银行家算法避免死锁 Java算法实现代码结果 最后 前言 2023-8-14 18:18:01 以下内容源自《【学习算法】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台…...

萤石直播以及回放的接入和销毁

以下基于vue项目 1.安装 npm i ezuikit-js 2、导入 main.js中 import EZUIKit from "ezuikit-js"; //导入萤石Vue.use(EZUIKit); 3、创建容器 <div class"video"><div id"video-container"></div><!-- <iframe :src…...

C语言易错知识点总结2

函数 第 1 题&#xff08;单选题&#xff09; 题目名称&#xff1a; 能把函数处理结果的二个数据返回给主调函数&#xff0c;在下面的方法中不正确的是&#xff1a;&#xff08; &#xff09; 题目内容&#xff1a; A .return 这二个数 B .形参用数组 C .形参用二个指针 D .用…...

Go学习-Day1

Go学习-Day1 个人博客&#xff1a;CSDN博客 打卡。 Go语言的核心开发团队&#xff1a; Ken Thompson (C语言&#xff0c;B语言&#xff0c;Unix的发明者&#xff0c;牛人)Rob Pike(UTF-8发明人)Robert Griesemer(协助HotSpot编译器&#xff0c;Js引擎V8) Go语言有静态语言的…...

冠达管理:机构密集调研医药生物股 反腐政策影响受关注

进入8月&#xff0c;跟着反腐事件发酵&#xff0c;医药生物板块呈现震荡。与此一起&#xff0c;组织出资者对该板块上市公司也展开了密集调研。 到昨日&#xff0c;8月以来就有包含南微医学、百济神州、维力医疗、方盛制药等12家医药生物板块的上市公司接受组织调研&#xff0c…...

安装Tomac服务器——安装步骤以及易出现问题的解决方法

文章目录 前言 一、下载Tomcat及解压 1、选择下载版本&#xff08;本文选择tomcat 8版本为例&#xff09; 2、解压安装包 二、配置环境 1、在电脑搜索栏里面搜索环境变量即可 2、点击高级系统设置->环境变量->新建系统变量 1) 新建系统变量&#xff0c;变量名为…...

JVM 性能优化思路

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ 一般在系统出现问题的时候&#xff0c;我们会考虑对 JVM 进行性能优化。优化思路就是根据问题的情况&#xff0c;结合工具进行问题排查&#xff0c;针对排查出来的可能问题…...

Labview解决“重置VI:xxx.vi”报错问题

文章目录 前言一、程序框图二、前面板三、问题描述四、解决办法 前言 在程序关闭前面板的时候小概率型出现了 重置VI&#xff1a;xxx.vi 这个报错&#xff0c;并且发现此时只能通过任务管理器杀掉 LabVIEW 进程才能退出&#xff0c;这里介绍一下解决方法。 一、程序框图 程序…...

2023河南萌新联赛第(五)场:郑州轻工业大学C-数位dp

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 给定一个正整数 n&#xff0c;你可以对 n 进行任意次&#xff08;包括零次&#xff09;如下操作&#xff1a; 选择 n 上的某一数位&#xff0c;将其删去&#xff0c;剩下的左右部分合并。例如 123&#xff0c;你可以选择…...

找不到mfc140u.dll怎么办?mfc140u.dll丢失怎样修复?简单三招搞定

最近我遇到了一个问题&#xff0c;发现我的电脑上出现了mfc140u.dll文件丢失的错误提示。这个错误导致一些应用程序无法正常运行&#xff0c;让我感到非常困扰。经过一番研究和尝试&#xff0c;我终于成功修复了这个问题&#xff0c;并从中总结出了一些心得。 mfc140u.dll丢失原…...

了解 Langchain️是个啥?:第 1 部分

一、说明 在日常生活中&#xff0c;我们主要致力于构建端到端的应用程序。我们可以使用许多自动 ML 平台和 CI/CD 管道来自动化 ml 管道。我们还有像Roboflow和Andrew N.G.的登陆AI这样的工具来自动化或创建端到端的计算机视觉应用程序。 如果我们想在OpenAI或拥抱脸的帮助下创…...

Axure RP移动端高保真CRM办公客户管理系统原型模板及元件库

Axure RP移动端高保真CRM办公客户管理系统原型模板及元件库&#xff0c;一套典型的移动端办公工具型APP Axure RP原型模板&#xff0c;可根据实际的产品需求进行扩展&#xff0c;也可以作为移动端原型设计的参考案例。为提升本作品参考价值&#xff0c;在模板设计过程中尽量追求…...

【JAVA】我们常常谈到的方法是指什么?

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️初识JAVA】 文章目录 前言方法方法的分类方法的定义方法调用方法重载 前言 在之前的文章中我们总是会介绍到类中的各式各样的方法&#xff0c;也许在应用中我们对它已经有了初步的了解&#xff0c;今…...

今天来给大家聊一聊什么是Hierarchical-CTC模型

随着人工智能领域的不断发展&#xff0c;语音识别技术在日常生活和工业应用中扮演着越来越重要的角色。为了提高识别准确性和效率&#xff0c;研究人员不断探索新的模型和算法。在这个领域中&#xff0c;Hierarchical-CTC模型引起了广泛的关注和兴趣。本文将介绍什么是Hierarch…...

cout还是printf?C++教程 - How to C++系列专栏第4篇

关于专栏 这个专栏是优质的C教程专栏&#xff0c;如果你还没看过第一篇&#xff0c;点击这里去第0篇 本专栏一致使用操作系统&#xff1a;macOS Ventura&#xff0c;代码编辑器&#xff1a;CLion&#xff0c;C编译器&#xff1a;Clang 感谢一路相伴的朋友们&#xff0c;感谢…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

探索Selenium:自动化测试的神奇钥匙

目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...

用鸿蒙HarmonyOS5实现中国象棋小游戏的过程

下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...