AVFormatContext封装层:理论与实战(一)https://developer.aliyun.com/article/1473934
4、示例源码 2
使用新版本的 FFmpeg 新增加的函数 av_find_best_stream()
查找给定媒体文件中最佳的流(音频流或视频流)
#include <stdio.h> extern "C" { #include <libavformat/avformat.h> } int main(int argc, char **argv) { // 1. 打开文件 const char *ifilename = "./debug/test.mp4"; printf("in_filename = %s\n", ifilename); avformat_network_init(); // AVFormatContext是描述一个媒体文件或媒体流的构成和基本信息的结构体 AVFormatContext *ifmt_ctx = NULL; // 输入文件的demux // 打开文件,主要是探测协议类型,如果是网络文件则创建网络链接 int ret = avformat_open_input(&ifmt_ctx, ifilename, NULL, NULL); if (ret < 0) { char buf[1024] = {0}; av_strerror(ret, buf, sizeof (buf) - 1); printf("open %s failed: %s\n", ifilename, buf); return -1; } // 2. 读取码流信息 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", ifilename, buf); avformat_close_input(&ifmt_ctx); return -1; } // 3.打印总体信息 printf_s("\n==== av_dump_format in_filename:%s ===\n", ifilename); av_dump_format(ifmt_ctx, 0, ifilename, 0); printf_s("\n==== av_dump_format finish =======\n\n"); printf("media name:%s\n", ifmt_ctx->url); printf("stream number:%d\n", ifmt_ctx->nb_streams); // nb_streams媒体流数量 printf("media average ratio:%lldkbps\n",(int64_t)(ifmt_ctx->bit_rate/1024)); // 媒体文件的码率,单位为bps/1000=Kbps // duration: 媒体文件时长,单位微妙 int total_seconds = (ifmt_ctx->duration) / AV_TIME_BASE; // 1000us = 1ms, 1000ms = 1秒 printf("audio duration: %02d:%02d:%02d\n", total_seconds / 3600, (total_seconds % 3600) / 60, (total_seconds % 60)); printf("\n"); // 4.读取码流信息 // 音频 int audioindex = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); if (audioindex < 0) { printf("av_find_best_stream %s eror.", av_get_media_type_string(AVMEDIA_TYPE_AUDIO)); return -1; } AVStream *audio_stream = ifmt_ctx->streams[audioindex]; printf("----- Audio info:\n"); printf("index: %d\n", audio_stream->index); // 序列号 printf("samplarate: %d Hz\n", audio_stream->codecpar->sample_rate); // 采样率 printf("sampleformat: %d\n", audio_stream->codecpar->format); // 采样格式 AV_SAMPLE_FMT_FLTP:8 printf("audio codec: %d\n", audio_stream->codecpar->codec_id); // 编码格式 AV_CODEC_ID_MP3:86017 AV_CODEC_ID_AAC:86018 if (audio_stream->duration != AV_NOPTS_VALUE) { int audio_duration = audio_stream->duration * av_q2d(audio_stream->time_base); printf("audio duration: %02d:%02d:%02d\n", audio_duration / 3600, (audio_duration % 3600) / 60, (audio_duration % 60)); } // 视频 int videoindex = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); if (videoindex < 0) { printf("av_find_best_stream %s eror.", av_get_media_type_string(AVMEDIA_TYPE_VIDEO)); return -1; } AVStream *video_stream = ifmt_ctx->streams[videoindex]; printf("----- Video info:\n"); printf("index: %d\n", video_stream->index); // 序列号 printf("fps: %lf\n", av_q2d(video_stream->avg_frame_rate)); // 帧率 printf("width: %d, height:%d \n", video_stream->codecpar->width, video_stream->codecpar->height); printf("video codec: %d\n", video_stream->codecpar->codec_id); // 编码格式 AV_CODEC_ID_H264: 27 if (video_stream->duration != AV_NOPTS_VALUE) { int video_duration = video_stream->duration * av_q2d(video_stream->time_base); printf("audio duration: %02d:%02d:%02d\n", video_duration / 3600, (video_duration % 3600) / 60, (video_duration % 60)); } // 5.提取码流 AVPacket *pkt = av_packet_alloc(); int pkt_count = 0; int print_max_count = 100; 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); } // 6.结束 if(pkt) av_packet_free(&pkt); if(ifmt_ctx) avformat_close_input(&ifmt_ctx); avformat_network_deinit(); return 0; }
5、运行结果 2
in_filename = ./debug/test.mp4 ==== av_dump_format in_filename:./debug/test.mp4 === ==== av_dump_format finish ======= media name:./debug/test.mp4 stream number:2 media average ratio:1403kbps audio duration: 00:01:57 ----- Audio info: index: 1 samplarate: 48000 Hz sampleformat: 8 audio codec: 86018 audio duration: 00:01:57 ----- Video info: index: 0 fps: 25.000000 width: 1280, height:720 video codec: 27 audio duration: 00:01:57 -----av_read_frame start audio pts: 0 audio dts: 0 audio size: 967 audio pos: 48 audio duration: 0.021333 video pts: 0 video dts: 0 video size: 105222 video pos: 1015 video duration: 0.040000 .... audio pts: 65536 audio dts: 65536 audio size: 949 audio pos: 346354 audio duration: 0.021333 video pts: 17408 video dts: 17408 video size: 6906 video pos: 347303 video duration: 0.040000 av_read_frame end Input #0, mov,mp4,m4a,3gp,3g2,mj2, from './debug/test.mp4': Metadata: major_brand : isom minor_version : 512 compatible_brands: isomiso2avc1mp41 creation_time : 1970-01-01T00:00:00.000000Z encoder : Lavf53.24.2 Duration: 00:01:57.31, start: 0.000000, bitrate: 1436 kb/s Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1048 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default) Metadata: creation_time : 1970-01-01T00:00:00.000000Z handler_name : VideoHandler Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, 5.1, fltp, 383 kb/s (default) Metadata: creation_time : 1970-01-01T00:00:00.000000Z handler_name : SoundHandler
四、 实战 2:转封装
1、原理讲解
从一种视频容器转成另一种视频容器。
所谓的封装格式转换,就是在 AVI,FLV,MKV,MP4 这些格式之间转换(对应 .avi
,.flv
,.mkv
,.mp4
文件)。需要注意的是,本程序并不进行视音频的编码和解码工作。而是直接将视音频压缩码流从一种封装格式文件中获取出来然后打包成另外一种封装格式的文件。
传统的转码程序工作原理如下图所示:
上图例举了一个举例:FLV(视频:H.264,音频:AAC)转码为 AVI(视频:MPEG2,音频 MP3)的例子。可见视频转码的过程通俗地讲相当于把视频和音频重新“录” 了一遍。
本程序的工作原理如下图所示:
由图可见,本程序并不进行视频和音频的编解码工作,因此本程序和普通的转码软件相比,有以下两个特点:
- 处理速度极快。视音频编解码算法十分复杂,占据了转码的绝大部分时间。因为不需要进行视音频的编码和解码,所以节约了大量的时间。
- 视音频质量无损。
- 因为不需要进行视音频的编码和解码, 所以不会有视音频的压缩损伤。
程序流程图如下图:
AVFormatContext封装层:理论与实战(三)https://developer.aliyun.com/article/1473936