(原)从mp4,flv文件中解析出h264和aac,送解码器解码失败

  转载请注明出处http://www.cnblogs.com/lihaiping/p/5285166.html

  今天在做本地文件解码测试,发现从mp4,flv文件中读出来的帧数据,h264和aac帧直接送解码器解码,发现解码失败,但文件放在pc上用ffplay和vlc却都能播放,而且这个测试的视频文件是用ffmpeg.exe进行转码出来的,所以应该不存在解码不了的问题,那问题在哪呢?

  百度了下,网上有人说mp4文件里面封装的h264有两种格式:h264和avc1:

  而这两种格式的差别是:

    AVC1 描述:H.264 bitstream without start codes.一般通过ffmpeg转码生成的视频,是不带起始码0×00000001的。
    H264 描述:H.264 bitstream with start codes.一般对于一下HDVD等电影的压制格式,是带有起始码0×00000001的。

  所以我又用vlc播放查看了下,原来真的是avc1,这才发现原来自己接触了这么久的流媒体,连avc1都没听过,哎,悲哀。

  那要如何才能解码呢?同时我查看了一下,ffmpeg中对h264的avc1并没有设置单独的解码器,只有一个h264的解码器codeid,那它是怎么实现解码avc1的?又是如何区分的呢?

  问题1:如何解码?

  在这里其实有人遇到了和我一样的问题:http://stackoverflow.com/questions/11330764/ffmpeg-cant-decode-h264-stream-frame-data

  同时还有人在论坛上讨论该如何解决:http://bbs.csdn.net/topics/390538510

  有人说avc1是原始的NAL打包格式,就是开始的若干字节(1,2,4字节)是NAL的长度,而不是start_code,此时必须借助某个全局的数据来获得编码器的profile,level,PPS,SPS等信息才可以解码。

  既然这样,那我要如何才能获得pps,sps等这些信息呢?有人写过:http://blog.csdn.net/gavinr/article/details/7183499,在这篇文章里面,需要注意几个之前没认真了解的新地方:

    1)pps及sps不能从packet获得,而是保存在AVCodecContext的extradata数据域中

    2)如何从extradata中解析出sps及pps呢?ffmpeg中提供了一个流过滤器"h264_mp4toannexb"可以完成

  解决方法为:使用ffmpeg提供的h264_mp4toannexb流过滤器进行解决:具体方法可以参考:http://blog.csdn.net/leixiaohua1020/article/details/11800877

  问题2:ffmpeg中没有对avc1使用单独的解码器,而是和h264同样使用同一个解码器?那它是如何区分的呢?

  这个问题,需要仔细翻看一下ffmpeg的源代码了,在ff_h264_decode_init函数中有这样的一段代码:

  

if (avctx->extradata_size > 0 && avctx->extradata) {
        ret = ff_h264_decode_extradata(h, avctx->extradata, avctx->extradata_size);
        if (ret < 0) {
            ff_h264_free_context(h);
            return ret;
        }
    }

继续往下看,看ff_h264_decode_extradata函数中做了些什么?

int ff_h264_decode_extradata(H264Context *h, const uint8_t *buf, int size)
{
    AVCodecContext *avctx = h->avctx;
    int ret;

    if (!buf || size <= 0)
        return -1;

    if (buf[0] == 1) {
        int i, cnt, nalsize;
        const unsigned char *p = buf;

        h->is_avc = 1;

        if (size < 7) {
            av_log(avctx, AV_LOG_ERROR,
                   "avcC %d too short\n", size);
            return AVERROR_INVALIDDATA;
        }
        /* sps and pps in the avcC always have length coded with 2 bytes,
         * so put a fake nal_length_size = 2 while parsing them */
        h->nal_length_size = 2;
        // Decode sps from avcC
        cnt = *(p + 5) & 0x1f; // Number of sps
        p  += 6;
        for (i = 0; i < cnt; i++) {
            nalsize = AV_RB16(p) + 2;
            if(nalsize > size - (p-buf))
                return AVERROR_INVALIDDATA;
            ret = decode_nal_units(h, p, nalsize, 1);
            if (ret < 0) {
                av_log(avctx, AV_LOG_ERROR,
                       "Decoding sps %d from avcC failed\n", i);
                return ret;
            }
            p += nalsize;
        }
        // Decode pps from avcC
        cnt = *(p++); // Number of pps
        for (i = 0; i < cnt; i++) {
            nalsize = AV_RB16(p) + 2;
            if(nalsize > size - (p-buf))
                return AVERROR_INVALIDDATA;
            ret = decode_nal_units(h, p, nalsize, 1);
            if (ret < 0) {
                av_log(avctx, AV_LOG_ERROR,
                       "Decoding pps %d from avcC failed\n", i);
                return ret;
            }
            p += nalsize;
        }
        // Store right nal length size that will be used to parse all other nals
        h->nal_length_size = (buf[4] & 0x03) + 1;
    } else {
        h->is_avc = 0;
        ret = decode_nal_units(h, buf, size, 1);
        if (ret < 0)
            return ret;
    }
    return size;
}

