Lesen einer im Speicher befindlichen Datei mit libavformat

Lesezeit: 5 Minuten

Benutzer-Avatar
Tomaka17

Ich versuche gerade, kleine Videodateien zu lesen, die von einem Server gesendet werden

Um eine Datei mit libavformat zu lesen, müssen Sie aufrufen

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

Das Problem ist, dass sich die Datei in diesem Fall nicht auf der Festplatte, sondern im Speicher befindet.

Im Moment lade ich die Datei herunter, schreibe sie unter einem temporären Namen auf die Festplatte und rufe sie dann auf av_open_input_file mit dem temporären Dateinamen, was keine sehr saubere Lösung ist.

In der Tat, was ich will, ist eine Funktion wie av_open_custom(&avFormatContext, &myReadFunction, &mySeekFunction); aber in der Dokumentation habe ich nichts gefunden. Ich denke, es ist technisch möglich, da der Name der Datei der Bibliothek nicht hilft, das verwendete Format zu bestimmen.

Gibt es also eine solche Funktion oder eine Alternative zu av_open_input_file?

Benutzer-Avatar
Tomaka17

Es ist komisch, wie ich die Lösung immer selbst finde, gleich nachdem ich die Frage auf dieser Seite gepostet habe, obwohl ich stundenlang an diesem Problem gearbeitet habe.

Tatsächlich müssen Sie initialisieren avFormatContext->pb vor dem Anruf av_open_input, und übergeben Sie ihm einen gefälschten Dateinamen. Dies steht nicht in der Dokumentation, sondern in einem Kommentar direkt im Quellcode der Bibliothek.

Beispielcode, wenn Sie von einem istream laden möchten (ungetestet, nur damit jemand, der das gleiche Problem hat, auf die Idee kommt)

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);

  • das ist toll zu wissen. Hat jemand die Lösung ohne Verwendung der Std-Bibliothek ausprobiert?

    – tom

    24. Juli 2012 um 8:06 Uhr

  • für gcc, me._stream.read und me._stream.gcount sollte nur me.read und me.gcount sein, sehen Sie cplusplus.com/reference/iostream/istream

    – tmatth

    17. Oktober 2012 um 18:30 Uhr

  • Diese Lösung funktioniert großartig, bis ich suchen muss. Irgendeine Idee, wie man diese Lösung mit der Suche zum Laufen bringt? Ich verwende derzeit avformat_seek_file mit dem Formatkontext für den Videostream und den Audiostream separat. Wenn Sie die Suche nach einer Streaming-Datei (URL) verwenden, funktioniert dies hervorragend. Wenn ich mit dieser Methode auf einem lokalen mp4 bin, bekomme ich [mov,mp4,m4a,3gp,3g2,mj2 @ 00ee8360] stream 0, offset 0xfd97fc: partial file.

    – leetNachtschatten

    20. März 2016 um 23:50 Uhr

  • Ich bekomme auch einen Absturz in x64, aber nicht in x86, wenn ich diese Methode verwende, stürzt innerhalb von avformat_open_input ab.

    – leetNachtschatten

    7. April 2016 um 16:13 Uhr

  • Hallo, ich scheine ‘null’ auf `avFormat.get()’ zu bekommen – kennen Sie vielleicht den Grund dafür?

    – dk123

    24. Juni 2016 um 21:17 Uhr

Das sind großartige Informationen und haben mir ziemlich geholfen, aber es gibt ein paar Probleme, die die Leute beachten sollten. libavformat kann und wird mit Ihrem Puffer herumspielen, den Sie avio_alloc_context gegeben haben. Dies führt zu wirklich ärgerlichen Double-Free-Fehlern oder möglicherweise Speicherlecks. Als ich anfing, nach dem Problem zu suchen, fand ich es https://lists.ffmpeg.org/pipermail/libav-user/2012-December/003257.html was es perfekt getroffen hat.

Meine Problemumgehung beim Aufräumen dieser Arbeit besteht darin, einfach weiterzumachen und anzurufen

    av_free(avioContext->buffer)

und setzen Sie dann Ihren eigenen Pufferzeiger (den Sie für Ihren avio_alloc_context-Aufruf zugewiesen haben) auf NULL, wenn Sie sich dafür interessieren.

  • Vielen Dank. Dies direkt vor av_free(avioContext) zu tun, löste mein Speicherleckproblem!

    – Michel

    3. Dezember 2014 um 15:04 Uhr


  • @keefer heutzutage, vielleicht wegen der von Ihnen verlinkten Beschwerde, scheint es möglich zu sein, NULL als Puffer und 0 als Größe zu übergeben, und ffmpeg wird das Vernünftige tun, anstatt diese verrückte Art der Speicherverwaltung zu erfordern, bei der Sie einen Puffer zuweisen und es kann entscheiden, es nach Belieben neu zuzuweisen und Sie mit einem baumelnden Zeiger zurückzulassen.

    – Benutzer1593842

    18. April 2019 um 22:15 Uhr

Die ausgezeichnete Antwort von Tomaka17 gab mir einen guten Start zur Lösung eines analogen Problems mit Qt QIODevice anstelle von std::istream. Ich stellte fest, dass ich Aspekte der Lösung von Tomaka17 mit Aspekten der damit verbundenen Erfahrung bei kombinieren musste http://cdry.wordpress.com/2009/09/09/using-custom-io-callbacks-with-ffmpeg/

Meine benutzerdefinierte Read-Funktion sieht so aus:

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

… aber ich musste auch eine benutzerdefinierte Seek-Funktion erstellen:

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();
}

… und ich habe es so zusammengebunden:

...
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);
...

Hinweis Ich habe die Speicherverwaltungsprobleme noch nicht ausgearbeitet.

  • FF_INPUT_BUFFER_PADDING_SIZE geändert wurde AV_INPUT_BUFFER_PADDING_SIZE in den neuesten ffmpeg-Versionen

    – Shmil die Katze

    20. Dezember 2017 um 12:14 Uhr

1385760cookie-checkLesen einer im Speicher befindlichen Datei mit libavformat

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy