FFmpeg从入门到牛掰(一):解复用(demux)讲解

转载请注明出处:https://blog.csdn.net/impingo
项目地址:https://github.com/im-pingo/pingos

概念

从今天开始我准备开始出一个ffmpeg系列教程,希望通过此系列教程能帮助大家认识ffmpeg和在工作用的运用。

在开始之前,首先要弄清以下几个概念:

  • 容器(Container)

容器就是一种文件格式,比如flv、mkv、mp4等。包含下面5种流以及文件头信息。

  • 流(Stream)

是一种视频数据信息的传输方式,5种流:音频,视频,字幕,附件,数据。

  • 包(Packet)
    在ffmpeg中包代表已经编码好的一个单位的音频或者视频。

  • 帧(Frame)
    在ffmpeg中帧代表一幅静止的图像(yuv数据)或一些数量的音频采样。

  • 编解码器(Codec)

是对视频进行压缩或者解压缩,CODEC =ENCode (编码) +DECode(解码)

  • 复用/解复用(mux/demux)

把不同的流按照某种容器的规则放入容器,这种行为叫做复用(mux)
把不同的流从某种容器中解析出来,这种行为叫做解复用(demux)

为了更好理解以上概念,下面以h264+aac编码的flv文件转码为 h265+mp3编码的mp4文件为例,ffmpeg的处理流程是这样的:

flv文件
flv解复用器
音频包 aac
aac音频解码器
原始音频采样pcm
mp3音频编码器
音频包mp3
视频包 h264
h264视频解码器
原始视频数据yuv
h265视频编码器
视频包 h265
mp4复用器
mp4文件

解复用操作

函数调用流程

最新的ffmpeg API接口不再需要 av_register_all()函数提前注册。
在这里插入图片描述

创建avformat上下文(可选,用avformat_open_input函数也可创建)

AVFormatContext* avformat_alloc_context()

结束时必须使用avformat_free_context()销毁AVFormatContext指针

打开输入文件

int avformat_open_input(AVFormatContext **ps, const char *url, ff_const59 AVInputFormat *fmt, AVDictionary **options);

该函数作用是打开一个输入流(或者文件)并且读取媒体头信息(如音视频编码类型等等)。
任务结束时使用函数 avformat_close_input()关闭。

参数描述
ps指向AVFormatContext的指针,可以用avformat_alloc_context()提前申请,当然也可直接用avformat_open_input函数生成,不论哪种方式,都必须使用avformat_free_context()销毁该指针
url要打开的媒体流地址(或文件路径)
fmt输入类型,如果此参数不为空,则强制设置输入媒体的类型(如flv、mp4等)
options可选的选项,此处参考ffmpeg命令行操作里的一些输入参数,如reorder_queue_size、stimeout、scan_all_pmts 等等。使用av_dict_set()函数设置,使用av_dict_free()释放。

返回值 0 成功,失败则返回 AVERROR 负值

读取流信息

int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)

读取媒体文件的数据包以获取流信息。 这个对于没有header的文件格式(例如MPEG)很有用。 总之使用该函数可以将输入流(文件)中的媒体信息(包括编码信息)解析出来。通过ic->streams[i]访问,streams的个数由ic->nb_streams获取。

读取音视频包

int av_read_frame(AVFormatContext *s, AVPacket *pkt);

将输入文件或输入URL的流内容读取到AVPacket中,关于AVPacket的字段含义,请参考以下代码中的日志输出。

解复用示例代码

#ifdef __cplusplus
extern "C" {
#endif
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#ifdef __cplusplus
}
#endif

static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, const char *tag)
{
    AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;

    printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
           tag,
           av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
           av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
           av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
           pkt->stream_index);
}

int main(int argc, char **argv)
{
    AVFormatContext *ifmt_ctx = NULL;
    AVPacket pkt;
    const char *in_filename;
    int ret, i;
    int stream_index = 0;
    int *stream_mapping = NULL;
    int stream_mapping_size = 0;

    if (argc < 2) {
        printf("usage: %s input\n"
               "API example program to remux a media file with libavformat and libavcodec.\n"
               "\n", argv[0]);
        return 1;
    }

    in_filename  = argv[1];

    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
        fprintf(stderr, "Could not open input file '%s'", in_filename);
        goto end;
    }

    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
        fprintf(stderr, "Failed to retrieve input stream information");
        goto end;
    }

    av_dump_format(ifmt_ctx, 0, in_filename, 0);

    stream_mapping_size = ifmt_ctx->nb_streams;
    stream_mapping = av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping));
    if (!stream_mapping) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    for (i = 0; i < ifmt_ctx->nb_streams; i++) {
        AVStream *in_stream = ifmt_ctx->streams[i];
        AVCodecParameters *in_codecpar = in_stream->codecpar;

        if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
            in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
            in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
            stream_mapping[i] = -1;
            continue;
        }

        stream_mapping[i] = stream_index++;
    }

    while (1) {
        AVStream *in_stream;

        ret = av_read_frame(ifmt_ctx, &pkt);
        if (ret < 0)
            break;

        in_stream  = ifmt_ctx->streams[pkt.stream_index];
        if (pkt.stream_index >= stream_mapping_size ||
            stream_mapping[pkt.stream_index] < 0) {
            av_packet_unref(&pkt);
            continue;
        }

        pkt.stream_index = stream_mapping[pkt.stream_index];
        log_packet(ifmt_ctx, &pkt, "in");

        av_packet_unref(&pkt);
    }

end:

    avformat_close_input(&ifmt_ctx);

    /* close output */
    av_freep(&stream_mapping);

    if (ret < 0 && ret != AVERROR_EOF) {
        fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
        return 1;
    }

    return 0;
}

在这里插入图片描述

QQ交流群:697773082

  • 10
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值