2012-03-07 16 views
24

Jestem obecnie próbuje odczytać małych plików wideo wysyłanych z serweraOdczyt pliku znajdującego się w pamięci z libavformat

Aby odczytać plik, używając libavformat, ci mają zadzwonić

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

Problem polega na tym, że w tym przypadku plik nie znajduje się na dysku, ale w pamięci.

W tej chwili robię to, pobierając plik, zapisując go na dysku, używając tymczasowej nazwy, a następnie wywołując av_open_input_file z nazwą pliku tymczasowego, co nie jest rozwiązaniem bardzo czystym.

W rzeczywistości to, czego chcę, to funkcja taka jak av_open_custom(&avFormatContext, &myReadFunction, &mySeekFunction);, ale nie znalazłem żadnego w dokumentacji. Zgaduję, że jest to technicznie możliwe, ponieważ nazwa pliku nie jest czymś, co pomaga bibliotece określić, którego formatu używa.

Czy istnieje funkcja podobna do tej lub alternatywa dla av_open_input_file?

+0

Escape your '\'s! ;-) – Konrad

Odpowiedz

30

To zabawne, że zawsze znajduję rozwiązanie samodzielnie zaraz po zamieszczeniu pytania na tej stronie, mimo że pracuję nad tym problemem od wielu godzin.

W rzeczywistości należy zainicjować avFormatContext->pb przed wywołaniem av_open_input i przekazać mu fałszywą nazwę pliku. Nie jest to napisane w dokumentacji, ale w komentarzu bezpośrednio w kodzie źródłowym biblioteki.

Przykład kodu, jeśli chcesz, aby załadować z istream (niesprawdzone, tak kogoś, który ma ten sam problem może się pomysł)

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

to świetnie wiedzieć. czy ktoś wypróbował rozwiązanie bez użycia biblioteki STD? – tom

+0

dla gcc, 'me._stream.read' i' me._stream.gcount' powinny być po prostu me.read i me.gcount, zobacz http://www.cplusplus.com/reference/iostream/istream/ – tmatth

+0

To rozwiązanie działa świetnie, dopóki nie będę potrzebował szukać. Czy masz pomysł, jak uzyskać to rozwiązanie przy wyszukiwaniu? Obecnie używam avformat_seek_file z kontekstem formatu strumienia wideo i strumienia audio oddzielnie. Podczas korzystania z wyszukiwania pliku strumieniowego (adresu URL) działa doskonale. Kiedy na lokalnym MP4 z tą metodą, otrzymuję '[mov, mp4, m4a, 3gp, 3g2, mj2 @ 00ee8360] strumień 0, przesunięcie 0xfd97fc: częściowy plik'. – leetNightshade

7

doskonała odpowiedź Tomaka17 dał mi dobry start w kierunku rozwiązywania analogiczną problemu używanie Qt QIODevice zamiast std :: istream. Uważam, że muszę połączyć aspekty rozwiązania Tomaka17 jest z aspektów związanych z nimi doświadczeń na http://cdry.wordpress.com/2009/09/09/using-custom-io-callbacks-with-ffmpeg/

Moje niestandardowe funkcja Czytaj wygląda następująco:

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

... ale Musiałem także tworzyć niestandardowe Szukajcie funkcja:

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

... i związał razem tak:

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

Uwaga Nie udało mi się jeszcze rozwiązać problemów związanych z zarządzaniem pamięcią.

+0

'FF_INPUT_BUFFER_PADDING_SIZE' został zmieniony na' AV_INPUT_BUFFER_PADDING_SIZE' w najnowszych wersjach ffmpeg –

9

To świetna informacja i pomogła mi całkiem sporo, ale jest kilka kwestii, o których ludzie powinni wiedzieć. libavformat może i będzie mieszać z twoim buforem, który dałeś avio_alloc_context. Prowadzi to do naprawdę irytujących podwójnych błędów lub prawdopodobnie wycieków pamięci. Kiedy zacząłem szukać problemu, znalazłem https://lists.ffmpeg.org/pipermail/libav-user/2012-December/003257.html, który idealnie go przybił.

Moja obejście podczas czyszczenia z tej pracy jest po prostu śmiało nazwać

av_free(avioContext->buffer) 

a następnie ustawiając własny wskaźnik bufora (które przeznaczono dla avio_alloc_context rozmowy) NULL jeśli zależy.

+0

Dzięki. Wykonanie tej czynności tuż przed av_free (avioContext) rozwiązało problem z wyciekiem pamięci! – Michel