FFmpeg 学習: ビデオを画像としてインターセプトします

概要

ビデオ ファイルには、いくつかの基本コンポーネントが含まれています。まず、ファイル自体はコンテナと呼ばれ、コンテナのタイプによってファイル内の情報の場所が決まります。AVI や Quicktime など。一連のストリームであり、通常はオーディオ ストリームとビデオ ストリームです。ストリーム内のデータ要素はフレームと呼ばれます。各ストリームは、異なる種類のコーデックによってエンコードされます。コーデックはデータのエンコードとデコードの方法を定義するため、コーデックという名前が付けられます。DivX や MP3 など。パケットは生のフレームにデコードできるデータを含むデータ フラグメントであり、各パケットには完全なフレームが含まれており、オーディオ ストリームの下に複数のフレームが含まれる場合もあります。

オーディオ データとビデオ データの処理は比較的単純ではなく、主に次の手順に分かれます。

10 ビデオ ファイルを開き、ストリームを取得します。
20 video_stram からデータ パケットを取得し、フレームに変換します。
30 フレームが読み込まれない場合は、実行を続行します。 20
40 フレーム を処理します
。 50 20 にジャンプします。

FFmpeg を使用してビデオを処理するのは非常に簡単です。この記事では、ビデオ ファイルのフレーム データを ppm 形式の画像として保存する方法を紹介します (ppm は、非常に単純なヘッダー情報と、残りのデータはすべて RGB データです)。

ファイルを開く

まず最初に、使用するライブラリを導入し、エラー出力関数を記述する必要があります。

#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavformat/avformat.h>
#include <libavutil/error.h>

void showErrMsg(int errNum, char *msg, size_t msgSize) {
    memset(msg, 0, msgSize);
    av_strerror(errNum, msg, msgSize);
    fprintf(stderr, msg);
}

この関数は、読みやすさを向上させるために FFmpeg 内のエラー コードを文字列に変換します。メイン関数は以下に記述されており、少なくとも 1 つのパラメータを渡す必要があり、パラメータはファイル パスとして開かれます。

fileName = argv[1];

if (argc < 2) {
    fprintf(stderr, "Please use FFmpegDemo1 <file> to open a video file");
    return -1;
}

if ((errNum = avformat_open_input(&pFormatCtx, fileName, NULL, NULL)) != 0) {
    showErrMsg(errNum, errMsg, 400);
    return -1;
}

av_register_all() 関数はバージョン 4 で非推奨になったため、情報を書き込む必要はありません。

int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options)この関数の機能は、URL が指すファイルを開くことです。

AVFormatContextこれは FFmepg の非常に重要な構造であり、Android のコンテキストに相当します。開発者がこの情報を簡単に取得できるように、FFmpeg の avformat のコンテキストを提供します。ただし、ここで渡されるポインタに注意してください。したがって、私たちの pFromatCtxAVFormatContextはポインター、およびパラメーターを渡すときにポインターのポインターを渡す必要があります。

fmt はファイルの形式です。NULL を渡すと、FFmpeg はファイルのエンコード形式を自動的に判断し、
ヘッダー情報を読み取り、情報を表示します。

if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
      showErrMsg(errNum, errMsg, 400);
      return -1;
  }


  av_dump_format(pFormatCtx, 0, fileName, 0);

avformat_find_stream_infoこの関数は、ファイルのストリームの情報を読み取りpFormatCtx->streamsav_dump_format関数を通じてこれらの詳細を出力します。

テストファイル出力の詳細は以下のとおりです。

Input #0, matroska,webm, from '../in.mkv':
  Metadata:
    title           :  www.*********.com
    encoder         : libebml v1.2.3 + libmatroska v1.3.0
    creation_time   : 2012-06-30T02:28:33.000000Z
  Duration: 00:25:43.08, start: 0.000000, bitrate: 1311 kb/s
    Chapter #0:0: start 0.105000, end 1543.082000
    Metadata:
      title           : 00:00:00.105
    Stream #0:0: Video: h264 (High), yuv420p(progressive), 1440x1080 [SAR 1:1 DAR 4:3], 23.98 fps, 23.98 tbr, 1k tbn, 47.95 tbc (default)
    Stream #0:1(jpn): Audio: aac (LC), 48000 Hz, stereo, fltp
    Metadata:
      title           : 日语
    Stream #0:2(chi): Audio: aac (HE-AAC), 44100 Hz, stereo, fltp (default)
    Metadata:
      title           : 国语
    Stream #0:3: Subtitle: subrip (default)

ビデオ ファイルには、ビデオ ストリームとオーディオ ストリームなどの多くのストリームが含まれており、オーディオ ストリームには複数の音声が含まれる場合があります。たとえば、上記の出力情報では、ビデオ ストリームは stream0 で、オーディオ ストリームには 2 つのストリーム (stream1 日本語) が含まれています。 stream2. 中国語。この記事の目的は、ビデオ ストリームをフレームに分解し、最初の 50 フレームの情報を保存することです。次に、対応するビデオ ストリームを見つける必要があります。コードは次のとおりです。

pFormatCtx的streamsすべてのフロー情報は に保存され、AVCodecParametersこの構造は に保存されAVMediaType、この列挙クラスはフローのタイプになります。

