今天学习解析媒体文件。

写了一个用例,解析MP4文件得到视频帧和音频帧,并分别保存到不同的文件。

照惯例,先学习,再代码。

学习

av_register_all

/*** 初始化 libavformat,并且注册所有的合并器、解析器和协议。* 如果你不调用这个方法,你可以明确地选择你想要程序支持的格式。* 参照 av_register_input_format()* 参照 av_register_output_format()*/
void av_register_all(void);

avformat_open_input

 /*** 打开一个输入流并且读取它的头部数据,这个编解码器不会被打开。* 这个流必须使用avformat_close_input()关闭。* * @参数 ps 指向一个用户提供的AVFormatContext(使用avformat_alloc_context分配的空间)。*         这个参数可以指向NULL,这样,该参数将会在方法内部被分配控件,并且写入ps。**          注意,如果这个参数是用户提供的,方法调用失败后,AVFormatContext会被释放。** @参数 url 被打开媒体流的url。对于媒体文件,它是媒体文件路径。* @参数 fmt 如果不为空,这个参数规定一个特定的输入格式。*           否则,输入格式将被自动检测。* @参数 options ...** @成功返回0,失败返回负数。** @注意,如果你想使用特定的IO,在分配格式上下文空间之前,设置它的pb字段*/
int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);

avformat_find_stream_info

 /* 读取媒体文件的包来获取流信息。这个方法对于没有头部的文件格式也是有用的,例如MPEG。* 对于MPEG-2这种帧模型重复的类型,这个方法也会计算真实帧率。** 该逻辑文件地址不被这个方法更改。* 检查的数据包可能会被缓冲以用于以后处理。* * @参数 ic 媒体文件句柄(格式上下文)* @参数 options ...* @成功返回值大于0* @注意:这个方法不保证打开所有的编解码器,所以非空的options参数将会返回一个完全合理的行为。  ** 为了让用户决定什么样的信息是他们需要的,我们不会浪费时间去获取用户不需要的东西。*/
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);

av_find_best_stream

 /* 在媒体文件中寻找最合适的流结构体(AVStream)。* 如果参数decoder_ret为非空,av_find_best_stream将会流的编解码寻找默认解码器。* 对于那些不能找到解码器的流来说,它会被忽略。* @param ic 媒体文件句柄(格式上下文)* @param type 流类型,视频(AVMEDIA_TYPE_VIDEO),音频(AVMEDIA_TYPE_AUDIO)* @param wanted_stream_nb AVStream在AVFormatContext中streams的索引,调试证明最好填-1。* @param related_stream -1* @param decoder_ret null* @param flags 0* @return 成功返回值非负。*/
int av_find_best_stream(AVFormatContext *ic,enum AVMediaType type,int wanted_stream_nb,int related_stream,AVCodec **decoder_ret,int flags);

avcodec_find_decoder

/* 通过匹配的编解码器ID,找到一个注册的解码器.*/
AVCodec *avcodec_find_decoder(enum AVCodecID id);

avcodec_alloc_context3

 /* 分配一个AVCodecContext,并且设置它的字段为默认值。* 这个结构体应该使用avcodec_free_context()释放*/
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);

avcodec_parameters_to_context

/*根据AVCodecParameters的值填充AVCodecContext。*AVCodecParameters与AVCodecContext相对应的将会被替换,不对应不替换。*/
int avcodec_parameters_to_context(AVCodecContext *codec,const AVCodecParameters *par);

avcodec_open2

 /* 使用提供的AVCodec来初始化AVCodecContext结构体。在使用这个功能之前,AVCodecContext必须通过* avcodec_alloc_context3()分配空间。* 这些方法 avcodec_find_decoder_by_name(),avcodec_find_encoder_by_name()... 提供一个简单的方法来检索到一个编解码器。*    这个方法不是线程安全的*/
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);