所以再这里h264的解码器是通过AVCodecContext的extradata在ff_h264_decode_init的时候,进行了区分,同时也进行初始化解码。

=================================================================

aac解码失败的问题:

http://blog.csdn.net/leixiaohua1020/article/details/39767055   这篇文章说了,视音频分离器(Demuxer),并不适用于一些格式。对于MP3编码的音频是没有问题的。但是在分离MP4/FLV/MKV等一些格式中的AAC编码的码流的时候,得到的AAC码流是不能播放的。原因是存储AAC数据的AVPacket的data字段中的数据是不包含7字节ADTS文件头的“砍头”的数据,是无法直接解码播放的(当然如果在这些数据前面手工加上7字节的ADTS文件头的话,就可以播放了)。

adts?又是一个新概念,怎么解决?谷歌不在问度娘,http://blog.csdn.net/tx3344/article/details/7414543,这篇文章介绍了adts的概念。

本来是想通过和h264的avc1方案一样来解决,但发现使用aac_adtstoasc流过滤器是行不通的,因为他一直是返回0,于是我看了一下ffmpeg中这个函数的源码,原来这个函数的源码就是返回0的.测试结果也是解码不了。

后面找到了一篇文章:http://blog.chinaunix.net/uid-24922718-id-3692670.html,本来想参考着这里面的方法实现:

                char bits[7] = {0};
                int sample_index = 0 , channel = 0;
                char temp = 0;
                int length = 7 + audiopack.size;
                sample_index = (audioCodecCtx->extradata[0] & 0x07) << 1;
                temp = (audioCodecCtx->extradata[1]&0x80);
                switch(audioCodecCtx->sample_rate)
                {
                    case 44100:
                        {
                            sample_index = 0x7;
                        }break;
                    default:
                        {
                            sample_index = sample_index + (temp>>7);
                        }break;
                }
                channel = ((audioCodecCtx->extradata[1] - temp) & 0xff) >> 3;
                bits[0] = 0xff;
                bits[1] = 0xf1;
                bits[2] = 0x40 | (sample_index<<2) | (channel>>2);
                bits[3] = ((channel&0x3)<<6) | (length >>11);
                bits[4] = (length>>3) & 0xff;
                bits[5] = ((length<<5) & 0xff) | 0x1f;
                bits[6] = 0xfc;

                fwrite(bits,1,7,f);

结果失败了,添加这几个自己的adts头还是一样解码不出数据。

最后参考http://blog.itpub.net/30168498/viewspace-1576794/这个文章的代码,进行应用,顺利解码aac。

  

时间: 2024-09-29 12:32:38

(原)从mp4,flv文件中解析出h264和aac,送解码器解码失败的相关文章

js中解析json对象:JSON.parse()用于从一个字符串中解析出json对象, JSON.stringify()用于从一个对象解析出字符串。

JSON.parse()用于从一个字符串中解析出json对象. var str = '{"name":"huangxiaojian","age":"23"}' ; JSON.parse(str); // age: "23" name: "huangxiaojian" 2.JSON.stringify()用于从一个对象解析出字符串. var a = {a:1,b:2 }; JSON.str

