using FFmpeg.AutoGen; using System; using System.Collections.Generic; using System.Drawing; using System.Runtime.InteropServices; namespace EC.FFmpegAutoGen { public sealed unsafe class VideoStreamDecoder : IDisposable { private readonly AVCodecContext* _pCodecContext; private readonly AVFormatContext* _pFormatContext; private readonly AVFrame* _pFrame; private readonly AVPacket* _pPacket; private readonly AVFrame* _receivedFrame; private readonly int _streamIndex; public VideoStreamDecoder(string url, AVHWDeviceType HWDeviceType = AVHWDeviceType.AV_HWDEVICE_TYPE_NONE) { _pFormatContext = ffmpeg.avformat_alloc_context(); _receivedFrame = ffmpeg.av_frame_alloc(); var pFormatContext = _pFormatContext; ffmpeg.avformat_open_input(&pFormatContext, url, null, null).ThrowExceptionIfError(); ffmpeg.avformat_find_stream_info(_pFormatContext, null).ThrowExceptionIfError(); AVCodec* codec = null; _streamIndex = ffmpeg .av_find_best_stream(_pFormatContext, AVMediaType.AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0) .ThrowExceptionIfError(); _pCodecContext = ffmpeg.avcodec_alloc_context3(codec); if (HWDeviceType != AVHWDeviceType.AV_HWDEVICE_TYPE_NONE) ffmpeg.av_hwdevice_ctx_create(&_pCodecContext->hw_device_ctx, HWDeviceType, null, null, 0) .ThrowExceptionIfError(); ffmpeg.avcodec_parameters_to_context(_pCodecContext, _pFormatContext->streams[_streamIndex]->codecpar) .ThrowExceptionIfError(); ffmpeg.avcodec_open2(_pCodecContext, codec, null).ThrowExceptionIfError(); CodecName = ffmpeg.avcodec_get_name(codec->id); FrameSize = new Size(_pCodecContext->width, _pCodecContext->height); PixelFormat = _pCodecContext->pix_fmt; _pPacket = ffmpeg.av_packet_alloc(); _pFrame = ffmpeg.av_frame_alloc(); } public string CodecName { get; } public Size FrameSize { get; } public AVPixelFormat PixelFormat { get; } public void Dispose() { try { var pFrame = _pFrame; ffmpeg.av_frame_free(&pFrame); var pPacket = _pPacket; ffmpeg.av_packet_free(&pPacket); ffmpeg.avcodec_close(_pCodecContext); var pFormatContext = _pFormatContext; ffmpeg.avformat_close_input(&pFormatContext); }catch(Exception ex) { Console.WriteLine(ex); } } public bool TryDecodeNextFrame(out AVFrame frame) { ffmpeg.av_frame_unref(_pFrame); ffmpeg.av_frame_unref(_receivedFrame); int error; do { try { do { ffmpeg.av_packet_unref(_pPacket); error = ffmpeg.av_read_frame(_pFormatContext, _pPacket); if (error == ffmpeg.AVERROR_EOF) { frame = *_pFrame; return false; } error.ThrowExceptionIfError(); } while (_pPacket->stream_index != _streamIndex); ffmpeg.avcodec_send_packet(_pCodecContext, _pPacket).ThrowExceptionIfError(); } finally { ffmpeg.av_packet_unref(_pPacket); } error = ffmpeg.avcodec_receive_frame(_pCodecContext, _pFrame); } while (error == ffmpeg.AVERROR(ffmpeg.EAGAIN)); error.ThrowExceptionIfError(); if (_pCodecContext->hw_device_ctx != null) { ffmpeg.av_hwframe_transfer_data(_receivedFrame, _pFrame, 0).ThrowExceptionIfError(); frame = *_receivedFrame; } else frame = *_pFrame; return true; } public IReadOnlyDictionary GetContextInfo() { AVDictionaryEntry* tag = null; var result = new Dictionary(); 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; } } }