AVMEDIA_TYPE_UNKNOWN = -1,  ///< Usually treated as AVMEDIA_TYPE_DATA
AVMEDIA_TYPE_VIDEO,
AVMEDIA_TYPE_AUDIO,
AVMEDIA_TYPE_DATA,          ///< Opaque data information usually continuous
AVMEDIA_TYPE_SUBTITLE,
AVMEDIA_TYPE_ATTACHMENT,    ///< Opaque data information usually sparse
AVMEDIA_TYPE_NB

トラバースする必要があるのは、ビデオ ストリームを見つけて保存してAVCodecParametersインデックスを作成することだけです。1つAVCodecParametersだけでなくAVMediaType、もう 1 つありますAVCodecID。この列挙クラスは、ビデオ ファイルのエンコード方法を保存します。この ID に従って、対応するデコーダを見つける必要があります。 :

 pCodec = avcodec_find_decoder(codecPar->codec_id);
if (pCodec == NULL) {
    fprintf(stderr, "Unsupported codec!");
    return -1;
}

デコーダを検索した後、デコーダの関連コンテンツを初期化する必要があります。これは主に liaavcodec ライブラリによって行われ、コンテキストも必要です。

 pCodecCtx = avcodec_alloc_context3(pCodec);
avcodec_parameters_to_context(pCodecCtx, codecPar);

if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
    return -1;
}

上記のコードでは、pCodecCtxまず に領域を割り当て、そこに先ほどの codecPar パラメータを渡してpCodecCtx対応するパラメータに設定し、avcodec_open2pCodecCtx を初期化し、デコーダを開く作業が完了します。

情報処理

最も単純な分析図を作成しました。

まず、いくつかのデータ構造を初期化する必要があります

pFrame = av_frame_alloc();
AVFrame *pFrameRGB = av_frame_alloc();
if (pFrameRGB == NULL) {
    return -1;
}
unsigned char *buffer = NULL;
int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height,AV_INPUT_BUFFER_PADDING_SIZE);buffer = (unsigned char *) av_malloc(numBytes * sizeof(unsigned char));

av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, buffer, AV_PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height, AV_INPUT_BUFFER_PADDING_SIZE);

struct SwsContext *sws_ctx = NULL;
AVPacket packet;

sws_ctx = sws_getContext(pCodecCtx->width,
                             pCodecCtx->height,
                             pCodecCtx->pix_fmt,
                             pCodecCtx->width,
                             pCodecCtx->height,
                             AV_PIX_FMT_RGB24,
                             SWS_BILINEAR,
                             NULL, NULL, NULL);
i = 0;

pFrameはデコードされた元のデータを格納するための構造体で、図のpFrame(empty)とpFrame(full)に相当し、pFrameRGBはSwsContextによって変換されたデータで、図のpFrame(tosave)に相当し、直接格納できます。画像としての pFrameRGB。Buffer は配列へのポインタであり、pFrameRGB のデータ領域を提供するために使用されます。SwsContext は、単純に画像を拡大縮小するために使用される libswscale のコンテキストです。

次にデータを処理する必要があります。

1 つ目は、av_read_framevideo_stream のデータをパケットに読み取り、そのパケットをavcodec_send_packetAVCodecContext に送信し、AVCodecContext がデータを元の画像データ フレームにデコードします。avcodec_receive_frameデコードされたフレームを SWScaleContext を通じて取得し、画像を渡します。 SWScaleContext を介したデータ rgb 形式でフォーマットされており、最後にパケットを連続的に解放する必要がありますが、ここで注意していただきたいのは、相互に 1 対 1 の対応関係がないこと、つまり、データを入力した後にデータを出力する必要がないことですavcodec_send_packet複数のフレームのデータがキャッシュされる可能性があるavcodec_receive_frameためです。AVCodecContext

セーブデータ

データの保存は比較的簡単です。コードを見てください。

リソースを解放する

FFMpeg で割り当てられたリソースのほとんどは、1 つずつ呼び出すことができる単純な解放メソッドを提供します。

av_free(buffer);
   av_frame_unref(pFrameRGB);
   av_frame_unref(pFrame);
   av_free(pFrame);
   avcodec_free_context(&pCodecCtx);
   avformat_close_input(&pFormatCtx);

プロジェクトを実行する

まず、次の cmake ファイルを構成します。

pthread がインストールされていない場合は、最初に pthread をインストールする必要があります。そうしないとプログラムは実行できません。また、ライブラリのリンク順序にも注意する必要があります。

pthread のインストールコードは次のとおりです。

sudo apt-get install glibc-doc

sudo apt-get install manpages-posix-dev

clion の実行機能を直接使用してプログラムを実行できますが、最初にビデオ ファイルのパスをパラメーターとして追加します。また、ファイル ファイルを直接生成してコマンド ラインで実行することもできます。

mkdir build;cd build/    \\执行外部构建
cmake ..
make
FFmpegDemo1 ../in.mkv

実行が完了すると、生成された ppm ファイルが表示され、clion はそれを直接開くことができます。

オリジナルの FFmpeg 学習 1 - ビデオを画像としてインターセプト - Nuggets

★記事末尾の名刺では、オーディオ・ビデオ開発学習教材(FFmpeg、webRTC、rtmp、hls、rtsp、ffplay、srs)やオーディオ・ビデオ学習ロードマップ等を無料で受け取ることができます。

下記参照!

 

おすすめ

転載: blog.csdn.net/yinshipin007/article/details/130715242