32

現在、サーバーから送信された小さなビデオ ファイルを読み込もうとしています

libavformat を使用してファイルを読み取るには、次のように呼び出す必要があります。

av_open_input_file(&avFormatContext, "C:\\path\\to\\video.avi", 0, 0, 0);

問題は、この場合、ファイルがディスク上ではなくメモリ内にあることです。

私が現在行っていることは、ファイルをダウンロードし、一時的な名前を使用してディスクに書き込み、av_open_input_file一時的なファイル名で呼び出すことですが、これはあまりクリーンなソリューションではありません。

実際、私が欲しいのは次のような関数ですがav_open_custom(&avFormatContext, &myReadFunction, &mySeekFunction);、ドキュメントには見つかりませんでした。ファイルの名前は、ライブラリが使用している形式を判断するのに役立つものではないため、技術的には可能だと思います。

このような関数、または av_open_input_file に代わるものはありますか?

4

3 に答える 3

43

この問題に何時間も取り組んできたにもかかわらず、このサイトに質問を投稿した直後に、いつも自分で解決策を見つけるのは面白いことです。

実際、 をavFormatContext->pb呼び出す前に初期化av_open_inputし、偽のファイル名を渡す必要があります。これはドキュメントには書かれていませんが、ライブラリのソース コードの解説に直接書かれています。

istream からロードする場合のコード例 (テストされていないため、同じ問題を抱えている人がアイデアを得ることができます)

static int readFunction(void* opaque, uint8_t* buf, int buf_size) {
    auto& me = *reinterpret_cast<std::istream*>(opaque);
    me.read(reinterpret_cast<char*>(buf), buf_size);
    return me.gcount();
}

std::ifstream stream("file.avi", std::ios::binary);

const std::shared_ptr<unsigned char> buffer(reinterpret_cast<unsigned char*>(av_malloc(8192)), &av_free);
const std::shared_ptr<AVIOContext> avioContext(avio_alloc_context(buffer.get(), 8192, 0, reinterpret_cast<void*>(static_cast<std::istream*>(&stream)), &readFunction, nullptr, nullptr), &av_free);

const auto avFormat = std::shared_ptr<AVFormatContext>(avformat_alloc_context(), &avformat_free_context);
auto avFormatPtr = avFormat.get();
avFormat->pb = avioContext.get();
avformat_open_input(&avFormatPtr, "dummyFilename", nullptr, nullptr);
于 2012-03-07T16:19:44.387 に答える
14

これは素晴らしい情報であり、私をかなり助けてくれましたが、人々が知っておくべきいくつかの問題があります。libavformatは、avio_alloc_contextに指定したバッファーを混乱させる可能性があります。これは、本当に厄介なダブルフリーエラーまたはメモリリークにつながります。私が問題を探し始めたとき、私はそれを完全に釘付けにしたhttps://lists.ffmpeg.org/pipermail/libav-user/2012-December/003257.htmlを見つけました。

この作業からクリーンアップするときの私の回避策は、先に進んで電話することです

    av_free(avioContext->buffer)

次に、必要に応じて、独自のバッファポインタ(avio_alloc_context呼び出しに割り当てたもの)をNULLに設定します。

于 2013-03-22T15:32:55.377 に答える
9

Tomaka17 の優れた回答により、std::istream ではなく Qt QIODevice を使用して類似の問題を解決するための良いスタートが切れました。Tomaka17 のソリューションの側面と、 http: //cdry.wordpress.com/2009/09/09/using-custom-io-callbacks-with-ffmpeg/ にある関連する経験の側面を融合させる必要があることがわかりました。

私のカスタム Read 関数は次のようになります。

int readFunction(void* opaque, uint8_t* buf, int buf_size)
{
    QIODevice* stream = (QIODevice*)opaque;
    int numBytes = stream->read((char*)buf, buf_size);
    return numBytes;
}

...しかし、カスタム Seek 関数も作成する必要がありました。

int64_t seekFunction(void* opaque, int64_t offset, int whence)
{
    if (whence == AVSEEK_SIZE)
        return -1; // I don't know "size of my handle in bytes"
    QIODevice* stream = (QIODevice*)opaque;
    if (stream->isSequential())
        return -1; // cannot seek a sequential stream
    if (! stream->seek(offset) )
        return -1;
    return stream->pos();
}

...そして、次のように結び付けました。

...
const int ioBufferSize = 32768;
unsigned char * ioBuffer = (unsigned char *)av_malloc(ioBufferSize + FF_INPUT_BUFFER_PADDING_SIZE); // can get av_free()ed by libav
AVIOContext * avioContext = avio_alloc_context(ioBuffer, ioBufferSize, 0, (void*)(&fileStream), &readFunction, NULL, &seekFunction);
AVFormatContext * container = avformat_alloc_context();
container->pb = avioContext;
avformat_open_input(&container, "dummyFileName", NULL, NULL);
...

メモリ管理の問題はまだ解決していないことに注意してください。

于 2013-01-25T18:31:16.920 に答える