转载自 原文地址:https://blog.csdn.net/impingo/article/details/103675069
- 容器(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的处理流程是这样的:
解复用操作
函数调用流程
最新的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;
}