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