【问题标题】:Parsing frame-by-frame from .mov using ffmpeg使用ffmpeg从.mov逐帧解析
【发布时间】:2011-10-27 05:18:45
【问题描述】:

我正在尝试从 .mov 文件中解析 H.264 帧。我想我已经得出结论,来自 FFMPEG 的 AVFormat-part 的 mov.c 是要走的路。但是 mov.c 是未注释代码旁边的约 2600 行代码。我正在寻找使用 FFMPEG 的示例,尤其是解析任何文件类型的结构。不管是 MPEG4 还是 Quicktime Movie,因为它们的结构非常相似。

如果没有现有示例(我找不到任何示例),也许有人使用过它,可以给我几行代码,或者解释如何开始?

我正在尝试做的事情: 我使用 AVCaptureSession 从摄像机捕获样本,然后将这些样本编码为 H264 并在 AVAssetsWriter、AVAssetsWriterInput 和 AVAssetsWriterInputPixelBufferAdaptor 的帮助下写入文件。原因是我不能直接访问硬件 H264 编码,因为苹果不允许这样做。我现在需要做的(我认为不确定)是解析出来的:

.mov 文件中的“mdat”-atom(电影数据,我认为可能不止一个)。 然后是“vide”-atom,然后是vide-atom(视频数据样本,可能不止一个)。我认为会有几个我相信是框架的原子。这些将是“avc1”类型(这是 H264 的类型)。 请纠正我,因为我很确定我还没有正确理解所有这些

然后我的问题是,我将如何解析单帧。我一直在阅读the documentation 并查看了iFrameExtractor(这不是很有帮助,因为它会解码帧)。当我应该使用 FFMPEG-AVFormat 中的mov.c 时,我想我已经正确理解了,但我不确定。

编辑: 我现在正在尝试这样:

  1. 我运行了 iFrameExtractor 的略微简化的初始化函数 iFrameExtractor,它在 .mov 文件中查找视频流。

  2. 我得到这样的框架数据:

    AVPacket packet;
    av_read_frame(pFormatCtx, &packet);
    NSData *frame;
    if(packet.stream_index == videoStream){
        frame = [NSData dataWithBytes:packet.data length:packet.size];
    }
    videoStream++;
    av_free_packet(&packet);
    return frame;
    

然后我将它传递给 NSOperation 的一个子类,在该子类中保留它以等待上传。 但我收到 EXC_BAD_ACC,从帧复制数据时我做错了吗?有任何想法吗。当我尝试使用其(非原子,保留)属性设置类变量NSData* frame 时,我得到了 EXC_...。 (它在合成行上显示 EXC_BAD_ACC)

【问题讨论】:

  • mov.c 不会帮助您实现最终目标。如果您需要 MOV/MP4 解析帮助,它会很方便。另一个方便的库,用于当事情变得粗糙mp4v2。基本上你将不得不自己写这个。由于各种原因,没有图书馆会完成这项工作。
  • @Steve McFarlin,谢谢,除了 qt 文档以掌握 mov 文件的整个结构之外,您有任何阅读技巧,因为我真的很难掌握所有这些,对吗?原子甚至不需要按特定顺序排列?您认为哪一个最容易使用 mov 或 mp4?
  • 问题 1 - 您需要阅读 ISO-14496-10 第 7.3 节和附录 B(14496-15 也很有帮助)。你需要知道什么是NALU。虽然可以使用 mp4v2 或 FFmpeg,但我不推荐它。我可以保证你将不得不修改这些库的源代码来做你想做的事。 MOV 文件是一个树形结构。解析非常容易。我建议你先写一个原子解析器。只需将每个原子的 FOURCC 代码转储到控制台即可。有一个简单的 Java 项目漂浮在某处,可以帮助您入门。
  • 问题 2 - 实际上我还没有看到那个项目。它更可能是编码的 H264 帧。如果这是您要采用的路径,那么您应该使用movieFragmentInterval。这样,SPS/PPS NALU 在整部电影之前被写入文件。这些应该在额外数据字段中,并且很可能采用附件 B 格式。同样,使用 FFmpeg/mp4v2 会比从头开始编写更难。在 RT 中甚至可能无法使用这些库进行操作。您当然必须修改它们。
  • 在 MOV 文件中,SPS/PPS NALU 位于 MOV 文件的标题中。在 MOV 文件完成之前,它们不会被写入。您可以在 iOS 中使用 movieFragmentInterval 创建一个“流式”快速时间文件,以便在任何示例数据之前写入此信息。但是,生成的文件对于“手动”解析要复杂得多。我不确定 FFmpeg 是否支持这种文件类型。 mdat 原子包含样本数据。如果您只存储 AVC 数据,那么这将是 I 和 P 帧(假设 Baseline 3.x)。再次。我强烈建议您从头开始编写。

