关于C#:如何从avi文件读取YUV8数据?

How to read YUV8 data from avi file?

我有一个avi文件,其中包含未压缩的灰色视频数据。我需要从中提取帧。文件大小为22 Gb。

我该怎么做?

我已经尝试过ffmpeg,但是它给我"找不到视频流的编解码器参数"消息-因为工作中没有编解码器,只有帧。

由于Opencv仅使用ffmpeg读取视频,因此也排除了opencv。

似乎剩下的唯一路径是尝试挖掘原始数据,但我不知道如何。

编辑:这是我使用opencv从文件中读取的代码。该故障发生在第二个if内。在文件上运行ffmpeg二进制文件也失败,并显示上述消息(找不到编解码器Aprameters等)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* register all formats and codecs */
av_register_all();

/* open input file, and allocate format context */
if (avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) < 0) {
    fprintf(stderr,"Could not open source file %s\
"
, src_filename);
    ret = 1;
    goto end;
}
fmt_ctx->seek2any = true;
/* retrieve stream information */
int res = avformat_find_stream_info(fmt_ctx, NULL);
if (res < 0) {
    fprintf(stderr,"Could not find stream information\
"
);
    ret = 1;
    goto end;
}

编辑:

这是我尝试提取的示例代码:pastebin。每次调用AVIStreamRead后,我得到的结果都是不变的缓冲区。


如果不需要跨平台功能,则Windows Video(VFW)API是一个不错的选择(http://msdn.microsoft.com/zh-cn/library/windows/desktop/dd756808(v=vs.85 ).aspx),因为有很多事情要做,所以我不会放置整个代码块,但是您应该能够从参考链接中找出来。基本上,您执行AVIFileOpen,然后使用streamtypeVIDEO通过AVIFileGetStream获取视频流,或者使用AVIStreamOpenFromFile一次执行视频流,然后使用AVIStreamRead从流中读取样本。如果您遇到无法解决的问题,我可以尝试提供帮助,但这应该非常简单。

此外,不确定ffmpeg为何失败,我一直在使用ffmpeg进行原始AVI读取,而没有涉及任何编解码器,您可以发布对ffpeg的调用实际上失败吗?

编辑:

对于读取的数据大小为0时出现的问题。AVI文件每秒具有N个用于帧的插槽,其中N是视频的fps。在现实生活中,采样不会以这种速度精确地到达(例如IP监控摄像机),因此实际数据采样索引可能是非连续的,例如1,5,11,...,并且VFW会在它们之间插入空采样(即从中读取零尺寸的样本)。您要做的是调用AVIStreamRead,其中NULL为缓冲区,大小为0,直到bRead不为0或您经过最后一个样本。当获得实际大小时,可以再次使用缓冲区指针和大小在该样本索引上调用AVIStreamRead。我通常会压缩视频,所以我不使用建议的大小,但是至少根据您的代码片段,我会做这样的事情:

1
2
3
4
5
6
7
8
9
10
11
12
13
...
bRead = 0;
do
{
    aviOpRes = AVIStreamRead(ppavi,smpS,1,NULL,0,&bRead,&smpN);
} while (bRead == 0 && ++smpS < si.dwLength + si.dwStart);
if(smpS >= si.dwLength + si.dwStart)
    break;
PUCHAR tempBuffer = new UCHAR[bRead];
aviOpRes = AVIStreamRead(ppavi,smpS,1,tempBuffer,bRead,&bRead,&smpN);
/* do whatever you need */
delete tempBuffer;
...

编辑2:

由于这可能使某人或您自己方便地在VFW和FFMPEG之间进行选择,因此我还更新了FFMPEG示例,以使其解析相同的文件(由于缺少错误检查,因此代码质量很抱歉,但是我想您可以请参阅逻辑流程):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/* register all formats and codecs */
av_register_all();
AVFormatContext* fmt_ctx = NULL;
/* open input file, and allocate format context */
const char *src_filename ="E:\\\\Output.avi";
if (avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) < 0) {
    fprintf(stderr,"Could not open source file %s\
"
, src_filename);
    abort();
}
/* retrieve stream information */
int res = avformat_find_stream_info(fmt_ctx, NULL);
if (res < 0) {
    fprintf(stderr,"Could not find stream information\
"
);
    abort();
}
int video_stream_index = 0; /* video stream is usualy 0 but still better to lookup in case it's not present */
for(; video_stream_index < fmt_ctx->nb_streams; ++video_stream_index)
{
    if(fmt_ctx->streams[video_stream_index]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        break;
}
if(video_stream_index == fmt_ctx->nb_streams)
    abort();
AVPacket *packet = new AVPacket;
while(av_read_frame(fmt_ctx, packet) == 0)
{
    if (packet->stream_index == video_stream_index)
        printf("Sample nr %d\
"
, packet->pts);
    av_free_packet(packet);
}

基本上,您打开上下文并从中读取数据包。您将同时获得音频和视频数据包,因此应检查该数据包是否属于感兴趣的流。 FFMPEG将为您节省空白帧的麻烦,并且仅提供其中包含数据的那些样本。