web-dev-qa-db-ja.com

X番をffmpegでフレーム化するにはどうすればよいですか?

私はビデオエディタを書いていますが、フレーム番号を知って、正確なフレームを探す必要があります。

Stackoverflowに関する他の投稿によると、ffmpegはシーク後にいくつかの壊れたフレームを表示する可能性があります。これは再生の問題ではありませんが、ビデオ編集者にとっては大きな問題です。

そして、私はフレーム番号でnotを時間で探す必要があります。これは、フレーム番号に変換すると不正確になります。

私はdrangerのtuts(今は時代遅れです)を読みました、そして結局:

_av_seek_frame(fmt_ctx, video_stream_id, frame, AVSEEK_FLAG_ANY);
_

常に_No. 0_をフレーム化しようとし、常に_return 0_をフレームに入れます。これは成功を意味します。次に、Blenderのソースコードを読み取ろうとしましたが、非常に複雑であることがわかりました(おそらく、画像バッファーを実装する必要がありますか?)。

それで、seek(context, frame_number)のような単純な呼び出しでフレームを探す簡単な方法はありますか(壊れたフレームではなく、完全なフレームを取得している間)?または、これを単純化する軽量ライブラリはありますか?

EDIT:ありがとうpraks411、私は解決策を見つけました:

_void AV_seek(AV * av, size_t frame)
{
    int frame_delta = frame - av->frame_id;
    if (frame_delta < 0 || frame_delta > 5)
        av_seek_frame(av->fmt_ctx, av->video_stream_id,
                frame, AVSEEK_FLAG_BACKWARD);
    while (av->frame_id != frame)
        AV_read_frame(av);
}

void AV_read_frame(AV * av)
{
    AVPacket packet;
    int frame_done;

    while (av_read_frame(av->fmt_ctx, &packet) >= 0) {
        if (packet.stream_index == av->video_stream_id) {
            avcodec_decode_video2(av->codec_ctx, av->frame, &frame_done, &packet);
            if (frame_done) {
                ...
                av->frame_id = packet.dts;
                av_free_packet(&packet);
                return;
            }
        }
        av_free_packet(&packet);
    }
}
_

EDIT2:このためのライブラリがあることがわかりました: FFMS2 。これは「フレームの正確なアクセスを容易にするFFmpegベースのソースライブラリ[...]」であり、移植可能です(少なくともWindowsとLinux間で)。

19
Giumo

av_seek_frameは、キーフレームへのタイムスタンプに基づいてのみシークします。キーフレームを探しているので、欲しいものが手に入らないかもしれません。したがって、最も近いキーフレームを探してから、目的のフレームに到達するまでフレームごとに読み取ることをお勧めします。

ただし、固定FPS値を扱っている場合は、タイムスタンプをフレームインデックスに簡単にマッピングできます。

ストリームを指定している場合は、シークする前に、時間をAVStream.time_base単位に変換する必要があります。 av_seek_frameavformat.hのffmpegドキュメントを読んでください。

たとえば、1.23秒のクリップを探したい場合:

 double m_out_start_time = 1.23;
 int flgs = AVSEEK_FLAG_ANY;
 int seek_ts = (m_out_start_time*(m_in_vid_strm->time_base.den))/(m_in_vid_strm->time_base.num);
 if(av_seek_frame(m_informat, m_in_vid_strm_idx,seek_ts, flgs) < 0)
 {
     PRINT_MSG("Failed to seek Video ")
 }
9
praks411