av_image_alloc

 /* 通过参数w,h,pix_fmt,align来分配一个图像的空间,参数pointers和linesizes将会被赋值。* pointers必须通过av_freep(&pointers[0])释放。* @param 调整缓冲区大小对齐的值*/
int av_image_alloc(uint8_t *pointers[4], int linesizes[4],int w, int h, enum AVPixelFormat pix_fmt, int align);

av_read_frame

/* 返回流的下一帧到AVPacket中。* 这个方法返回存储在文件中的数据,它并不确定这些有效帧有解码器。* 每次调用这个方法,它将分割文件中的数据到AVPacket里。它不会遗漏* 有效帧之间的数据,来给解码器最大的信息来解码。** 如果返回的AVPacket有效,在不使用时,必须用av_packet_unref释放。* 对于视频,AVPacket包含一帧。对于音频,如果每一帧大小固定已知,那么* AVPacket包含多帧;如果每一帧大小可变,那么AVPacket包含一帧。** 这几个值(pkt->pts, pkt->dts and pkt->duration)总是会被赋值。*/
int av_read_frame(AVFormatContext *s, AVPacket *pkt);

struct AVPacket

 /* 这个结构体存储压缩数据。它通过解析器输出,作为解码器的输入,或者通过编码器输出, 然后传给合成器。* 对于视频,它代表一个压缩帧,对于音频,它可能代表多个压缩帧,解码器允许输出一个空的packet,* 没有压缩数据,仅仅包含次要数据*/
typedef struct AVPacket 

avcodec_send_packet

 /* 将数据包发送给解码器。(可以想象解码内部有个存放数据的容器)*/
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);

avcodec_receive_frame

/* 返回解码过后的一个帧,与avcodec_send_packet配套使用。* avcodec_send_packet传入的AVPacket可能包含多帧,所以avcodec_receive_frame可以调用* 多次,直到失败。*/
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);

源码

解析MP4文件得到视频帧和音频帧,并分别保存到不同的文件。(代码许多地方返回值未校验)

