由于内存使用情况,我们需要使用缓冲解决方案在内存中加载一小块流,而不是加载整个流:
avformat_network_init();
std::uint8_t *avioc_buffer =
static_cast<uint8_t *> (av_malloc(AV_BUFFER_BLOCK_SIZE));
avioc_ = avio_alloc_context(
avioc_buffer, AV_BUFFER_BLOCK_SIZE, 0, &data_provider_,
&ReadAVBlockCallback, nullptr, nullptr);
fmtc_ = avformat_alloc_context();
fmtc_->pb = avioc_[idx];
fmtc_->flags |= AVFMT_FLAG_CUSTOM_IO;
avformat_open_input(&fmtc_, nullptr, nullptr, nullptr);
ReadAVBlockCallback 是为重新填充新块而提供的回调。AV_BUFFER_BLOCK_SIZE 为 8Mytes。
问题是我需要使用 av_seek_frame API 实现搜索功能,而如果要搜索的帧不在当前缓冲区块中,av_seek_frame 不会在需要时调用回调“ReadAVBlockCallback”来填充缓冲区。我使用的是 FFmpeg 4.1(4.4 中也有同样的问题)。我重建了 FFmpeg 以使用调试器查看它。
我观察到的情况是:
“av_index_search_timestamp” 调用正在查看“流的 index_entries”,如果找到并返回了搜索点的条目,则无需调用回调来重新填充“流缓冲区”。但问题是这个缓冲区没有带有此搜索时间戳的数据包,因此它无法搜索到正确的关键帧。
以下是 matroskadec.c 的代码片段
if ((index = av_index_search_timestamp(st, timestamp, flags)) < 0 || index == st->nb_index_entries - 1) { //if the index is valid, no need to refill the stream buffer but the current buffer does not always have the target frame.
avio_seek(s->pb, st->index_entries[st->nb_index_entries - 1].pos,
SEEK_SET);
matroska->current_id = 0;
while ((index = av_index_search_timestamp(st, timestamp, flags)) < 0 || index == st->nb_index_entries - 1) {
matroska_clear_queue(matroska);
if (matroska_parse_cluster(matroska) < 0) // this is where the new block is load
break;
}
}
你能告诉我如何修复它吗?新版本中有解决方案吗?
万分感谢。