2012-03-07 25 views
24

Tôi hiện đang cố gắng để đọc các file video nhỏ được gửi từ một máy chủĐọc một tập tin nằm trong bộ nhớ với libavformat

Để đọc một tập tin sử dụng libavformat, bạn có nghĩa vụ phải gọi

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

Vấn đề là trong trường hợp này tập tin không nằm trên đĩa, nhưng trong bộ nhớ.

Điều tôi đang làm là tải xuống tệp, ghi tệp trên đĩa bằng tên tạm thời và sau đó gọi av_open_input_file bằng tên tệp tạm thời, không phải là giải pháp rất sạch sẽ.

Trong thực tế, những gì tôi muốn là một chức năng như av_open_custom(&avFormatContext, &myReadFunction, &mySeekFunction); nhưng tôi không tìm thấy bất kỳ tài liệu nào trong tài liệu. Tôi đoán nó là kỹ thuật có thể, vì tên của tập tin không phải là một cái gì đó giúp thư viện xác định định dạng nó đang sử dụng.

Vì vậy, có chức năng như thế này hoặc thay thế cho av_open_input_file?

+0

Thoát '\' của bạn! ;-) – Konrad

Trả lời

30

Thật buồn cười khi tôi luôn tự tìm ra giải pháp ngay sau khi tôi đăng câu hỏi trên trang web này, mặc dù tôi đã làm việc về vấn đề này hàng giờ liền.

Trong thực tế, bạn phải khởi tạo avFormatContext->pb trước khi gọi av_open_input và chuyển đến tên tệp giả. Điều này không được viết trong tài liệu nhưng trong một bình luận trực tiếp trong mã nguồn của thư viện.

Ví dụ mã nếu bạn muốn tải từ một istream (chưa được kiểm tra, chỉ để ai đó có cùng một vấn đề có thể lấy ý tưởng)

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

điều này thật tuyệt vời. có ai đã thử giải pháp w/o sử dụng thư viện std? – tom

+0

cho gcc, 'me._stream.read' và' me._stream.gcount' chỉ là me.read và me.gcount, xem http://www.cplusplus.com/reference/iostream/istream/ – tmatth

+0

Giải pháp này hoạt động tốt cho đến khi tôi cần tìm kiếm. Bất kỳ ý tưởng làm thế nào để có được giải pháp này làm việc với tìm kiếm? Tôi hiện đang sử dụng avformat_seek_file với ngữ cảnh định dạng trên luồng video và luồng âm thanh riêng biệt. Khi sử dụng tìm kiếm trên một tập tin trực tuyến (url) nó hoạt động tuyệt vời. Khi trên một mp4 địa phương với phương pháp này, tôi nhận được '[mov, mp4, m4a, 3gp, 3g2, mj2 @ 00ee8360] dòng 0, bù đắp 0xfd97fc: một phần tập tin'. – leetNightshade

7

câu trả lời tuyệt vời Tomaka17 đã quyết định cho tôi một sự khởi đầu tốt đối với việc giải quyết một vấn đề tương tự sử dụng Qt QIODevice thay vì std :: istream. Tôi tìm thấy tôi cần thiết để pha trộn các khía cạnh của giải pháp Tomaka17, với các khía cạnh của kinh nghiệm có liên quan tại http://cdry.wordpress.com/2009/09/09/using-custom-io-callbacks-with-ffmpeg/

tùy chỉnh của tôi đọc chức năng trông như thế này:

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

... nhưng tôi cũng cần thiết để tạo ra một tùy chỉnh tìm kiếm chức năng:

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

... và tôi gắn nó lại với nhau như thế này:

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

Lưu ý Tôi chưa nêu ra các vấn đề quản lý bộ nhớ.

+0

'FF_INPUT_BUFFER_PADDING_SIZE' đã được thay đổi thành' AV_INPUT_BUFFER_PADDING_SIZE' trong các phiên bản ffmpeg mới nhất –

9

Đây là thông tin tuyệt vời và đã giúp tôi khá nhiều, nhưng có một vài vấn đề mà mọi người nên biết. libavformat có thể và sẽ gây rối với bộ đệm của bạn mà bạn đã cung cấp cho avio_alloc_context. Điều này dẫn đến các lỗi đôi khi gây phiền nhiễu thực sự hoặc có thể là rò rỉ bộ nhớ. Khi tôi bắt đầu tìm kiếm sự cố, tôi đã tìm thấy https://lists.ffmpeg.org/pipermail/libav-user/2012-December/003257.html được đóng đinh hoàn hảo.

workaround của tôi khi dọn dẹp từ công việc này là chỉ cần đi trước và gọi

av_free(avioContext->buffer) 

và sau đó thiết lập con trỏ đệm của riêng bạn (mà bạn phân bổ cho cuộc gọi avio_alloc_context của bạn) để NULL nếu bạn quan tâm.

+0

Cảm ơn. Làm điều này ngay trước khi av_free (avioContext) giải quyết vấn đề rò rỉ bộ nhớ của tôi! – Michel

Các vấn đề liên quan