extern "C"
{
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
}int get_format_from_sample_fmt(const char **fmt, enum AVSampleFormat sample_fmt)
{int i;struct sample_fmt_entry {enum AVSampleFormat sample_fmt; const char *fmt_be, *fmt_le;} sample_fmt_entries[] = {{ AV_SAMPLE_FMT_U8,  "u8",    "u8" },{ AV_SAMPLE_FMT_S16, "s16be", "s16le" },{ AV_SAMPLE_FMT_S32, "s32be", "s32le" },{ AV_SAMPLE_FMT_FLT, "f32be", "f32le" },{ AV_SAMPLE_FMT_DBL, "f64be", "f64le" },};*fmt = NULL;for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) {struct sample_fmt_entry *entry = &sample_fmt_entries[i];if (sample_fmt == entry->sample_fmt) {*fmt = AV_NE(entry->fmt_be, entry->fmt_le);return 0;}}fprintf(stderr,"sample format %s is not supported as output format\n",av_get_sample_fmt_name(sample_fmt));return -1;
}void demuxing_decoding(const char* filename)
{av_register_all();puts("注册...");AVFormatContext* formatContext = nullptr;if (0 != avformat_open_input(&formatContext, filename, NULL, NULL)){puts("avformat_open_input失败.");return;}puts("avformat_open_input...");if (avformat_find_stream_info(formatContext, NULL) < 0){puts("avformat_find_stream_info失败.");return;}puts("avformat_find_stream_info...");//视频int video_stream_idx = av_find_best_stream(formatContext, AVMEDIA_TYPE_VIDEO, 0, -1, nullptr, 0);if (video_stream_idx < 0){puts("avformat_find_stream_info AVMEDIA_TYPE_VIDEO失败.");return;}AVStream* video_st = formatContext->streams[video_stream_idx];AVCodec *videCodec = avcodec_find_decoder(video_st->codecpar->codec_id);AVCodecContext* videoContext = avcodec_alloc_context3(videCodec);avcodec_parameters_to_context(videoContext, video_st->codecpar);avcodec_open2(videoContext, videCodec, nullptr);uint8_t *video_dst_data[4] = { NULL };int video_dst_linesize[4];int video_dst_bufsize = av_image_alloc(video_dst_data, video_dst_linesize, videoContext->width, videoContext->height, videoContext->pix_fmt, 1);//音频int audio_stream_idx = av_find_best_stream(formatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);AVStream* audio_st = formatContext->streams[audio_stream_idx];AVCodec* audioCodec = avcodec_find_decoder(audio_st->codecpar->codec_id);AVCodecContext* audioContext = avcodec_alloc_context3(audioCodec);avcodec_parameters_to_context(audioContext, audio_st->codecpar);avcodec_open2(audioContext, audioCodec, nullptr);av_dump_format(formatContext, 0, filename, 0);AVFrame* frame = av_frame_alloc();AVPacket pkt;av_init_packet(&pkt);pkt.data = NULL;pkt.size = 0;FILE *videofile = nullptr;char video_name[128] = { 0 };sprintf_s(video_name, 128, "output\\demuxing_video.%s", videCodec->name);fopen_s(&videofile, video_name, "wb");FILE *audiofile = nullptr;char audio_name[128] = { 0 };sprintf_s(audio_name, 128, "output\\demuxing_audio.%s", audioCodec->name);fopen_s(&audiofile, audio_name, "wb");while (av_read_frame(formatContext, &pkt) >= 0) {if (pkt.stream_index == video_stream_idx){avcodec_send_packet(videoContext, &pkt);while (0 == avcodec_receive_frame(videoContext, frame)){av_image_copy(video_dst_data, video_dst_linesize, (const uint8_t **)frame->data, frame->linesize, videoContext->pix_fmt, videoContext->width, videoContext->height);fwrite(video_dst_data[0], 1, video_dst_bufsize, videofile);puts("写入视频");}}else if (pkt.stream_index == audio_stream_idx){avcodec_send_packet(audioContext, &pkt);while (0 == avcodec_receive_frame(audioContext, frame)){size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample((AVSampleFormat)frame->format);fwrite(frame->extended_data[0], 1, unpadded_linesize, audiofile);puts("写入音频");}}av_packet_unref(&pkt);}printf("Play the output video file with the command:\n""ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n",av_get_pix_fmt_name(videoContext->pix_fmt), videoContext->width, videoContext->height,video_name);enum AVSampleFormat sfmt = audioContext->sample_fmt;int n_channels = audioContext->channels;const char *fmt;if (av_sample_fmt_is_planar(sfmt)) {const char *packed = av_get_sample_fmt_name(sfmt);printf("Warning: the sample format the decoder produced is planar ""(%s). This example will output the first channel only.\n",packed ? packed : "?");sfmt = av_get_packed_sample_fmt(sfmt);n_channels = 1;}if (get_format_from_sample_fmt(&fmt, sfmt) < 0){}printf("Play the output audio file with the command:\n""ffplay -f %s -ac %d -ar %d %s\n",fmt, n_channels, audioContext->sample_rate,audio_name);fclose(videofile);fclose(audiofile);avcodec_free_context(&videoContext);avcodec_free_context(&audioContext);avformat_close_input(&formatContext);av_frame_free(&frame);av_free(video_dst_data[0]);getchar();
}

