DUICUO

マルチメディア処理に不可欠 - FFmpeg ライブラリの強力な機能により、オーディオとビデオの処理がより効率的になります。

I. FFmpegライブラリの紹介

FFmpegは、オーディオおよびビデオフォーマットの変換、エンコードとデコード、ストリーミングメディア処理を可能にする、無料のオープンソースオーディオおよびビデオ処理ライブラリです。libavcodec(オーディオおよびビデオコーデック)、libavformat(カプセル化フォーマット処理ライブラリ)、libavfilter(オーディオおよびビデオフィルタライブラリ)など、複数のオープンソースコンポーネントで構成されています。高い移植性、強力な機能、そしてシンプルでメンテナンスしやすいコードにより、FFmpegはストリーミングメディア、マルチメディアプレーヤー、ビデオ編集ソフトウェア、ビデオ会議、ライブストリーミングなどで広く利用されています。

FFmpegは、MPEG4、AVI、WMV、FLV、H.264などのビデオフォーマットと、MP3、WMA、AAC、AMRなどのオーディオフォーマットをサポートしています。さらに、FFmpegはFFserverを介してストリーミングメディアサーバーを構築するために使用でき、RTSPやRTMPなどの伝送プロトコルをサポートしています。FFmpegは、ffmpegやffplayなどのコマンドラインツールも提供しており、オーディオファイルやビデオファイルを迅速に変換・再生できます。

FFmpeg の使用は比較的複雑ですが、対応する API ドキュメント、充実したコミュニティ サポート、強力な機能により、多くの開発者やビデオ愛好家に好まれるツールの 1 つとなっています。

II. FFmpegライブラリのユースケース

FFmpegは、ストリーミングメディア、マルチメディアプレーヤー、ビデオ編集ソフトウェア、ビデオ会議、ライブストリーミングなど、さまざまな分野で広く利用されています。以下の用途に使用できます。

  • メディア プレーヤー: FFmpeg ライブラリを使用して、さまざまなオーディオおよびビデオ形式のデコード、再生、制御ができるほか、早送り、一時停止、スクリーンショットなどの操作もサポートします。
  • ビデオ編集ソフトウェア:FFmpegライブラリが提供するオーディオおよびビデオ処理機能を通じて、ビデオ編集、結合、画像調整、字幕追加などの操作を実行できます。これは、ビデオ編集ソフトウェア開発に不可欠なコンポーネントの1つです。
  • ストリーミングメディアサービス:FFmpegライブラリを使用すると、カスタムレコーディングシステムやライブストリーミングシステムを実装できます。複数の伝送プロトコル(RTSP、RTMPなど)をサポートすることで、オーディオとビデオのストリームをインターネットにプッシュし、リアルタイムのライブストリーミングと配信を実現できます。
  • ビデオの変換と処理: FFmpeg ライブラリを使用すると、形式の変換、オーディオおよびビデオ ストリームの抽出、オーディオおよびビデオ ファイルへの透かしの追加などの操作を実行できるため、さまざまなオーディオおよびビデオ処理シナリオに適しています。

III. FFmpegライブラリアーキテクチャ設計

Fmpegライブラリはモジュール設計を採用しており、全体的なアーキテクチャは次のモジュールに分かれています。

  • libavcodec: H.264、HEVC、AAC、MP3 などの一般的な形式を含むさまざまなオーディオおよびビデオ形式のエンコードおよびデコード機能を提供するオーディオ/ビデオ コーデック モジュール。
  • libavformat: AVI、MP4、FLV、MKV などのさまざまなオーディオおよびビデオ コンテナ形式の読み取りと書き込みに使用されるコンテナ形式処理モジュール。
  • libavfilter: 画像処理、色調整、ミキシング、その他の操作のためのさまざまなフィルターと効果を提供するオーディオおよびビデオ フィルター モジュール。
  • libswscale: 画像カラースペース変換モジュール。主にビデオのスケーリング、変換、および処理操作に使用されます。
  • libavutil: 他のモジュールでの関数の実装をサポートするためにさまざまなユーティリティ関数とデータ構造を提供する汎用ユーティリティ ライブラリ。

