文档章节

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

xiaot99
 xiaot99
发布于 2016/12/27 16:03
字数 868
阅读 90
收藏 2

     

#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
粉丝 16
博文 73
码字总数 166962
作品 0
成都
程序员
私信 提问
FFmpeg音视频核心技术精讲与实战(目前完整)

第1章 课程导学与准备工作 全民娱乐时代,需要音视频等多媒体产品层出不穷,但会处理音视频数据的工程师却极度匮乏,进入音视频开发领域正当时,这门课程就是为这样的你而生。来吧!加入我们...

weixin_43744894
12/05
0
0
FFmpeg音视频核心技术精讲与实战目前最新

第1章 课程导学与准备工作 全民娱乐时代,需要音视频等多媒体产品层出不穷,但会处理音视频数据的工程师却极度匮乏,进入音视频开发领域正当时,这门课程就是为这样的你而生。来吧!加入我们...

wuzhangchao
11/27
0
0
FFmpeg音视频核心技术精讲与实战完整版

第1章 课程导学与准备工作 全民娱乐时代,需要音视频等多媒体产品层出不穷,但会处理音视频数据的工程师却极度匮乏,进入音视频开发领域正当时,这门课程就是为这样的你而生。来吧!加入我们...

安若森
11/26
0
0
FFmpeg音视频核心技术精讲与实战(目前最全)

第1章 课程导学与准备工作 全民娱乐时代,需要音视频等多媒体产品层出不穷,但会处理音视频数据的工程师却极度匮乏,进入音视频开发领域正当时,这门课程就是为这样的你而生。来吧!加入我们...

weixin_43745537
12/04
0
0
ONVIF协议网络摄像机(IPC)客户端程序开发(12):读取音视频流

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

benkaoya
2017/05/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Ubuntu18.04 安装MySQL

1.安装MySQL sudo apt-get install mysql-server 2.配置MySQL sudo mysql_secure_installation 3.设置MySQL非root用户 设置原因:配置过程为系统root权限,在构建MySQL连接时出现错误:ERROR...

AI_SKI
今天
2
0
3.6 rc脚本(start方法) 3.7 rc脚本(stop和status方法) 3.8 rc脚本(以daemon方式启动)

3.6-3.7 rc脚本(start、stop和status方法) #!/usr/bin/env python# -*- coding: utf-8 -*-# [@Version](https://my.oschina.net/u/931210) : python 2.7# [@Time](https://my.oschina.......

隐匿的蚂蚁
今天
3
0
Cnn学习相关博客

CNN卷积神经网络原理讲解+图片识别应用(附源码) 笨方法学习CNN图像识别系列 深度学习图像识别项目(中):Keras和卷积神经网络(CNN) 卷积神经网络模型部署到移动设备 使用CNN神经网络进行...

-九天-
昨天
4
0
flutter 底部输入框 聊天输入框 Flexible

想在页面底部放个输入框,结果键盘一直遮住了,原来是布局问题 Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("评论"), ...

大灰狼wow
昨天
4
0
Kernel I2C子系统

备注:所有图片来源于网络 1,I2C协议: 物理拓扑: I2C总线由两根信号线组成,一条是时钟信号线SCL,一条是数据信号线SDA。一条I2C总线可以接多个设备,每个设备都接入I2C总线的SCL和SDA。I...

yepanl
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部