ffmpeg之demux相关推荐

  1. ffmpeg之demux 解复用

    今天学习解析媒体文件. 写了一个用例,解析MP4文件得到视频帧和音频帧,并分别保存到不同的文件. 照惯例,先学习,再代码. 学习 av_register_all [cpp] view plaincop ...

  2. 【FFMPEG源码分析】ffmpeg中context与AVClass,AVOption之间的关系

    通过前面三篇文章的分析大致了解了ffmpeg中demuxer/decoder模块的内部大致结构和数据处理流程.在阅读源码的过程中经常会看到XXXContext,AVClass xxx_class, A ...

  3. FFmpeg从零开始

    2022.3.28 色彩空间 YUV,是一种颜色编码方法.常使用在各个视频处理组件中. YUV在对照片或视频编码时,考虑到人类的感知能力,允许降低色度的带宽."Y"表示明亮度(Lu ...

  4. FFMpeg example:封装和解封装demux

    前言: 本文是下面blog的笔记并增加了步骤解析和注释.  muxer和demux原理和使用的函数一样,audio,video是一样的,所以4种情况分析一种即可. 最简单的基于FFmpeg的封装格式处 ...

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

    转载请注明出处:https://blog.csdn.net/impingo 项目地址:https://github.com/im-pingo/pingos 解复用讲解 概念 解复用操作 函数调用流程 ...

  6. ffmpeg对mp3媒体数据的demux和部分decode流程 【ffmpeg-3.3.7】

    目录 背景 mp3媒体资源的组成结构 函数调用流程图 ff_id3v2_read_dict id3v2_parse read_ttag mp3数据部分的格式解析 mp3_read_header 背景 ...

  7. MP4文件两种格式AVC1和H264的区别及利用FFMPEG demux为h264码流事项

    原文:http://www.mworkbox.com/wp/work/314.html MP4的视频H264封装有2种格式:h264和avc1,对于这个细节,很容易被忽略.笔者也是在改编LIVE555 ...

  8. (转)MP4文件两种格式AVC1和H264的区别及利用FFMPEG demux为h264码流事项

    出自:http://www.mworkbox.com/wp/work/314.html 2013-05-04 MP4的视频H264封装有2种格式:h264和avc1,对于这个细节,很容易被忽略.笔者也 ...

  9. ffmpeg 基本用法大全

    FFmpeg FFmpeg 基本用法 本课要解决的问题 1.FFmpeg的转码流程是什么? 2.常见的视频格式包含哪些内容吗? 3.如何把这些内容从视频文件中抽取出来? 4.如何从一种格式转换为另一种 ...

最新文章

  1. 何恺明等人新作:效果超ResNet,利用NAS方法设计随机连接网络 | 技术头条
  2. 全国计算机二级vb 无纸化,2013年3月全国计算机等级考试二级VB无纸化上机题题库题干及答案解析(2)...
  3. 极光推送 api ios参数问题
  4. Kubernetes 核心概念 1
  5. jquery给div赋值
  6. jQuery打造智能提示插件
  7. 【微信小程序多人开发的配置流程】
  8. 论文:轨迹路线生成算法
  9. LOJ #2155. 「POI2011 R1」同谋者 Conspiracy(深入性质分析)
  10. php后台登录页,后台登录页面模板源码
  11. go sync.Cond 源码解析
  12. 威联通NAS配置1.18.1Minecraft服务器
  13. Python案例3—计算基础代谢率V_1.0
  14. 淘宝店铺列表部分示例
  15. PostgreSQL 10.0 preview 功能增强 - 更强可靠性, 过去式事务状态可查(杜绝unknown事务)...
  16. Enchanted Keyfinder-查看、提取windows、office的密钥
  17. 深圳正规的现货白银交易平台
  18. 小猿日记 - 程序猿的日常日记(1)
  19. 你看起来很厉害,所以一事无成
  20. PRTG常见问题解答

热门文章

  1. 三维力控接入多比物联网云平台教程
  2. Dropzone组件的使用及注意点(SSM)
  3. 安装KeOps过程中的踩坑记录
  4. 「Adobe国际认证」考证的意义,是能力还是标签?
  5. Office Server 镜像下载及安装
  6. 华中“HackFUN”创客马拉松大赛
  7. 用python爬取伯乐在线的准备
  8. 文本分类算法研究与实现
  9. Mac 关闭Chrome自动更新功能
  10. Keil里找不到芯片的flash编程算法怎么办?