主頁 >  其他 > FFMpeg.AutoGen(1)講解官方example代碼:Main函式、 解碼

FFMpeg.AutoGen(1)講解官方example代碼:Main函式、 解碼

2020-09-12 04:15:56 其他

FFMpeg是一套C編譯的開源工具集,主要用于視頻處理,可以編解碼視頻,建立流媒體服務器等等,官方網站:http://ffmpeg.org/

FFMpeg.AutoGen封裝方法以方便C#呼叫FFmpeg,專案地址:https://github.com/Ruslan-B/FFmpeg.AutoGen,可以使用NuGet安裝,


image

AutoGen只是封裝呼叫FFmpeg,程式還是需要下在FFmpeg工具放在程式目錄里,且版本要對應, 筆者用FFMpeg.AutoGetn的官方example代碼介紹一下FFMpege如何使用(源代碼在其github庫里),

example是一個命令列程式,mian函式里面的代碼如下,我將通過此函式呼叫順序介紹ffmpeg.AutoGet的用法,

目錄:

1.注冊FFmpeg庫,實際就將ffmpeg庫的地址告訴autogen

2.ffmpeg 一些呼叫其的配置(可選)
2.1 配置日志輸出
2.2配置硬體解碼器ffmpeg是支持硬解的.具體支持型別可以參考ffmpeg官方檔案,轉載網友摘錄的ffmpeg支持硬解編碼的列舉,

3.解碼函式DecodeAllFramesToImages
3.1 VideoStreamDecoder類
3.2 VideoFrameConverter類
3.3 相關資料結構AVPacket,AVFrame



本文使用ffmpeg.autogen版本4.2.2,對應ffmpeg版本也是4.2.2,

  1 private static void Main(string[] args)
  2         {
  3             Console.WriteLine("Current directory: " + Environment.CurrentDirectory);
  4             Console.WriteLine("Running in {0}-bit mode.", Environment.Is64BitProcess ? "64" : "32");
  5 
  6             FFmpegBinariesHelper.RegisterFFmpegBinaries();
  7 
  8             Console.WriteLine($"FFmpeg version info: {ffmpeg.av_version_info()}");
  9 
 10             //配置ffmpeg輸出日志
 11             SetupLogging();
 12             //配置硬體解碼器
 13             ConfigureHWDecoder(out var deviceType);
 14 
 15             //解碼
 16             Console.WriteLine("Decoding...");
 17             DecodeAllFramesToImages(deviceType);
 18 
 19             //編碼
 20             Console.WriteLine("Encoding...");
 21             EncodeImagesToH264();
 22         }

1.注冊FFmpeg庫,實際就將ffmpeg庫的地址告訴autogen

  1 FFmpegBinariesHelper.RegisterFFmpegBinaries();

注冊FFmpeg,這里的FFmpegBinariesHelper類需要在程式里重寫,我這里摘抄官方demo的代碼

  1 namespace FFmpeg.AutoGen.Example
  2 {
  3     public class FFmpegBinariesHelper
  4     {
  5         internal static void RegisterFFmpegBinaries()
  6         {
  7             var current = Environment.CurrentDirectory;
  8             var probe = Path.Combine("FFmpeg", "bin", Environment.Is64BitProcess ? "x64" : "x86");
  9             while (current != null)
 10             {
 11                 var ffmpegBinaryPath = Path.Combine(current, probe);
 12                 if (Directory.Exists(ffmpegBinaryPath))
 13                 {
 14                     Console.WriteLine($"FFmpeg binaries found in: {ffmpegBinaryPath}");
 15                     ffmpeg.RootPath = ffmpegBinaryPath;
 16                     return;
 17                 }
 18 
 19                 current = Directory.GetParent(current)?.FullName;
 20             }
 21         }
 22     }
 23 }

代碼的功能就是尋找ffmpeg的路徑,

核心代碼:

  1 ffmpeg.RootPath = ffmpegBinaryPath;

2.ffmpeg 一些呼叫其的配置(可選)