FFmpegライブラリでは、各モジュールは比較的独立しており、単独でも組み合わせても使用できるため、モジュール間の呼び出しや拡張が容易です。例えば、オーディオとビデオのエンコードとデコードにはlibavcodecモジュール、カプセル化形式の処理にはlibavformatモジュール、そしてビデオのスケーリングと変換を行い、対象ファイルに出力するためのlibswscaleモジュールを使用できます。

IV. FFmpegライブラリの長所と短所

アドバンテージ:

  • オープンソースかつ無料であり、Windows、Linux、Mac OS などのクロスプラットフォーム オペレーティング システムをサポートしています。
  • さまざまなオーディオおよびビデオ形式のエンコード、デコード、変換、処理をサポートする強力なツールです。
  • 高度なカスタマイズが可能で、ニーズに応じてさらに開発したりカスタマイズしたりできます。
  • コミュニティは活発で、ドキュメントやチュートリアルも豊富にあるので、簡単に学習できます。

欠点:

  • 学習曲線は比較的急峻で、ある程度のプログラミング経験と基礎が必要です。
  • ドキュメントとチュートリアルは散在しており、検索して読むには忍耐が必要です。
  • 特定のシナリオではパフォーマンスのボトルネックが発生する可能性があり、ターゲットを絞った最適化が必要になります。

V. FFmpegのデコードプロセス

つまり、そのプロセスはおおよそ次のステップに分けられます。

  • メディア ファイルを読み取り、形式がサポートされているかどうかを確認し、メディア ファイルを開きます。
  • オーディオ ストリームとビデオ ストリームを取得し、それらがオーディオ ストリームであるかビデオ ストリームであるかを判断して、デコード操作を実行します。
  • データフレームが再生可能かどうかを判断します。再生可能であれば再生し、そうでない場合はフレームをスキップします。
  • 再生が完了すると、フレーム データによって占有されていたリソースが解放され、ファイルが完全に読み取られるまで次のフレーム データが読み取られます。
  • メディア ファイルを閉じます。

VI. FFmpeg APIの分類

FFmpeg API は、主に以下の側面を含む、多数のオーディオおよびビデオ処理機能とインターフェースを提供します。

  • AVFormat API: このAPIは主にマルチメディア形式の処理に使用され、マルチメディアファイルのカプセル化とカプセル化解除、多重化と多重化といった操作が含まれます。例えば、このAPIはオーディオファイルやビデオファイルを読み取り、それらのオーディオストリームとビデオストリームを取得するために使用できます。
  • AVCodec API: このAPIは、オーディオおよびビデオコーデックの実装を提供し、様々なオーディオおよびビデオ形式のエンコードおよびデコード操作をサポートします。例えば、MP4、FLV、その他の形式のデコードに使用できます。
  • AVFilter API: このAPIは、様々なフィルターやエフェクトを含むオーディオおよびビデオフィルタリング機能を提供します。画像処理、色調整、ブレンディングなどの操作に使用できます。例えば、ビデオの回転やスケーリングなどのフィルター操作を実行できます。
  • SwScaler API: このAPIは、主にビデオのスケーリング、変換、および処理に使用される画像の色空間変換機能を提供します。例えば、RGB形式の画像をYUV420P形式に変換できます。
  • AVutil API: この API は、メモリ管理、文字列処理、タイムスタンプ計算、その他の操作など、他のモジュールでの機能の実装をサポートするためのさまざまなユーティリティ関数とデータ構造を提供します。

VII. WPFコード例を使用してFFmpegライブラリの使用方法を紹介する

以下は、FFmpeg ライブラリを使用して 1 つのビデオ ファイルを別の形式に変換する方法を示した、WPF ベースの簡単な例です。

 using (var videoReader = new VideoFileReader()) { videoReader.Open(@"C:\Videos\input.mp4"); using (var videoWriter = new VideoFileWriter()) { var outputFilePath = @"C:\Videos\output.avi"; var codec = "msmpeg4v3"; videoWriter.Open(outputFilePath, videoReader.Width, videoReader.Height, videoReader.FrameRate, VideoCodec.FromFourCC(codec)); var currentFrame = new VideoFrame(videoReader.Width, videoReader.Height); while (videoReader.ReadVideoFrame(currentFrame)) { videoWriter.WriteVideoFrame(currentFrame); } } }

