我正在尝试使用自定义上下文解码视频。目的是我想直接从内存中解码视频。在下面的代码中,我在传递给 avio_alloc_context
的 read
函数中读取文件 - 但这仅用于测试目的。
我想我已经阅读了 Stackoverflow 上或任何其他网站上与此主题相关的任何帖子。至少我确实尽力做到了。虽然有很多共同点,但细节有所不同:人们设置了不同的标志,有些人说 av_probe_input_format
是必需的,有些人说不是,等等。出于某种原因,对我没有任何作用。
我的问题是未指定像素格式(请参阅下面的输出),这就是我后来在调用 sws_getContext
时遇到问题的原因。我查看了pFormatContext->streams[videoStreamIndex]->codec->pix_fmt
,是-1。
请注意我在代码中的评论//我尝试过的事情
和//似乎没有帮助
。我认为,答案可能隐藏在那里。我尝试了迄今为止已阅读的许多提示组合,但我想我遗漏了一个细节。
问题不在于视频文件,因为当我采用标准方式并在没有自定义上下文的情况下调用 avformat_open_input(&pFormatContext, pFilePath, NULL, NULL)
时,一切运行正常。
代码按原样编译和运行。
#include <libavformat/avformat.h>
#include <string.h>
#include <stdio.h>
FILE *f;
static int read(void *opaque, uint8_t *buf, int buf_size) {
if (feof(f)) return -1;
return fread(buf, 1, buf_size, f);
}
int openVideo(const char *pFilePath) {
const int bufferSize = 32768;
int ret;
av_register_all();
f = fopen(pFilePath, "rb");
uint8_t *pBuffer = (uint8_t *) av_malloc(bufferSize + AVPROBE_PADDING_SIZE);
AVIOContext *pAVIOContext = avio_alloc_context(pBuffer, bufferSize, 0, NULL,
&read, NULL, NULL);
if (!f || !pBuffer || !pAVIOContext) {
printf("error: open / alloc failed\n");
// cleanup...
return 1;
}
AVFormatContext *pFormatContext = avformat_alloc_context();
pFormatContext->pb = pAVIOContext;
const int readBytes = read(NULL, pBuffer, bufferSize);
printf("readBytes = %i\n", readBytes);
if (readBytes <= 0) {
printf("error: read failed\n");
// cleanup...
return 2;
}
if (fseek(f, 0, SEEK_SET) != 0) {
printf("error: fseek failed\n");
// cleanup...
return 3;
}
// required for av_probe_input_format
memset(pBuffer + readBytes, 0, AVPROBE_PADDING_SIZE);
AVProbeData probeData;
probeData.buf = pBuffer;
probeData.buf_size = readBytes;
probeData.filename = "";
probeData.mime_type = NULL;
pFormatContext->iformat = av_probe_input_format(&probeData, 1);
// things I tried:
//pFormatContext->flags = AVFMT_FLAG_CUSTOM_IO;
//pFormatContext->iformat->flags |= AVFMT_NOFILE;
//pFormatContext->iformat->read_header = NULL;
// seems not to help (therefore commented out here):
AVDictionary *pDictionary = NULL;
//av_dict_set(&pDictionary, "analyzeduration", "8000000", 0);
//av_dict_set(&pDictionary, "probesize", "8000000", 0);
if ((ret = avformat_open_input(&pFormatContext, "", NULL, &pDictionary)) < 0) {
char buffer[4096];
av_strerror(ret, buffer, sizeof(buffer));
printf("error: avformat_open_input failed: %s\n", buffer);
// cleanup...
return 4;
}
printf("retrieving stream information...\n");
if ((ret = avformat_find_stream_info(pFormatContext, NULL)) < 0) {
char buffer[4096];
av_strerror(ret, buffer, sizeof(buffer));
printf("error: avformat_find_stream_info failed: %s\n", buffer);
// cleanup...
return 5;
}
printf("nb_streams = %i\n", pFormatContext->nb_streams);
// further code...
// cleanup...
return 0;
}
int main() {
openVideo("video.mp4");
return 0;
}
这是我得到的输出:
读取字节数 = 32768
正在检索流信息...
[mov,mp4,m4a,3gp,3g2,mj2 @ 0xdf8d20] 流 0,偏移量 0x30:部分文件
[mov,mp4,m4a,3gp,3g2,mj2 @ 0xdf8d20] 找不到流 0 的编解码器参数(视频:h264 (avc1/0x31637661),无,640x360,351 kb/s):未指定的像素格式
考虑增加“analyzeduration”和“probesize”选项的值
nb_streams = 2
更新:
感谢 WLGfx,这是解决方案:唯一缺少的是搜索功能。显然,实现它对于解码是强制性的。返回新的偏移量很重要——如果成功则返回新的偏移量而不是 0(网上找到的一些解决方案只返回 fseek 的返回值,这是错误的)。这是使其工作的最小解决方案:
static int64_t seek(void *opaque, int64_t offset, int whence) {
if (whence == SEEK_SET && fseek(f, offset, SEEK_SET) == 0) {
return offset;
}
// handling AVSEEK_SIZE doesn't seem mandatory
return -1;
}
当然,对avio_alloc_context
的调用需要做相应的调整:
AVIOContext *pAVIOContext = avio_alloc_context(pBuffer, bufferSize, 0, NULL,
&read, NULL, &seek);
最佳答案
因为你的是一个基于文件的流,所以它是可搜索的,所以你可以在创建 AVIOContext 时提供 AVIO 搜索:
avioContext = avio_alloc_context((uint8_t *)avio_buffer, AVIO_QUEUE_SIZE * PKT_SIZE7,
0,
this, // *** This is your data pointer to a class or other data passed to the callbacks
avio_ReadFunc,
NULL,
avio_SeekFunc);
用这个回调处理查找:(你可以将 ptr
转换为你的类或其他数据结构)
int64_t FFIOBufferManager::avio_SeekFunc(void *ptr, int64_t pos64, int whence) {
// SEEK_SET(0), SEEK_CUR(1), SEEK_END(2), AVSEEK_SIZE
// ptr is cast to your data or class
switch (whence) {
case 0 : // SEEK_SET
... etc
case (AVSEEK_SIZE) : // get size
return -1; // if you're unable to get the size
break;
}
// set new position in the file
return (int64_t)new_pos; // new position
}
您还可以在将 AVIOContext 附加到 AVFormatContext 时定义编解码器和探测大小。这允许 ffmpeg 在流中寻找以更好地确定格式。
context->pb = ffio->avioContext;
context->flags = AVFMT_FLAG_CUSTOM_IO;
context->iformat = av_find_input_format("mpegts"); // not necessary
context->probesize = 1200000;
到目前为止,我还不需要 av_probe_input_format
,但我的流还是 mpegts。
希望这对您有所帮助。
编辑:向 avio_alloc_context 函数添加注释以提及如何在回调中使用 ptr
。
关于c - FFmpeg:使用自定义上下文打开视频时未指定像素格式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47597638/