标签: parsing ffmpeg video-processing quicktime mpeg-4


【解决方案1】:

我使用以下内容来解析 mov 文件中的每一帧。

-(NSData *)nextFrame {
    AVPacket packet;
    NSData *frame = nil;

    while(!frame && av_read_frame(pFormatCtx, &packet)>=0) {

        if(packet.stream_index == streamNo) {
            frame = [[[NSData alloc] initWithBytes:packet.data length:packet.size] autorelease];
        }
        av_free_packet(&packet);
    }
    return frame;
}

虽然要注意,因为 av_read_frame 不验证帧,但这是在解码步骤中完成的。这意味着返回的“帧”可能包含不属于实际帧的额外信息。

要初始化 AVFormatContext *pFormatCtx 和 AVCodecContext *pCodecCtx,我使用以下代码(我相信它源自 Martin Böhme 的示例代码):

    AVCodec *pCodec;

    // Register all formats and codecs
    av_register_all();

    // Open video file
    if(avformat_open_input(&pFormatCtx, [moviePath cStringUsingEncoding:NSASCIIStringEncoding], NULL, NULL)!=0)
        goto initError; // Couldn't open file

    // Retrieve stream information
    if(avformat_find_stream_info(pFormatCtx,NULL)<0)
        goto initError; // Couldn't find stream information

    // Find the video stream
    streamNo = -1;
    for(int i=0; i<pFormatCtx->nb_streams; i++){
        if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            streamNo = i;
            break;
        }
    }
    if(streamNo == -1)
        goto initError; // Didn't find a video stream

    // Get a pointer to the codec context for the video stream
    pCodecCtx=pFormatCtx->streams[streamNo]->codec;

    // Find the decoder for the video stream
    pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
    if(pCodec==NULL)
        goto initError; // Codec not found

    // Open codec
    if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)
        goto initError; // Could not open codec

    return self;

initError:
    NSLog(@"initError in VideoFrameExtractor");
    [self release];
    return nil;

希望这对将来的某人有所帮助。

【讨论】:

    【解决方案2】:

    如果您将 H264 流式传输到 iOS,则需要分段流式传输(也称为苹果直播流式传输)。

    这是一个开源项目:http://code.google.com/p/httpsegmenter/

    【讨论】:

    • 我将从 iOS 流式传输。使用 AVCaptureSession 和 AVAssetsWriter 从相机写入文件。然后我想解析文件以获取 H264 帧并将它们上传到文件。我已经完成了所有工作,包括用于上传的 http 数据包。我需要的是一种访问 .mov 文件中的帧,访问原始帧数据的方法。也许它可以与另一个答案中发布的示例一起使用。我现在正在尝试,如果您对如何使其工作有其他建议,请分享:)
    • 您要删除音频通道吗?你想使用不同的容器吗?我还是不明白。
    • 我意识到另一个答案将不起作用,因为它会解码帧,因此它将不再以 H264 编码。我需要立即从视频流中提取帧而不解码
    • 我已经用(我认为)对我的问题的全面描述更新了这个问题。希望你能帮忙,谢谢
    【解决方案3】:

    有一个很好的关于使用 libavcodec/libavformat here 的教程。听起来您感兴趣的是他们尚未实现的DoSomethingWithTheImage() 函数。

    【讨论】:

    • 我想要原始的 H.264 数据,所以我可以稍后在服务器端将帧重新组合成一个 mov。我以前看过这个例子,不知道我是否应该跳过解码步骤?并保留rawData=packet.data?当我解码时会发生什么?那我应该从 H.264 标准出发吗?
    • @yi_H 我正在反汇编 .mov 文件,同时它正在录制以将 H264 帧发送到我再次重新组装它们的服务器。据我了解,这是使用 iOS 实时流式传输 H264 的唯一方法。
    • @awoodland 我不想解码它,我想要用 H264 编码的数据。你有解析 qtmov 格式的经验吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-12
    • 1970-01-01
    • 2015-12-02
    • 2011-08-29
    • 2016-07-21
    相关资源
    最近更新 更多