以下は、WPF を使用してビデオ デコーダーを作成するためのサンプル コード スニペットです。

 using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Imaging; using System.Threading.Tasks; using FFmpeg.AutoGen; namespace VideoDecoderDemo { public partial class MainWindow : Window { private AVFormatContext* pFormatCtx = null; private int videoStreamIndex = -1; private AVCodecContext* pCodecCtx = null; private AVCodec* pCodec = null; private AVFrame* pFrame = null; private AVPacket* pPacket = null; private AVPixelFormat sourcePixelFormat; private AVPixelFormat destinationPixelFormat; private IntPtr imgDataPtr = IntPtr.Zero; private int imgLineSize = 0; private Task decodingTask; private bool isDecoding = false; public MainWindow() { InitializeComponent(); } private void OpenFileButton_Click(object sender, RoutedEventArgs e) { Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); dlg.DefaultExt = ".mp4"; dlg.Filter = "Video Files (*.mp4;*.avi;*.mkv)|*.mp4;*.avi;*.mkv|All Files (*.*)|*.*"; Nullable<bool> result = dlg.ShowDialog(); if (result == true) { string filename = dlg.FileName; OpenVideoFile(filename); } } private void PlayButton_Click(object sender, RoutedEventArgs e) { if (!isDecoding) { StartDecoding(); PlayButton.Content = "停止播放"; } else { StopDecoding(); PlayButton.Content = "开始播放"; } } private unsafe void OpenVideoFile(string filename) { // 初始化FFmpeg库ffmpeg.av_register_all(); // 打开视频文件int ret = ffmpeg.avformat_open_input(&pFormatCtx, filename, null, null); if (ret < 0) { MessageBox.Show("打开视频文件失败:" + System.Runtime.InteropServices.Marshal.PtrToStringAnsi((IntPtr)ffmpeg.av_err2str(ret))); return; } // 获取视频流信息ret = ffmpeg.avformat_find_stream_info(pFormatCtx, null); if (ret < 0) { MessageBox.Show("获取视频流信息失败:" + System.Runtime.InteropServices.Marshal.PtrToStringAnsi((IntPtr)ffmpeg.av_err2str(ret))); return; } // 查找视频流索引for (int i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codec->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO) { videoStreamIndex = i; break; } } if (videoStreamIndex == -1) { MessageBox.Show("没有找到视频流"); return; } // 获取视频解码器pCodecCtx = pFormatCtx->streams[videoStreamIndex]->codec; pCodec = ffmpeg.avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == null) { MessageBox.Show("找不到视频解码器"); return; } // 打开视频解码器ret = ffmpeg.avcodec_open2(pCodecCtx, pCodec, null); if (ret < 0) { MessageBox.Show("打开视频解码器失败:" + System.Runtime.InteropServices.Marshal.PtrToStringAnsi((IntPtr)ffmpeg.av_err2str(ret))); return; } // 分配解码后数据的结构体pFrame = ffmpeg.av_frame_alloc(); // 分配解码前数据的结构体pPacket = ffmpeg.av_packet_alloc(); if (pPacket == null) { MessageBox.Show("分配AVPacket结构体失败"); return; } // 获取视频像素格式sourcePixelFormat = pCodecCtx->pix_fmt; if (sourcePixelFormat == AVPixelFormat.AV_PIX_FMT_NONE) { MessageBox.Show("找不到视频像素格式"); return; } // 设置要转换后的像素格式destinationPixelFormat = AVPixelFormat.AV_PIX_FMT_BGR24; // 计算转换后每行图像数据所占的字节数int bytesPerLine = ffmpeg.av_image_get_linesize(destinationPixelFormat, pCodecCtx->width, 0); // 分配转换后的图像数据空间imgDataPtr = (IntPtr)ffmpeg.av_malloc((ulong)bytesPerLine * pCodecCtx->height); // 创建Bitmap并显示BitmapSource bitmapSource = BitmapSource.Create(pCodecCtx->width, pCodecCtx->height, 96, 96, System.Windows.Media.PixelFormats.Bgr24, null, imgDataPtr, bytesPerLine * pCodecCtx->height, bytesPerLine); VideoImage.Source = bitmapSource; } private void StartDecoding() { isDecoding = true; decodingTask = new Task(() => { while (isDecoding && ffmpeg.av_read_frame(pFormatCtx, pPacket) >= 0) { if (pPacket->stream_index == videoStreamIndex) { int ret = ffmpeg.avcodec_send_packet(pCodecCtx, pPacket); if (ret < 0) { break; } while (ffmpeg.avcodec_receive_frame(pCodecCtx, pFrame) == 0) { // 创建SwScale上下文SwsContext* swsctx = ffmpeg.sws_getContext( pFrame->width, pFrame->height, sourcePixelFormat, pFrame->width, pFrame->height, destinationPixelFormat, ffmpeg.SWS_BICUBIC, null, null, null); // 执行像素格式转换ffmpeg.sws_scale(swsctx, pFrame->data, pFrame->linesize, 0, pFrame->height, &imgDataPtr, &imgLineSize); // 释放SwScale上下文ffmpeg.sws_freeContext(swsctx); Dispatcher.Invoke(() => { // 创建Bitmap并显示BitmapSource bitmapSource = BitmapSource.Create(pCodecCtx->width, pCodecCtx->height, 96, 96, System.Windows.Media.PixelFormats.Bgr24, null, imgDataPtr, imgLineSize * pCodecCtx->height, imgLineSize); VideoImage.Source = bitmapSource; }); } } // 释放AVPacket的缓冲区ffmpeg.av_packet_unref(pPacket); } StopDecoding(); // 释放内存if (imgDataPtr != IntPtr.Zero) { ffmpeg.av_free(imgDataPtr); imgDataPtr = IntPtr.Zero; } if (pPacket != null) { ffmpeg.av_packet_free(&pPacket); pPacket = null; } if (pFrame != null) { ffmpeg.av_frame_free(&pFrame); pFrame = null; } if (pCodecCtx != null) { ffmpeg.avcodec_close(pCodecCtx); pCodecCtx = null; } if (pFormatCtx != null) { ffmpeg.avformat_close_input(&pFormatCtx); pFormatCtx = null; } }); decodingTask.Start(); } private void StopDecoding() { isDecoding = false; if (decodingTask != null && !decodingTask.IsCompleted) { decodingTask.Wait(); } } } }

コードフローチャート

このコードは、FFmpeg を使用してビデオをデコードし、デコードされた画像を WPF のイメージ コントロールに表示します。`OpenFileButton_Click` 関数はビデオ ファイルを開きます。`PlayButton_Click` 関数はビデオの再生を開始/停止します。`StartDecoding` および `StopDecoding` 関数はデコードの開始と終了を制御します。`OpenVideoFile` 関数では、最初にビデオ ファイルを開き、ビデオ ストリーム情報を取得し、ビデオ ストリーム インデックスを見つけ、ビデオ デコーダーを取得し、ビデオ デコーダーを開いて、デコード前後のデータ用のメモリ領域を割り当てます。`StartDecoding` 関数では、`av_read_frame` と `avcodec_receive_frame` という 2 つの FFmpeg 関数を使用して、デコード前後のデータを取得します。これらの関数呼び出し内で、ピクセル形式の変換を実行し、変換された画像データをイメージ コントロールに表示します。最後に、`StopDecoding` 関数で、使用済みの FFmpeg メモリ領域をすべて解放し、デコーダーとビデオ ファイルを閉じます。

VI. FFmpegライブラリの概要

FFmpegは、様々なオーディオおよびビデオフォーマットのエンコード、デコード、変換、処理が可能な強力なオーディオおよびビデオ処理ライブラリです。習得は容易ではありませんが、ドキュメントとチュートリアルが充実しているため、容易に習得できます。特定のシナリオでは、FFmpegを使用することで、オーディオおよびビデオ処理ソリューションの開発を大幅に簡素化し、作業負荷を軽減できます。