{ ffmpeg }

  • 06 解封装流程

    /

    解封装流程

    #include <stdio.h>
    <!--more-->
    #include <libavformat/avformat.h>


    int main(int argc, char **argv)
    {
    //打开网络流。这里如果只需要读取本地媒体文件,不需要用到网络功能,可以不用加上这一句
    //avformat_network_init();

    // set input filename
    const char *default_filename = "believe.mp4";
    char *in_filename = NULL;
    if(argv[1] == NULL) in_filename = default_filename;
    else in_filename = argv[1];
    printf("in_filename = %s\n", in_filename);

    AVFormatContext *ifmt_ctx = NULL; // 输入文件的demux
    int videoindex = -1; // 视频索引
    int audioindex = -1; // 音频索引

    // 打开文件,主要是探测协议类型,如果是网络文件则创建网络链接
    int ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);
    if (ret < 0) //如果打开媒体文件失败,打印失败原因
    {
    char buf[1024] = { 0 };
    av_strerror(ret, buf, sizeof(buf) - 1);
    printf("open %s failed:%s\n", in_filename, buf);
    goto failed;
    }

    // 解析流信息
    ret = avformat_find_stream_info(ifmt_ctx, NULL);
    if (ret < 0) //如果打开媒体文件失败,打印失败原因
    {
    char buf[1024] = { 0 };
    av_strerror(ret, buf, sizeof(buf) - 1);
    printf("avformat_find_stream_info %s failed:%s\n", in_filename, buf);
    goto failed;
    }

    //打开媒体文件成功
    printf_s("\n==== av_dump_format in_filename:%s ===\n", in_filename);
    av_dump_format(ifmt_ctx, 0, in_filename, 0);
    printf_s("\n==== av_dump_format finish =======\n\n");
    // url: 调用avformat_open_input读取到的媒体文件的路径/名字
    printf("media name:%s\n", ifmt_ctx->url);
    // nb_streams: nb_streams媒体流数量
    printf("stream number:%d\n", ifmt_ctx->nb_streams);
    // bit_rate: 媒体文件的码率,单位为bps (kbps=bps/1024)
    printf("media average ratio:%lldkbps\n",(int64_t)(ifmt_ctx->bit_rate/1024));
    // 时长
    int total_seconds, hour, minute, second;
    // duration: 媒体文件时长,单位us
    total_seconds = (ifmt_ctx->duration) / AV_TIME_BASE; // us->s
    hour = total_seconds / 3600;
    minute = (total_seconds % 3600) / 60;
    second = (total_seconds % 60);
    //通过上述运算,可以得到媒体文件的总时长
    printf("total duration: %02d:%02d:%02d\n", hour, minute, second);
    printf("\n");
    /*
    * 老版本通过遍历的方式读取媒体文件视频和音频的信息
    * 新版本的FFmpeg新增加了函数av_find_best_stream,也可以取得同样的效果
    */
    for (uint32_t i = 0; i < ifmt_ctx->nb_streams; i++)
    {
    AVStream *in_stream = ifmt_ctx->streams[i];// 音频流、视频流、字幕流
    //如果是音频流,则打印音频的信息
    if (AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type)
    {
    printf("----- Audio info:\n");
    // index: 每个流成分在ffmpeg解复用分析后都有唯一的index作为标识
    printf("index:%d\n", in_stream->index);
    // sample_rate: 音频编解码器的采样率,单位为Hz
    printf("samplerate:%dHz\n", in_stream->codecpar->sample_rate);
    // codecpar->format: 音频采样格式
    if (AV_SAMPLE_FMT_FLTP == in_stream->codecpar->format)
    {
    printf("sampleformat:AV_SAMPLE_FMT_FLTP\n");
    }
    else if (AV_SAMPLE_FMT_S16P == in_stream->codecpar->format)
    {
    printf("sampleformat:AV_SAMPLE_FMT_S16P\n");
    }
    // channels: 音频信道数目
    printf("channel number:%d\n", in_stream->codecpar->channels);
    // codec_id: 音频压缩编码格式
    if (AV_CODEC_ID_AAC == in_stream->codecpar->codec_id)
    {
    printf("audio codec:AAC\n");
    }
    else if (AV_CODEC_ID_MP3 == in_stream->codecpar->codec_id)
    {
    printf("audio codec:MP3\n");
    }
    else
    {
    printf("audio codec_id:%d\n", in_stream->codecpar->codec_id);
    }
    // 音频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的
    if(in_stream->duration != AV_NOPTS_VALUE) // 如果有效
    {
    // 总时长 = 采样数 *单个采样持续+时间
    int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base); // 分数->double

    printf("audio duration: %02d:%02d:%02d\n",
    duration_audio / 3600, (duration_audio % 3600) / 60, (duration_audio % 60));
    }
    else
    {
    printf("audio duration unknown");
    }

    printf("\n");

    audioindex = i; // 获取音频的索引
    }
    else if (AVMEDIA_TYPE_VIDEO == in_stream->codecpar->codec_type) //如果是视频流,则打印视频的信息
    {
    printf("----- Video info:\n");
    printf("index:%d\n", in_stream->index);
    // avg_frame_rate: 视频帧率,单位为fps,表示每秒出现多少帧
    printf("fps:%lffps\n", av_q2d(in_stream->avg_frame_rate));
    if (AV_CODEC_ID_MPEG4 == in_stream->codecpar->codec_id) //视频压缩编码格式
    {
    printf("video codec:MPEG4\n");
    }
    else if (AV_CODEC_ID_H264 == in_stream->codecpar->codec_id) //视频压缩编码格式
    {
    printf("video codec:H264\n");
    }
    else
    {
    printf("video codec_id:%d\n", in_stream->codecpar->codec_id);
    }
    // 视频帧宽度和帧高度
    printf("width:%d height:%d\n", in_stream->codecpar->width,
    in_stream->codecpar->height);
    //视频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的
    if(in_stream->duration != AV_NOPTS_VALUE)
    {
    int duration_video = (in_stream->duration) * av_q2d(in_stream->time_base);
    printf("video duration: %02d:%02d:%02d\n",
    duration_video / 3600,
    (duration_video % 3600) / 60,
    (duration_video % 60)); //将视频总时长转换为时分秒的格式打印到控制台上
    }
    else
    {
    printf("video duration unknown");
    }

    printf("\n");
    videoindex = i;
    }
    }

    AVPacket *pkt = av_packet_alloc();

    int pkt_count = 0;
    int print_max_count = 10;
    printf("\n-----av_read_frame start\n");
    while (1)
    {
    ret = av_read_frame(ifmt_ctx, pkt);
    if (ret < 0)
    {
    printf("av_read_frame end\n");
    break;
    }

    if(pkt_count++ < print_max_count)
    {
    if (pkt->stream_index == audioindex)
    {
    printf("audio pts: %lld\n", pkt->pts);
    printf("audio dts: %lld\n", pkt->dts);
    printf("audio size: %d\n", pkt->size);
    printf("audio pos: %lld\n", pkt->pos);
    printf("audio duration: %lf\n\n",
    pkt->duration * av_q2d(ifmt_ctx->streams[audioindex]->time_base));
    }
    else if (pkt->stream_index == videoindex)
    {
    printf("video pts: %lld\n", pkt->pts);
    printf("video dts: %lld\n", pkt->dts);
    printf("video size: %d\n", pkt->size);
    printf("video pos: %lld\n", pkt->pos);
    printf("video duration: %lf\n\n",
    pkt->duration * av_q2d(ifmt_ctx->streams[videoindex]->time_base));
    }
    else
    {
    printf("unknown stream_index:\n", pkt->stream_index);
    }
    }

    av_packet_unref(pkt);
    }

    if(pkt)
    av_packet_free(&pkt);
    failed:
    if(ifmt_ctx)
    avformat_close_input(&ifmt_ctx);


    getchar(); //加上这一句,防止程序打印完信息马上退出
    return 0;
    }

  • 00 数据结构查询表

    /

    数据结构查询表

    AVFormatContext

    AVFormatContext 是处理多媒体文件的核心结构体,包含了多媒体文件的封装格式信息。