文档章节

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

xiaot99
 xiaot99
发布于 2016/12/27 16:03
字数 868
阅读 80
收藏 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
成都
程序员
ONVIF协议网络摄像机(IPC)客户端程序开发(12):读取音视频流

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

benkaoya
2017/05/19
0
0
使用ffmpeg命令实现本地摄像头的远程访问——非编程纯命令

1 ffserver命令 fserver是一个音频和视频的流式服务器。它通过在启动时读入的配置文件完成配置,不指定时用默认的/etc/ffserver.conf文件。ffserver接受一些或者FFM流作为输入然后通过RTP/R...

zhangyujsj
2015/04/26
0
1
FFmpeg代码导读——基础篇

从事音视频技术开发对FFmpeg都不会感到陌生,通过它可以完成音视频采集、编解码、转码、后处理以及流媒体服务等诸多的功能,可以说涵盖了音视频开发中绝大多数的领域。金山云多媒体SDK团队在...

livevideostack
01/08
0
0
centos上安装ffmpeg

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

水墨如丹青
04/13
0
0
FFmpeg编写一个简单播放器 --开篇

FFMPEG是一个很好的库,可以用来创建视频应用或者生成特定的工具。FFMPEG几乎为你把所有的繁重工作都做了,比如解码、编码、复用和解复用。这使得多媒体应用程序变得容易编写。它是一个简单的...

GuoKai
2012/09/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周四乱弹 —— 毒蛇当辣条

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @ 达尔文:分享花澤香菜/前野智昭/小野大輔/井上喜久子的单曲《ミッション! 健?康?第?イチ》 《ミッション! 健?康?第?イチ》- 花澤香菜/前野智...

小小编辑
今天
4
2
java -jar运行内存设置

java -Xms64m #JVM启动时的初始堆大小 -Xmx128m #最大堆大小 -Xmn64m #年轻代的大小,其余的空间是老年代 -XX:MaxMetaspaceSize=128m # -XX:CompressedClassSpaceSize=6...

李玉长
今天
1
0
Spring | 手把手教你SSM最优雅的整合方式

HEY 本节主要内容为:基于Spring从0到1搭建一个web工程,适合初学者,Java初级开发者。欢迎与我交流。 MODULE 新建一个Maven工程。 不论你是什么工具,选这个就可以了,然后next,直至finis...

冯文议
今天
1
0
RxJS的另外四种实现方式(四)——性能最高的库(续)

接上一篇RxJS的另外四种实现方式(三)——性能最高的库 上一篇文章我展示了这个最高性能库的实现方法。下面我介绍一下这个性能提升的秘密。 首先,为了弄清楚Most库究竟为何如此快,我必须借...

一个灰
今天
1
0
麒麟AI首席科学家现世

8月31日,华为发布了新一代顶级人工智能手机芯片麒麟980,成为全球首款7nm工艺手机芯片,AI方面也实现飞跃,支持人脸识别、物体识别、物体检测、图像分割、智能翻译等。 虽然如今人人都在热议...

问题终结者
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部