2.1 配置日志輸出

  1  	    /// <summary>
  2         /// 配置日志
  3         /// </summary>
  4         private static unsafe void SetupLogging()
  5         {
  6             ffmpeg.av_log_set_level(ffmpeg.AV_LOG_VERBOSE);
  7 
  8             // do not convert to local function
  9             av_log_set_callback_callback logCallback = (p0, level, format, vl) =>
 10             {
 11                 if (level > ffmpeg.av_log_get_level()) return;
 12 
 13                 var lineSize = 1024;
 14                 var lineBuffer = stackalloc byte[lineSize];
 15                 var printPrefix = 1;
 16                 ffmpeg.av_log_format_line(p0, level, format, vl, lineBuffer, lineSize, &printPrefix);
 17                 var line = Marshal.PtrToStringAnsi((IntPtr) lineBuffer);
 18                 Console.ForegroundColor = ConsoleColor.Yellow;
 19                 Console.Write(line);
 20                 Console.ResetColor();
 21             };
 22 
 23             ffmpeg.av_log_set_callback(logCallback);
 24         }

主要就是配置日志回呼,

核心代碼:

  1 ffmpeg.av_log_set_callback(logCallback)

2.2配置硬體解碼器ffmpeg是支持硬解的.具體支持型別可以參考ffmpeg官方檔案,轉載網友摘錄的ffmpeg支持硬解編碼的列舉,

  1 enum AVHWDeviceType {
  2     AV_HWDEVICE_TYPE_NONE,
  3     AV_HWDEVICE_TYPE_VDPAU,
  4     AV_HWDEVICE_TYPE_CUDA,
  5     AV_HWDEVICE_TYPE_VAAPI,
  6      AV_HWDEVICE_TYPE_DXVA2,
  7      AV_HWDEVICE_TYPE_QSV,
  8      AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
  9      AV_HWDEVICE_TYPE_D3D11VA,
 10      AV_HWDEVICE_TYPE_DRM,
 11      AV_HWDEVICE_TYPE_OPENCL,
 12      AV_HWDEVICE_TYPE_MEDIACODEC,
 13  };
