7

FFmpeg ライブラリ git-ee94362 libavformat v55.2.100 を使用しています。目的は、HLS を使用して 2 つのストリーム (ビデオとオーディオ) を M3U8 プレイリストに多重化することです。さらに、すべての TS セグメント ファイルの長さを正確に 3.0 秒にする必要があります (フレーム レートは 25 fps)。

これに到達するために、いくつかのオプションとプロパティを設定しようとしています

コードは次のようになります。

AVCodecContext *codec_ctx = NULL;
AVFormatContext *ofmt_ctx = NULL;

int ret = 0, gopSize = (int)(3.0 * 25);   // 3 sec * 25 fps

// ofmt_ctx and codec_ctx initialization and filling are OK, but: 
codec_ctx->time_base.num = 1;
codec_ctx->time_base.den = 25 // fps

// It seems, that the following three lines have no effect without explisit setting of the "hls_time" property
codec_ctx->keyint_min = gopSize;       // in FFMpeg application, the corresponding option is "-keyint_min 3"
codec_ctx->scenechange_threshold = 0;  // in FFMpeg application, the corresponding option is "-sc_threshold 0"
codec_ctx->gop_size = gopSize;         // in FFMpeg application, the corresponding option is "-g 3"

ret = av_opt_set_double(ofmt_ctx, "hls_time", 3.0, AV_OPT_SEARCH_CHILDREN);

// Any of the following lines causes "Option not found" error.
ret = av_opt_set(codec_ctx->priv_data, "profile", "main", AV_OPT_SEARCH_CHILDREN);
ret = av_opt_set(codec_ctx->priv_data, "preset", "ultrafast", AV_OPT_SEARCH_CHILDREN);
ret = av_opt_get(ofmt_ctx, "segment_time",  AV_OPT_SEARCH_CHILDREN, &str);
ret = av_opt_set((ofmt_ctx, "segment_time", "3.0", AV_OPT_SEARCH_CHILDREN);

とにかく、TS ファイルの長さは異なり (~2-3 秒)、正確には 3.0 秒ではありません。私たちの質問: 問題を解決する最善の方法は何ですか?

アンドレイ・モチェノフ。

4

4 に答える 4

2

あなたが直面している主な問題は、おそらくビデオ ファイルの適切な位置にキー フレームがないことです。入力からストリームをコピーするだけの場合、これは特に問題です。

FFmpeg は、セグメントを「カット」するタイミングを計算するためにキー フレームに依存しています。あなたがそれについて考えるとき、それは理にかなっています。各セグメントはそれ自体で完全に機能する必要があるため、2 つのキー フレームの間を単にカットすることはできません。さて、FFmpeg は独自に新しいキー フレームを挿入するべきだと主張する人もいるかもしれませんが、それはあまりにも使いやすいでしょう ;)

ありがたいことに、FFmpeg を使用してキー フレームを強制できます。パラメータを使用するか、コードでフラグを自分で設定します。すでにキー フレームを強制しようとしているとおっしゃいましたが、それは正しく行われなかったと思います。

私のこのテストでは、非常に良い結果が得られます。これはコマンド ラインのみです。申し訳ありませんが、コードでコマンド ライン パラメータを適用する方法をすでに知っているようですので、問題ないはずです。また、「hls_XXX」パラメーターを使用していないことにも注意してください。a) 正直なところ、それらを信頼していないためです。b) そうすれば、HLS 以外のストリームでも機能するはずです。

ffmpeg -i inputFile.mov -force_key_frames "expr:gte(t,n_forced*10)" -strict -2 -c:a aac -c:v libx264 -f segment -segment_list_type m3u8 -segment_list_size 0 -segment_time 10.0 -segment_time_delta 0.1 -segment_list stream/test.m3u8 stream/test%02d.ts 

force_key_frames コマンドがどのように機能するかは、こちらで確認できます。

ここまでで、上記のコマンドをいくつか追加して C++ で実装しました。ただし、トランスコーディング プロセス中にキー フレームを手動で設定したため、「force_key_frames」はありません。これが私がしたことです:

AVDictionary* headerOptions(0);
av_dict_set(&headerOptions, "segment_format", "mpegts", 0);
av_dict_set(&headerOptions, "segment_list_type", "m3u8", 0);
av_dict_set(&headerOptions, "segment_list", _playlistFileName.c_str(), 0);
av_dict_set_int(&headerOptions, "segment_list_size", 0, 0);
av_dict_set(&headerOptions, "segment_time_delta", TO_STRING(1.00).c_str(), 0);
av_dict_set(&headerOptions, "segment_time", TO_STRING(_segmentDuration).c_str(), 0);
av_dict_set_int(&headerOptions, "reference_stream", _videoStream->index, 0);
av_dict_set(&headerOptions, "segment_list_flags", "cache+live", 0);
avformat_write_header(_formatContext, &headerOptions);

結果の m3u8 は次のとおりです。

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-ALLOW-CACHE:YES
#EXT-X-TARGETDURATION:11
#EXTINF:10.083333,
test00.ts
#EXTINF:10.000000,
test01.ts
#EXTINF:10.000000,
test02.ts
#EXTINF:10.000000,
test03.ts
#EXTINF:10.000000,
test04.ts
#EXTINF:10.000000,
test05.ts
#EXTINF:0.083333,
test06.ts
#EXT-X-ENDLIST

完璧ではありませんが (最初の部分は少しずれています)、それ以上の結果は得られないと確信しています。

もちろん、ストリームをコピーするだけで、入力ファイルに常に正しいキー フレームがあることを確認するのが最善の方法ですが、取得するファイルを制御できない場合もあります。

サイドノート

コードで FFmpeg を使用している場合は、必ず最初に cli ffmpeg コマンドを使用してコードで行っていることを試してください。そのように動作させることができれば、少なくともコードでどのパラメータを設定すればよいかがわかります。コマンド ライン ツールを使用して動作する場合は、何らかの方法でコードで実行できるはずです ;)

于 2015-08-20T12:23:42.353 に答える