文档章节

FFmpeg 获取视频文件中的音视频源数据

xiaot99
 xiaot99
发布于 2016/12/27 16:03
字数 868
阅读 63
收藏 2
点赞 0
评论 0

     

#define AUDIO_AAC_ADTS_LEN  7
#define FILE_PATH_LENGTH    64

int set_acc_adts_header(uint8_t *header, int packet_len)
{
    int profile = 2;  //AAC LC,MediaCodecInfo.CodecProfileLevel.AACObjectLC;
    int freqIdx = 4;  //32K, 见后面注释avpriv_mpeg4audio_sample_rates中32000对应的数组下标,来自ffmpeg源码
    int chanCfg = 2;  //见后面注释channel_configuration,Stero双声道立体声

    /*int avpriv_mpeg4audio_sample_rates[] = {
    96000, 88200, 64000, 48000, 44100, 32000,
    24000, 22050, 16000, 12000, 11025, 8000, 7350
    };
    channel_configuration: 表示声道数chanCfg
    0: Defined in AOT Specifc Config
    1: 1 channel: front-center
    2: 2 channels: front-left, front-right
    3: 3 channels: front-center, front-left, front-right
    4: 4 channels: front-center, front-left, front-right, back-center
    5: 5 channels: front-center, front-left, front-right, back-left, back-right
    6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel
    7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel
    8-15: Reserved
    */

    //Fill in ADTS data
    header[0] = (uint8_t)0xFF;
    header[1] = (uint8_t)0xF1;
    header[2] = (uint8_t)(((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
    header[3] = (uint8_t)(((chanCfg & 3) << 6) + (packet_len >> 11));
    header[4] = (uint8_t)((packet_len & 0x7FF) >> 3);
    header[5] = (uint8_t)(((packet_len & 7) << 5) + 0x1F);
    header[6] = (uint8_t)0xFC;

    return 0;
}


int main()
{
    AVFormatContext*                format_context_p = NULL;	//ffmpeg文件管理器
    AVPacket                        pkt;
    AVCodecContext*                 codec_context_p = NULL;
    AVBitStreamFilterContext*       bit_stream_filter_p = NULL;
    int                             read_ret = 0;
    int                             video_index = -1;
    int                             audio_index = -1;
    int                             err_no;
    int                             ff_ret;
    char                            address[FILE_PATH_LENGTH] = "D://TEST.mp4"
    char                            adts_head[AUDIO_AAC_ADTS_LEN]
    unsigned char*                  begin_p = NULL;



    av_register_all();

    //打开文件
    err_no = avformat_open_input(&format_context_p, address, 0, 0);
    if (err_no < 0) {
        printf("avformat_open_input() error [%d].\n", err_no);
        return -1;
    }

    //找到流信息
    err_no = avformat_find_stream_info(format_context_p, 0);
    if (err_no < 0) {
        printf("avformat_find_stream_info() error [%d].\n", err_no);
        return -1;
    }

    av_dump_format(format_context_p, 0, format_context_p->filename, 0);

    //获取视频索引
    for (unsigned int i = 0; i < format_context_p->nb_streams; i++) {

        if (format_context_p->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_index = i;
            break;
        }
    }

    //获取音频索引
    for (unsigned int i = 0; i < format_context_p->nb_streams; i++) {

        if (format_context_p->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
            audio_index = i;
            break;
        }
    }

    //获取音频编码
    if (audio_index == -1 && video_index == -1) {
        printf("%s\n", "can not find audio or video codec index error.");
        return -1;
    }

    if (video_index >= 0) {
        //mp4中read出来的帧数据并非标准的H264 nal数据,需要filter过滤
        bit_stream_filter_p = av_bitstream_filter_init("h264_mp4toannexb");
        if (!bit_stream_filter_p) {
            printf("%s\n", "av_bitstream_filter_init() error");
            return -1;
        }
    }
    
    //初始化pkt
    av_init_packet(&pkt);
    //读取帧数据
    while(1) {      
        read_ret = av_read_frame(format_context_p, &pkt);    
        if (read_ret == AVERROR_EOF) {
            /* 
             说明:格式为mp4、avi等封装格式可以使用seek接口定位到指定位置
             但如果是一个H264等源码流的文件无法使用,需要重新初始化format_context
             */
             
            //如果起始时间戳可用
            if (format_context_p->streams[video_index]->start_time >= 0) {
                read_ret = av_seek_frame(format_context_p, video_index, format_context_p->streams[video_index]->start_time, 0);
                if (read_ret >= 0) {
                    continue;
                }
            } else {
                break;
            }
        }

        if (read_ret < 0) {
            printf("av_read_frame() error [%d].", read_ret);
            break;
        }
        
        if (pkt.stream_index == video_index) {
            //视频流
            codec_context_p = format_context_p->streams[video_index]->codec;
            /*
             说明:av_bitstream_filter_filter()函数,根据返回值判断资源的回收
             返回值 = 0,没有开辟新的内存空间,pkt.data仍然指向pkt.buf->data
             返回值 > 0, 重新开辟了新的内存空间,pkt.data为指针,pkt.size为长度
             返回值 < 0, 这种情况可能需要放弃掉该帧数据
             */
            ff_ret = av_bitstream_filter_filter(bit_stream_filter_p, codec_context_p, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);
            if (ff_ret >= 0) {
                begin_p = pkt.data;
                rd_len = pkt.size;
            }

            //视频帧数据以及长度,保存文件后可以使用H264VISTA查看帧数据的正确性
            begin_p;
            rd_len;

            //分配了新的pkt->data空间,使用后销毁
            if (ff_ret > 0 && pkt.size > 0){
                av_free(pkt.data);
            }
        } else if (pkt.stream_index == audio_index) {
            //音频流
            /*
             说明:当音频编码格式为AAC时,是缺少ADTS头信息的,
             创建ADTS头
             */          
            if (format_context_p->streams[audio_index]->codec->codec_id == AV_CODEC_ID_AAC) {
                set_acc_adts_header(adts_head, pkt.size + CSS_MEDIA_CHANNEL_MEDIA_AUDIO_AAC_ATDS_LEN);

                //拼装音频数据ADTS+pkt.data
            } else {
                //音频数据,保存文件后使用播放器播放
            }

        }
        av_free_packet(&pkt);
    }

    if (bit_stream_filter_p) {
        av_bitstream_filter_close(bit_stream_filter_p);
    }

    if(format_context_p) {
        avformat_close_input(&format_context_p);
    }

    return 0;
}

 

© 著作权归作者所有

共有 人打赏支持
xiaot99
粉丝 15
博文 72
码字总数 166962
作品 0
成都
程序员
ONVIF协议网络摄像机(IPC)客户端程序开发(12):读取音视频流

1 专栏导读 本专栏第一篇文章「专栏开篇」列出了专栏的完整目录,按目录顺序阅读,有助于你的理解,专栏前面文章讲过的知识点(或代码段),后面文章不会赘述。为了节省篇幅,突出重点,在文...

benkaoya ⋅ 2017/05/19 ⋅ 0

centos上安装ffmpeg

FFmpeg介绍 FFmpeg是一个开源免费跨平台的视频和音频流方案,属于自由软件,采用LGPL或GPL许可证(依据你选择的组件)。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的...

水墨如丹青 ⋅ 04/13 ⋅ 0

FFMPEG命令行处理视频进阶——高阶必读

FFMPEG拥有强大的视频处理能力,可惜的是有很多人不知道如何使用。本文深入介绍如何用编译好ffmpeg.exe程序处理视频,既有常用的简单的处理,也有一些比较少见的高大上的处理,一定能让你受益...

zhangamxqun ⋅ 05/13 ⋅ 0

android端采用FFmpeg进行音频混合与拼接剪切

接触FFmpeg有一段时间了,它是音视频开发的开源库,几乎其他所有播放器、直播平台都基于FFmpeg进行二次开发。本篇文章来总结下采用FFmpeg进行音频处理:音频混合、音频剪切、音频拼接与音频转...

mp624183768 ⋅ 05/13 ⋅ 0

M3U8下载,直播源下载,FLASH下载(一)-ffmpeg安装手册(windows)

前言 FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/...

u_ascend ⋅ 05/23 ⋅ 0

Android平台上裁剪m4a

Android手机上设置铃声的操作是比较灵活的,一般读者听到一首喜欢的歌曲,马上就可以对这首歌曲进行裁剪,裁剪到片段后,再通过系统的接口设置为铃声(电话铃声、闹钟铃声等)。 前提是,播放...

奇哥3 ⋅ 04/28 ⋅ 0

直播,音视频编码器和解码器(EasyDarwin)-Android

使用摄像头采集视频数据,并通过MediaCodec进行H264编码,之后打包成RTSP格式并上传的。 TextuewView也提供了一个setTransform方法,该方法接收一个matrix参数,使用该参数对当前的渲染内容进...

shareus ⋅ 05/18 ⋅ 0

通过FFmpeg解码和OpenGL的YUV转RGB实现Android视频播放

前言 在我的博文 https://blog.csdn.net/ericbar/article/details/80506390 中,我们在Android平台上,实现了通过FFmpeg在native(C/C++)层进行视频解码,并通过OpenGL实现了硬件渲染工作,...

ericbar ⋅ 05/30 ⋅ 0

ckplayer播放器解决MP4文件缓冲结束前无法播放的方法

最近给客户上传mp4的视频,发现不下载完成,不会播放,查了下,视频要等加载完才能播放,而不是边加载边播放这是因为视频的元数据信息不在第一帧所致。元数据是指保存视频属性的一组参数,比...

likeni1314 ⋅ 2017/05/10 ⋅ 0

程序员可以没钱,但不能不「骚」!

点击上方“程序人生”,选择“置顶公众号” 第一时间关注程序猿(媛)身边的故事 作者 痴海 版权归原作者所有,如需转载,请联系原作者。 前言 程序员在人们的映像中是一个高智商、但同时又很...

csdnsevenn ⋅ 05/08 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

高并发之Nginx的限流

首先Nginx的版本号有要求,最低为1.11.5 如果低于这个版本,在Nginx的配置中 upstream web_app { server 到达Ip1:端口 max_conns=10; server 到达Ip2:端口 max_conns=10; } server { listen ...

算法之名 ⋅ 今天 ⋅ 0

Spring | IOC AOP 注解 简单使用

写在前面的话 很久没更新笔记了,有人会抱怨:小冯啊,你是不是在偷懒啊,没有学习了。老哥,真的冤枉:我觉得我自己很菜,还在努力学习呢,正在学习Vue.js做管理系统呢。即便这样,我还是不...

Wenyi_Feng ⋅ 今天 ⋅ 0

博客迁移到 https://www.jianshu.com/u/aa501451a235

博客迁移到 https://www.jianshu.com/u/aa501451a235 本博客不再更新

为为02 ⋅ 今天 ⋅ 0

win10怎么彻底关闭自动更新

win10自带的更新每天都很多,每一次下载都要占用大量网络,而且安装要等得时间也蛮久的。 工具/原料 Win10 方法/步骤 单击左下角开始菜单点击设置图标进入设置界面 在设置窗口中输入“服务”...

阿K1225 ⋅ 今天 ⋅ 0

Elasticsearch 6.3.0 SQL功能使用案例分享

The best elasticsearch highlevel java rest api-----bboss Elasticsearch 6.3.0 官方新推出的SQL检索插件非常不错,本文一个实际案例来介绍其使用方法。 1.代码中的sql检索 @Testpu...

bboss ⋅ 今天 ⋅ 0

informix数据库在linux中的安装以及用java/c/c++访问

一、安装前准备 安装JDK(略) 到IBM官网上下载informix软件:iif.12.10.FC9DE.linux-x86_64.tar放在某个大家都可以访问的目录比如:/mypkg,并解压到该目录下。 我也放到了百度云和天翼云上...

wangxuwei ⋅ 今天 ⋅ 0

PHP语言系统ZBLOG或许无法重现月光博客的闪耀历史[图]

最近在写博客,希望通过自己努力打造一个优秀的教育类主题博客,名动江湖,但是问题来了,现在写博客还有前途吗?面对强大的自媒体站点围剿,还有信心和可能型吗? 至于程序部分,我选择了P...

原创小博客 ⋅ 今天 ⋅ 0

IntelliJ IDEA 2018.1新特性

工欲善其事必先利其器,如果有一款IDE可以让你更高效地专注于开发以及源码阅读,为什么不试一试? 本文转载自:netty技术内幕 3月27日,jetbrains正式发布期待已久的IntelliJ IDEA 2018.1,再...

Romane ⋅ 今天 ⋅ 0

浅谈设计模式之工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻...

佛系程序猿灬 ⋅ 今天 ⋅ 0

Dockerfile基础命令总结

FROM 指定使用的基础base image FROM scratch # 制作base image ,不使用任何基础imageFROM centos # 使用base imageFROM ubuntu:14.04 尽量使用官方的base image,为了安全 LABEL 描述作...

ExtreU ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部