使用自定义 AVIOContext 查找流信息时出现 FFMPEG 错误
FFMPEG error when finding stream information with custom AVIOContext
我正在编写将文件作为流接收并对其进行解码的软件。我有以下用于流输入的自定义 AVIO 代码:
/* Allocate a 4kb buffer for copying. */
std::uint32_t bufSize = 4096;
struct vidBuf
{
std::byte* ptr;
int size;
};
vidBuf tmpVidBuf = { const_cast<std::byte*>(videoBuffer.data()),
static_cast<int>(videoBuffer.size()) };
AVIOContext *avioContext =
avio_alloc_context(reinterpret_cast<std::uint8_t*>(av_malloc(bufSize)),
bufSize, 0,
reinterpret_cast<void*>(&tmpVidBuf),
[](void *opaque, std::uint8_t *buf, int bufSize) -> int
{
auto &me = *reinterpret_cast<vidBuf*>(opaque);
bufSize = std::min(bufSize, me.size);
std::copy_n(me.ptr, bufSize, reinterpret_cast<std::byte*>(buf));
me.ptr += bufSize;
me.size -= bufSize;
return bufSize;
}, nullptr, nullptr);
auto avFormatPtr = avformat_alloc_context();
avFormatPtr->pb = avioContext;
avFormatPtr->flags |= AVFMT_FLAG_CUSTOM_IO;
//avFormatPtr->probesize = tmpVidBuf.size;
//avFormatPtr->max_analyze_duration = 5000000;
avformat_open_input(&avFormatPtr, nullptr, nullptr, nullptr);
if(auto ret = avformat_find_stream_info(avFormatPtr, nullptr);
ret < 0)
logerror << "Could not open the video file: " << makeAVError(ret) << '\n';
但是,当我 运行 这段代码时,我得到了错误:
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x55d10736d580] stream 0, offset 0x30: partial file
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x55d10736d580] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none(tv, bt709), 540x360, 649 kb/s): unspecified pixel format
Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf58.76.100
Duration: 00:04:08.41, start: 0.000000, bitrate: N/A
Stream #0:0(und): Video: h264 (avc1 / 0x31637661), none(tv, bt709), 540x360, 649 kb/s, SAR 1:1 DAR 3:2, 29.97 fps, 29.97 tbr, 30k tbn, 60k tbc (default)
Metadata:
handler_name : ISO Media file produced by Google Inc. Created on: 01/10/2021.
vendor_id : [0][0][0][0]
Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 22050 Hz, mono, fltp, 69 kb/s (default)
Metadata:
handler_name : ISO Media file produced by Google Inc. Created on: 01/10/2021.
vendor_id : [0][0][0][0]
Assertion desc failed at libswscale/swscale_internal.h:677
注意视频流数据中缺少 YUV420p 部分。
这很奇怪,因为如果我 运行 我的程序使用不同的 mp4 文件,它工作得很好,这个错误只发生在特定的 mp4 文件上。我知道 mp4 文件是有效的,因为 mpv 可以播放它,并且 ffprobe 能够获取它的元数据:
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'heard.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf58.76.100
Duration: 00:04:08.41, start: 0.000000, bitrate: 724 kb/s
Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p(tv, bt709), 540x360 [SAR 1:1 DAR 3:2], 649 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 59.94 tbc (default)
Metadata:
handler_name : ISO Media file produced by Google Inc. Created on: 01/10/2021.
vendor_id : [0][0][0][0]
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 22050 Hz, mono, fltp, 69 kb/s (default)
Metadata:
handler_name : ISO Media file produced by Google Inc. Created on: 01/10/2021.
vendor_id : [0][0][0][0]
正如您在我的代码中所见,我也尝试设置 analyzeduration 和 probesize,但这并没有解决问题。
我也知道这个错误是因为我的自定义io,因为当我avformat_open_input
直接打开文件时,它能够很好地解码。我是 ffmpeg 的新手,所以我可能错过了一些简单的东西。
正如 SuRGeoNix 所指出的,我没有为 AVIO 上下文实现搜索功能;我认为这搞砸了 FFMPEG,因为它无法计算出缓冲区的大小。这是我现在的工作代码:
std::uint32_t bufSize = 4096;
struct vidBuf
{
std::byte* ptr;
std::byte* origPtr;
int size;
int fullSize;
};
vidBuf tmpVidBuf = { const_cast<std::byte*>(videoBuffer.data()),
const_cast<std::byte*>(videoBuffer.data()),
static_cast<int>(videoBuffer.size()),
static_cast<int>(videoBuffer.size()), };
AVIOContext *avioContext =
avio_alloc_context(reinterpret_cast<std::uint8_t*>(av_malloc(bufSize)),
bufSize, 0,
reinterpret_cast<void*>(&tmpVidBuf),
[](void *opaque, std::uint8_t *buf, int bufSize) -> int
{
auto &me = *reinterpret_cast<vidBuf*>(opaque);
bufSize = std::min(bufSize, me.size);
std::copy_n(me.ptr, bufSize, reinterpret_cast<std::byte*>(buf));
me.ptr += bufSize;
me.size -= bufSize;
return bufSize;
},
nullptr,
[](void *opaque, std::int64_t where, int whence) -> std::int64_t
{
auto me = reinterpret_cast<vidBuf*>(opaque);
switch(whence)
{
case AVSEEK_SIZE:
/* Maybe size left? */
return me->fullSize;
break;
case SEEK_SET:
if(me->fullSize > where)
{
me->ptr = me->origPtr + where;
me->size = me->fullSize - where;
}
else
return EOF;
break;
case SEEK_CUR:
if(me->size > where)
{
me->ptr += where;
me->size -= where;
}
else
return EOF;
break;
case SEEK_END:
if(me->fullSize > where)
{
me->ptr = (me->origPtr + me->fullSize) - where;
int curPos = me->ptr - me->origPtr;
me->size = me->fullSize - curPos;
}
else
return EOF;
break;
default:
/* On error, do nothing, return current position of file. */
logerror << "Could not process buffer seek: "
<< whence << ".\n";
break;
}
return me->ptr - me->origPtr;
});
我正在编写将文件作为流接收并对其进行解码的软件。我有以下用于流输入的自定义 AVIO 代码:
/* Allocate a 4kb buffer for copying. */
std::uint32_t bufSize = 4096;
struct vidBuf
{
std::byte* ptr;
int size;
};
vidBuf tmpVidBuf = { const_cast<std::byte*>(videoBuffer.data()),
static_cast<int>(videoBuffer.size()) };
AVIOContext *avioContext =
avio_alloc_context(reinterpret_cast<std::uint8_t*>(av_malloc(bufSize)),
bufSize, 0,
reinterpret_cast<void*>(&tmpVidBuf),
[](void *opaque, std::uint8_t *buf, int bufSize) -> int
{
auto &me = *reinterpret_cast<vidBuf*>(opaque);
bufSize = std::min(bufSize, me.size);
std::copy_n(me.ptr, bufSize, reinterpret_cast<std::byte*>(buf));
me.ptr += bufSize;
me.size -= bufSize;
return bufSize;
}, nullptr, nullptr);
auto avFormatPtr = avformat_alloc_context();
avFormatPtr->pb = avioContext;
avFormatPtr->flags |= AVFMT_FLAG_CUSTOM_IO;
//avFormatPtr->probesize = tmpVidBuf.size;
//avFormatPtr->max_analyze_duration = 5000000;
avformat_open_input(&avFormatPtr, nullptr, nullptr, nullptr);
if(auto ret = avformat_find_stream_info(avFormatPtr, nullptr);
ret < 0)
logerror << "Could not open the video file: " << makeAVError(ret) << '\n';
但是,当我 运行 这段代码时,我得到了错误:
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x55d10736d580] stream 0, offset 0x30: partial file
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x55d10736d580] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none(tv, bt709), 540x360, 649 kb/s): unspecified pixel format
Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf58.76.100
Duration: 00:04:08.41, start: 0.000000, bitrate: N/A
Stream #0:0(und): Video: h264 (avc1 / 0x31637661), none(tv, bt709), 540x360, 649 kb/s, SAR 1:1 DAR 3:2, 29.97 fps, 29.97 tbr, 30k tbn, 60k tbc (default)
Metadata:
handler_name : ISO Media file produced by Google Inc. Created on: 01/10/2021.
vendor_id : [0][0][0][0]
Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 22050 Hz, mono, fltp, 69 kb/s (default)
Metadata:
handler_name : ISO Media file produced by Google Inc. Created on: 01/10/2021.
vendor_id : [0][0][0][0]
Assertion desc failed at libswscale/swscale_internal.h:677
注意视频流数据中缺少 YUV420p 部分。
这很奇怪,因为如果我 运行 我的程序使用不同的 mp4 文件,它工作得很好,这个错误只发生在特定的 mp4 文件上。我知道 mp4 文件是有效的,因为 mpv 可以播放它,并且 ffprobe 能够获取它的元数据:
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'heard.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf58.76.100
Duration: 00:04:08.41, start: 0.000000, bitrate: 724 kb/s
Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p(tv, bt709), 540x360 [SAR 1:1 DAR 3:2], 649 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 59.94 tbc (default)
Metadata:
handler_name : ISO Media file produced by Google Inc. Created on: 01/10/2021.
vendor_id : [0][0][0][0]
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 22050 Hz, mono, fltp, 69 kb/s (default)
Metadata:
handler_name : ISO Media file produced by Google Inc. Created on: 01/10/2021.
vendor_id : [0][0][0][0]
正如您在我的代码中所见,我也尝试设置 analyzeduration 和 probesize,但这并没有解决问题。
我也知道这个错误是因为我的自定义io,因为当我avformat_open_input
直接打开文件时,它能够很好地解码。我是 ffmpeg 的新手,所以我可能错过了一些简单的东西。
正如 SuRGeoNix 所指出的,我没有为 AVIO 上下文实现搜索功能;我认为这搞砸了 FFMPEG,因为它无法计算出缓冲区的大小。这是我现在的工作代码:
std::uint32_t bufSize = 4096;
struct vidBuf
{
std::byte* ptr;
std::byte* origPtr;
int size;
int fullSize;
};
vidBuf tmpVidBuf = { const_cast<std::byte*>(videoBuffer.data()),
const_cast<std::byte*>(videoBuffer.data()),
static_cast<int>(videoBuffer.size()),
static_cast<int>(videoBuffer.size()), };
AVIOContext *avioContext =
avio_alloc_context(reinterpret_cast<std::uint8_t*>(av_malloc(bufSize)),
bufSize, 0,
reinterpret_cast<void*>(&tmpVidBuf),
[](void *opaque, std::uint8_t *buf, int bufSize) -> int
{
auto &me = *reinterpret_cast<vidBuf*>(opaque);
bufSize = std::min(bufSize, me.size);
std::copy_n(me.ptr, bufSize, reinterpret_cast<std::byte*>(buf));
me.ptr += bufSize;
me.size -= bufSize;
return bufSize;
},
nullptr,
[](void *opaque, std::int64_t where, int whence) -> std::int64_t
{
auto me = reinterpret_cast<vidBuf*>(opaque);
switch(whence)
{
case AVSEEK_SIZE:
/* Maybe size left? */
return me->fullSize;
break;
case SEEK_SET:
if(me->fullSize > where)
{
me->ptr = me->origPtr + where;
me->size = me->fullSize - where;
}
else
return EOF;
break;
case SEEK_CUR:
if(me->size > where)
{
me->ptr += where;
me->size -= where;
}
else
return EOF;
break;
case SEEK_END:
if(me->fullSize > where)
{
me->ptr = (me->origPtr + me->fullSize) - where;
int curPos = me->ptr - me->origPtr;
me->size = me->fullSize - curPos;
}
else
return EOF;
break;
default:
/* On error, do nothing, return current position of file. */
logerror << "Could not process buffer seek: "
<< whence << ".\n";
break;
}
return me->ptr - me->origPtr;
});