atitit.mp4&#160;视频文件多媒体格式结构详解

atitit.mp4 视频文件多媒体格式结构详解 1. 一.基本概念1 2. MP4文件概述2 3. mp4是由一个个“box”组成的,2 4. 典型简化mp43 5. Fragments5 6. ref6 7. 具体列表6 8. Ref29 MP4文件格式详解(ISO-14496-12/14) Author:Pirate Leo Email:[email protected] 1. 一.基本概念 1. 文件,由许多Box和FullBox组成. 2. Box,每个Box由Header和Data组

[转]【流媒體】H264—MP4格式及在MP4文件中提取H264的SPS、PPS及码流

[流媒體]H264—MP4格式及在MP4文件中提取H264的SPS.PPS及码流 SkySeraph Apr 1st 2012  Email:[email protected].com 一.MP4格式基本概念 MP4格式对应标准MPEG-4标准(ISO/IEC14496) 二.MP4封装格式核心概念 1  MP4封装格式对应标准为 ISO/IEC 14496-12(信息技术 视听对象编码的第12部分: ISO 基本媒体文件格式/Information technology Coding of a

FLV格式详解

Overview Flash Video(简称FLV),是一种流行的网络格式.目前国内外大部分视频分享网站都是采用的这种格式. File Structure 从整个文件上开看,FLV是由The FLV header 和 The FLV File Body 组成. 1.The FLV header Field Type Comment Signature UI8 Signature byte always 'F' (0x46) Signature UI8 Signature byte always

计算机二级-C语言-程序填空题-190117记录-对文件的处理,复制两个文件,往新文件中写入数据。

//给定程序的功能是,调用函数fun将指定源文件中的内容赋值到指定目标文件中,复制成功时函数返回1,失败时返回0,把复制的内容输出到终端屏幕.主函数中源文件名放在变量sfname中,目标文件名放在变量tfname中. //重难点:对文件的处理.如何判断文件是否达到末尾,如何往文件中写入数据. 1 #include <stdio.h> 2 #include <stdlib.h> 3 int fun(char *source, char *target) 4 { FILE *fs,*f

iis中添加视频播放支持mp4文件、flv文件等

今天在ftp上传了一个mp4的文件,发现播放器播放不了,找个本地文件替换了发现可以正常播放,魅力网络为此在浏览器中直接输入mp4的url发现无法显示网页,于是怀疑是服务器的问题,可能是没添加这个视频的格式支持. 所以进入服务器打开iis,添加MIME类型属性就可以了 这样再访问mp4url就出现了mp4的下载页面 不再是无法打开了

C#实现通过ffmpeg从flv视频文件中截图的方法

本文实例讲述了C#实现通过ffmpeg从flv视频文件中截图的方法.分享给大家供大家参考.具体分析如下: 需要先下载ffmpeg,这是开源的,代码如下所示: 代码如下: using System; using System.Configuration; public class PublicMethod:System.Web.UI.Page { public PublicMethod() { } //文件路径 public static string ffmpegtool = "ffmpeg/f

vue-cli脚手架中webpack配置基础文件详解

一.前言 vue-cli是构建vue单页应用的脚手架,输入一串指定的命令行从而自动生成vue.js+wepack的项目模板.这其中webpack发挥了很大的作用,它使得我们的代码模块化,引入一些插件帮我们完善功能可以将文件打包压缩,图片转base64等.后期对项目的配置使得我们对于脚手架自动生成的代码的理解更为重要,接下来我将基于webpack3.6.0版本结合文档将文件各个击破,纯干料.重点章节点击查看:package.json:config/index.js:webpack.base.con

NS前缀\OC中的注释\访问OC原文件、C原文件中的函数

///////////////////////////////////////// //////////////////////////////////////// NS前缀 NS来自于NeXTStep的一个软件 NeXT Software OC中不支持命名空间(namespace) NS是为了避免命名冲突而给的前缀 看到NS前缀就知道是Cocoa中的系统类的名称 "@"的使用方法 1.@""这个符号表示将C中的字符串转化为OC中的字符串对象 2.@符号 OC中的大