example通過 ConfigureHWDecoder(out var deviceType); 獲取系統支持的硬體的解碼型別,并讓在命令列讓用戶選擇,然后將用戶選擇的硬體解碼器保存到區域變數deviceType里,解碼時根據此變數解碼,
  1         /// <summary>
  2         /// 配置硬體解碼器
  3         /// </summary>
  4         /// <param name="HWtype"></param>
  5         private static void ConfigureHWDecoder(out AVHWDeviceType HWtype)
  6         {
  7             HWtype = AVHWDeviceType.AV_HWDEVICE_TYPE_NONE;
  8             Console.WriteLine("Use hardware acceleration for decoding?[n]");
  9             var key = Console.ReadLine();
 10             var availableHWDecoders = new Dictionary<int, AVHWDeviceType>();
 11             if (key == "y")
 12             {
 13                 Console.WriteLine("Select hardware decoder:");
 14                 var type = AVHWDeviceType.AV_HWDEVICE_TYPE_NONE;
 15                 var number = 0;
 16                 while ((type = ffmpeg.av_hwdevice_iterate_types(type)) != AVHWDeviceType.AV_HWDEVICE_TYPE_NONE)
 17                 {
 18                     Console.WriteLine($"{++number}. {type}");
 19                     availableHWDecoders.Add(number, type);
 20                 }
 21                 if (availableHWDecoders.Count == 0)
 22                 {
 23                     Console.WriteLine("Your system have no hardware decoders.");
 24                     HWtype = ,;
 25                     return;
 26                 }
 27                 int decoderNumber = availableHWDecoders.SingleOrDefault(t => t.Value =https://www.cnblogs.com/edzjx/p/= AVHWDeviceType.AV_HWDEVICE_TYPE_DXVA2).Key;
 28                 if (decoderNumber == 0)
 29                     decoderNumber = availableHWDecoders.First().Key;
 30                 Console.WriteLine($"Selected [{decoderNumber}]");
 31                 int.TryParse(Console.ReadLine(),out var inputDecoderNumber);
 32                 availableHWDecoders.TryGetValue(inputDecoderNumber == 0 ? decoderNumber: inputDecoderNumber, out HWtype);
 33             }
 34         }
 35 

核心代碼:ffmpeg.av_hwdevice_iterate_types(type)獲得系統支持的硬體解碼,

ffmpeg.av_hwdevice_iterate_types(type)根據傳入的硬體解碼其型別,回傳AVHWDeviceType列舉里下一個系統支持的硬體解碼器型別,

3.Example里的解碼函式DecodeAllFramesToImages

  1         /// <summary>
  2         /// 解碼
  3         /// </summary>
  4         /// <param name="HWDevice"></param>
  5         private static unsafe void DecodeAllFramesToImages(AVHWDeviceType HWDevice)
  6         {
  7             // decode all frames from url, please not it might local resorce, e.g. string url = "../../sample_mpeg4.mp4";
  8             var url = "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"; // be advised this file holds 1440 frames
  9             using (var vsd = new VideoStreamDecoder(url,HWDevice))
 10             {
 11                 Console.WriteLine($"codec name: {vsd.CodecName}");
 12 
 13                 var info = vsd.GetContextInfo();
 14                 info.ToList().ForEach(x => Console.WriteLine($"{x.Key} = {x.Value}"));
 15 
 16                 var sourceSize = vsd.FrameSize;
 17                 var sourcePixelFormat = HWDevice == AVHWDeviceType.AV_HWDEVICE_TYPE_NONE ? vsd.PixelFormat : GetHWPixelFormat(HWDevice);
 18                 var destinationSize = sourceSize;
 19                 var destinationPixelFormat = AVPixelFormat.AV_PIX_FMT_BGR24;
 20                 using (var vfc = new VideoFrameConverter(sourceSize, sourcePixelFormat, destinationSize, destinationPixelFormat))
 21                 {
 22                     var frameNumber = 0;
 23                     while (vsd.TryDecodeNextFrame(out var frame))
 24                     {
 25                         var convertedFrame = vfc.Convert(frame);
 26 
 27                         using (var bitmap = new Bitmap(convertedFrame.width, convertedFrame.height, convertedFrame.linesize[0], PixelFormat.Format24bppRgb, (IntPtr) convertedFrame.data[0]))
 28                             bitmap.Save($"frame.{frameNumber:D8}.jpg", ImageFormat.Jpeg);
 29 
 30                         Console.WriteLine($"frame: {frameNumber}");
 31                         frameNumber++;
 32                     }
 33                 }
 34             }
 35         }

example源代碼里解碼主要使用VideoStreamDecoder和VideoFrameConverter兩個類,這兩個類不是FFMpeg.AutoGen里的型別,而是example代碼里,也就是說解碼作業是需要用戶自己封裝解碼類,圖省事可以直接照搬example里的代碼,筆者很推薦讀一下這兩個類的源代碼(可以在檔案末尾查附件看注釋過的這兩個類),可以搞清楚ffmpeg的解碼流程,

3.1 example里的VideoStreamDecoder類

VideoStreamDecoder作用:通過配置解碼器獲取實際有用的幀資料,大概的流程是:

  1. 打開流并引數回傳格式背景關系AVFormatContext (avformat_open_input(&pFormatContext, url, null, null))
  2. 獲取媒體資訊資料存到AVFormatContext 格式背景關系(avformat_find_stream_info(_pFormatContext, null))
  3. 據根AVFormatContext和媒體資訊(AVMediaType.AVMEDIA_TYPE_VIDEO )找到最佳匹配的流索引并引數回傳解碼器AVCodec(av_find_best_stream(_pFormatContext, AVMediaType.AVMEDIA_TYPE_VIDEO  , -1, -1 , &codec, 0))
  4. 根據AVCodec分配解碼器背景關系AVCodecContext(_pCodecContext=avcodec_alloc_context3(codec),如果指定硬解還要配置AVCodecContext里硬體解碼器hw_device_ctx)
  5. 配置解碼器背景關系格式引數,avcodec_parameters_to_context(根據_pFormatContext->streams[_streamIndex]->codecpar)
  6. 根據解碼器codec初始化解碼器背景關系 avcodec_open2(_pCodecContext, codec, null)
  7. 輪詢幀:把未解碼幀包(AVPacket)放入解碼器(avcodec_send_packet)從解碼器里獲取解碼的幀(AVFrame)(avcodec_receive_frame)

3.2example里的VideoFrameConverter類

VideoFrameConverter作用:對幀資料進行規格形狀轉換,格式轉換的大概流程

  • 創建幀格式轉換器SwsContext(ffmpeg.sws_getContext,可以指定轉換器的演算法,具體可以看參考檔案【6】)
  • 計算轉換程序中需要的快取
  • 創建快取:創建快取指標(ref _dstData, ref _dstLinesize)——創建快取實際記憶體——兩者關聯(av_image_fill_arrays),具體可以看參考檔案【7】【8】
  • 輪詢轉換:實際上就是呼叫sws_scale,最侄訓傳一個轉換好的AVFrame

3.3.相關資料結構AVPacket,AVFrame

其中有兩個概念包和幀需要注意一下,這里轉載灰色飄零博客里描述(參考檔案【5】):

AVPacket

用于存盤壓縮的資料,分別包括有音頻壓縮資料,視頻壓縮資料和字幕壓縮資料,它通常在解復用操作后存盤壓縮資料,然后作為輸入傳給解碼器,或者由編碼器輸出然后傳遞給復用器,對于視頻壓縮資料,一個AVPacket通常包括一個視頻幀,對于音頻壓縮資料,可能包括幾個壓縮的音頻幀,

AVFrame

用于存盤解碼后的音頻或者視頻資料,AVFrame必須通過av_frame_alloc進行分配,通過av_frame_free釋放,

兩者之間的關系

av_read_frame得到壓縮的資料包AVPacket,一般有三種壓縮的資料包(視頻、音頻和字幕),都用AVPacket表示,

然后呼叫avcodec_send_packet 和 avcodec_receive_frame對AVPacket進行解碼得到AVFrame,

注:從 FFmpeg 3.x 開始,avcodec_decode_video2 就被廢棄了,取而代之的是 avcodec_send_packet 和 avcodec_receive_frame,





參考檔案:

【1】FFmpeg視頻解碼硬體加速

【2】FFmpeg開發之PacketQueue中AVPacket和AVFrame關系

【3】ffmpeg+ffserver搭建流媒體服務器

【4】FFmpeg框架的基礎知識

【5】FFMPEG-資料結構解釋(AVCodecContext,AVStream,AVFormatContext)

【6】ffmpeg中的sws_scale演算法性能測驗 一片云霧 2011-10-29

【7】av_image_fill_arrays詳解 韭菜大蔥餡雞蛋 2019-12-14

【8】FFmpeg av_image_fill_arrays填充AVFrame資料緩沖 fengyuzaitu 2019-11-12


附件1 Example中unsafe void DecodeAllFramesToImages(AVHWDeviceType HWDevice)解碼函式原始碼及注釋

  1         /// <summary>
  2         /// 解碼
  3         /// </summary>
  4         /// <param name="HWDevice">硬體解碼型別</param>
  5         private static unsafe void DecodeAllFramesToImages(AVHWDeviceType HWDevice)
  6         {
  7             // decode all frames from url, please not it might local resorce, e.g. string url = "../../sample_mpeg4.mp4";
  8             var url = "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"; // be advised this file holds 1440 frames
  9 
 10             //使用自行撰寫的視頻解碼器類進行解碼
 11             using (var vsd = new VideoStreamDecoder(url,HWDevice))
 12             {
 13                 Console.WriteLine($"codec name: {vsd.CodecName}");
 14 
 15                 //獲取媒體資訊
 16                 var info = vsd.GetContextInfo();
 17                 info.ToList().ForEach(x => Console.WriteLine($"{x.Key} = {x.Value}"));
 18 
 19                 var sourceSize = vsd.FrameSize;
 20                 //資源編碼格式
 21                 var sourcePixelFormat = HWDevice == AVHWDeviceType.AV_HWDEVICE_TYPE_NONE ? vsd.PixelFormat : GetHWPixelFormat(HWDevice);
 22                 //目標尺寸與原尺寸一致
 23                 var destinationSize = sourceSize;
 24                 //目標媒體格式是bit型別
 25                 var destinationPixelFormat = AVPixelFormat.AV_PIX_FMT_BGR24;
 26                 //幀格式轉換
 27                 using (var vfc = new VideoFrameConverter(sourceSize, sourcePixelFormat, destinationSize, destinationPixelFormat))
 28                 {
 29                     var frameNumber = 0;
 30                     while (vsd.TryDecodeNextFrame(out var frame))
 31                     {
 32                         var convertedFrame = vfc.Convert(frame);
 33 
 34                         using (var bitmap = new Bitmap(convertedFrame.width, convertedFrame.height, convertedFrame.linesize[0], PixelFormat.Format24bppRgb, (IntPtr) convertedFrame.data[0]))
 35                             bitmap.Save($"frame.{frameNumber:D8}.jpg", ImageFormat.Jpeg);
 36 
 37                         Console.WriteLine($"frame: {frameNumber}");
 38                         frameNumber++;
 39                     }
 40                 }
 41             }
 42         }
 43 
View Code

附件2 Example中解碼類VideoStreamDecoder類原始碼及注釋

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Drawing;
  4 using System.IO;
  5 using System.Runtime.InteropServices;
  6 
  7 namespace FFmpeg.AutoGen.Example
  8 {
  9     public sealed unsafe class VideoStreamDecoder : IDisposable
 10     {
 11         private readonly AVCodecContext* _pCodecContext;
 12         private readonly AVFormatContext* _pFormatContext;
 13         private readonly int _streamIndex;
 14         //
 15         private readonly AVFrame* _pFrame;
 16         //
 17         private readonly AVFrame* _receivedFrame;
 18         private readonly AVPacket* _pPacket;
 19         /// <summary>
 20         /// 視頻解碼器
 21         /// </summary>
 22         /// <param name="url">視頻流URL</param>
 23         /// <param name="HWDeviceType">硬體解碼器型別(默認AVHWDeviceType.AV_HWDEVICE_TYPE_NONE)</param>
 24         public VideoStreamDecoder(string url, AVHWDeviceType HWDeviceType = AVHWDeviceType.AV_HWDEVICE_TYPE_NONE)
 25         {
 26             //分配一個AVFormatContext
 27             _pFormatContext = ffmpeg.avformat_alloc_context();
 28             //分配一個AVFrame
 29             _receivedFrame = ffmpeg.av_frame_alloc();
 30 
 31             var pFormatContext = _pFormatContext;
 32             //將源音視頻流傳遞給ffmpeg即ffmpeg打開源視頻流
 33             ffmpeg.avformat_open_input(&pFormatContext, url, null, null).ThrowExceptionIfError();
 34             //獲取音視頻流資訊
 35             ffmpeg.avformat_find_stream_info(_pFormatContext, null).ThrowExceptionIfError();
 36             AVCodec* codec = null;
 37             //在源里找到最佳的流,如果指定了解碼器,則根據解碼器尋找流,將解碼器傳遞給codec
 38             _streamIndex = ffmpeg.av_find_best_stream(_pFormatContext, AVMediaType.AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0).ThrowExceptionIfError();
 39             //根據解碼器分配一個AVCodecContext ,僅僅分配工具,還沒有初始化,
 40             _pCodecContext = ffmpeg.avcodec_alloc_context3(codec);
 41             //如果硬解碼
 42             if (HWDeviceType != AVHWDeviceType.AV_HWDEVICE_TYPE_NONE)
 43             {
 44                 //根據硬體編碼型別創建AVHWDeviceContext,存在AVFormatContext.hw_device_ctx (_pCodecContext->hw_device_ctx)
 45                 ffmpeg.av_hwdevice_ctx_create(&_pCodecContext->hw_device_ctx, HWDeviceType, null, null, 0).ThrowExceptionIfError();
 46             }
 47             //將最佳流的格式引數傳遞給codecContext
 48             ffmpeg.avcodec_parameters_to_context(_pCodecContext, _pFormatContext->streams[_streamIndex]->codecpar).ThrowExceptionIfError();
 49             //根據codec初始化pCodecContext ,與_pCodecContext = ffmpeg.avcodec_alloc_context3(codec);對應
 50             ffmpeg.avcodec_open2(_pCodecContext, codec, null).ThrowExceptionIfError();
 51 
 52             CodecName = ffmpeg.avcodec_get_name(codec->id);
 53             FrameSize = new Size(_pCodecContext->width, _pCodecContext->height);
 54             PixelFormat = _pCodecContext->pix_fmt;
 55             //分配AVPacket
 56             /* AVPacket用于存盤壓縮的資料,分別包括有音頻壓縮資料,視頻壓縮資料和字幕壓縮資料,
 57                        它通常在解復用操作后存盤壓縮資料,然后作為輸入傳給解碼器,或者由編碼器輸出然后傳遞給復用器,
 58                        對于視頻壓縮資料,一個AVPacket通常包括一個視頻幀,對于音頻壓縮資料,可能包括幾個壓縮的音頻幀,
 59              */
 60             _pPacket = ffmpeg.av_packet_alloc();
 61 
 62             //分配AVFrame
 63             /*AVFrame用于存盤解碼后的音頻或者視頻資料,
 64                     AVFrame必須通過av_frame_alloc進行分配,通過av_frame_free釋放,
 65             */
 66             _pFrame = ffmpeg.av_frame_alloc();
 67         }
 68 
 69         public string CodecName { get; }
 70         public Size FrameSize { get; }
 71         public AVPixelFormat PixelFormat { get; }
 72 
 73         public void Dispose()
 74         {
 75             ffmpeg.av_frame_unref(_pFrame);
 76             ffmpeg.av_free(_pFrame);
 77 
 78             ffmpeg.av_packet_unref(_pPacket);
 79             ffmpeg.av_free(_pPacket);
 80 
 81             ffmpeg.avcodec_close(_pCodecContext);
 82             var pFormatContext = _pFormatContext;
 83             ffmpeg.avformat_close_input(&pFormatContext);
 84         }
 85 
 86         /// <summary>
 87         /// 解碼下一幀幀
 88         /// </summary>
 89         /// <param name="frame">引數回傳解碼后的幀</param>
 90         /// <returns></returns>
 91         public bool TryDecodeNextFrame(out AVFrame frame)
 92         {
 93             //取消幀的參考,幀將不會被任何資源參考
 94             ffmpeg.av_frame_unref(_pFrame);
 95             ffmpeg.av_frame_unref(_receivedFrame);
 96             int error;
 97             do
 98             {
 99 
100 
101                 try
102                 {
103                     #region 讀取幀忽略無效幀
104                     do
105                     {
106 
107                         //讀取無效幀
108                         error = ffmpeg.av_read_frame(_pFormatContext, _pPacket);//根據pFormatContext讀取幀,回傳到Packet中
109                         if (error == ffmpeg.AVERROR_EOF)//如果已經是影視片流末尾則回傳
110                         {
111                             frame = *_pFrame;
112                             return false;
113                         }
114                         //數值是負數是錯誤資訊
115                         error.ThrowExceptionIfError();
116                     } while (_pPacket->stream_index != _streamIndex); //忽略掉音視頻流里面與有效流(初始化(建構式)時標記的_streamIndex)不一致的流
117                     #endregion
118 
119                     //將幀資料放入解碼器
120                     ffmpeg.avcodec_send_packet(_pCodecContext, _pPacket).ThrowExceptionIfError();  //將原始資料資料(_pPacket)作為輸入提供給解碼器(_pCodecContext)
121                 }
122                 finally
123                 {
124                     //消除對_pPacket的參考
125                     ffmpeg.av_packet_unref(_pPacket);
126                 }
127 
128 
129 
130                 //讀取解碼器里解碼(_pCodecContext)后的幀通過引數回傳(_pFrame)
131                 error = ffmpeg.avcodec_receive_frame(_pCodecContext, _pFrame);
132 
133             } while (error == ffmpeg.AVERROR(ffmpeg.EAGAIN));//當回傳值等于 EAGAIN(再試一次),認為讀取幀結束
134             error.ThrowExceptionIfError();
135 
136             if (_pCodecContext->hw_device_ctx != null)//如果配置了硬體解碼則呼叫硬體解碼器解碼
137             {
138                 //將_pFrame通過硬體解碼后放入_receivedFrame
139                 ffmpeg.av_hwframe_transfer_data(_receivedFrame, _pFrame, 0).ThrowExceptionIfError();
140                 frame = *_receivedFrame;
141             }
142             else
143             {
144                 frame = *_pFrame;
145             }
146             return true;
147         }
148 
149         /// <summary>
150         /// 獲取媒體TAG資訊
151         /// </summary>
152         /// <returns></returns>
153         public IReadOnlyDictionary<string, string> GetContextInfo()
154         {
155             AVDictionaryEntry* tag = null;
156             var result = new Dictionary<string, string>();
157             while ((tag = ffmpeg.av_dict_get(_pFormatContext->metadata, "", tag, ffmpeg.AV_DICT_IGNORE_SUFFIX)) != null)
158             {
159                 var key = Marshal.PtrToStringAnsi((IntPtr) tag->key);
160                 var value = Marshal.PtrToStringAnsi((IntPtr) tag->value);
161                 result.Add(key, value);
162             }
163 
164             return result;
165         }
166     }
167 }
View Code

附件3 Example中幀轉換類VideoFrameConverter類原始碼及注釋

  1 using System;
  2 using System.Drawing;
  3 using System.Runtime.InteropServices;
  4 
  5 namespace FFmpeg.AutoGen.Example
  6 {
  7     public sealed unsafe class VideoFrameConverter : IDisposable
  8     {
  9         private readonly IntPtr _convertedFrameBufferPtr;
 10         private readonly Size _destinationSize;
 11         private readonly byte_ptrArray4 _dstData;
 12         private readonly int_array4 _dstLinesize;
 13         private readonly SwsContext* _pConvertContext;
 14         /// <summary>
 15         /// 幀格式轉換
 16         /// </summary>
 17         /// <param name="sourceSize"></param>
 18         /// <param name="sourcePixelFormat"></param>
 19         /// <param name="destinationSize"></param>
 20         /// <param name="destinationPixelFormat"></param>
 21         public VideoFrameConverter(Size sourceSize, AVPixelFormat sourcePixelFormat,
 22             Size destinationSize, AVPixelFormat destinationPixelFormat)
 23         {
 24             _destinationSize = destinationSize;
 25                 //分配并回傳一個SwsContext,您需要它使用sws_scale()執行伸縮/轉換操作
 26                 //主要就是使用SwsContext進行轉換!!!
 27             _pConvertContext = ffmpeg.sws_getContext(sourceSize.Width, sourceSize.Height, sourcePixelFormat,
 28                 destinationSize.Width,
 29                 destinationSize.Height
 30                 , destinationPixelFormat,
 31                 ffmpeg.SWS_FAST_BILINEAR //默認演算法 還有其他演算法
 32                 , null
 33                 , null
 34                 , null //額外引數 在flasgs指定的演算法,而使用的引數,如果  SWS_BICUBIC  SWS_GAUSS  SWS_LANCZOS這些演算法,  這里沒有使用
 35                 );
 36             if (_pConvertContext == null) throw new ApplicationException("Could not initialize the conversion context.");
 37             //獲取媒體幀所需要的大小
 38             var convertedFrameBufferSize = ffmpeg.av_image_get_buffer_size(destinationPixelFormat
 39                 , destinationSize.Width, destinationSize.Height
 40                 , 1);
 41             //申請非托管記憶體,unsafe代碼
 42             _convertedFrameBufferPtr = Marshal.AllocHGlobal(convertedFrameBufferSize);
 43 
 44             //轉換幀的記憶體指標
 45             _dstData = https://www.cnblogs.com/edzjx/p/new byte_ptrArray4();
 46             _dstLinesize = new int_array4();
 47 
 48             //掛在幀資料的記憶體區把_dstData里存的的指標指向_convertedFrameBufferPtr
 49             ffmpeg.av_image_fill_arrays(ref _dstData, ref _dstLinesize
 50                 , (byte*) _convertedFrameBufferPtr
 51                 , destinationPixelFormat
 52                 , destinationSize.Width, destinationSize.Height
 53                 , 1);
 54         }
 55 
 56         public void Dispose()
 57         {
 58             Marshal.FreeHGlobal(_convertedFrameBufferPtr);
 59             ffmpeg.sws_freeContext(_pConvertContext);
 60         }
 61 
 62         public AVFrame Convert(AVFrame sourceFrame)
 63         {
 64             //轉換格式
 65             ffmpeg.sws_scale(_pConvertContext
 66                 , sourceFrame.data
 67                 , sourceFrame.linesize
 68                 , 0, sourceFrame.height
 69                 , _dstData, _dstLinesize);
 70 
 71             var data = https://www.cnblogs.com/edzjx/p/new byte_ptrArray8();
 72             data.UpdateFrom(_dstData);
 73             var linesize = new int_array8();
 74             linesize.UpdateFrom(_dstLinesize);
 75 
 76             return new AVFrame
 77             {
 78                 data = https://www.cnblogs.com/edzjx/p/data,
 79                 linesize = linesize,
 80                 width = _destinationSize.Width,
 81                 height = _destinationSize.Height
 82             };
 83         }
 84     }
 85 }
View Code

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/10523.html

標籤:其他

上一篇:VoIP Asterisk 互聯下載

下一篇:關于映射的問題。有沒人知道

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more