FFMPEG Tips (2) 如何提取码流的基本信息
2016-12-13 00:00
375 查看
本文是我的《FFMPEG Tips》系列的第二篇文章,上篇文章《FFMPEG Tips (1) 如何打印日志》主要分享了如何利用 ffmpeg 库打印日志,而本文则主要分享一下如何利用 ffmpeg 库拿到码流的一些基本信息。
1. 码流中的哪些信息值得关注 ?
[ ] 是否包含:音频、视频
[ ] 码流的封装格式
[ ] 视频的编码格式
[ ] 音频的编码格式
[ ] 视频的分辨率、帧率、码率
[ ] 音频的采样率、位宽、通道数
[ ] 码流的总时长
[ ] 其他 Metadata 信息,如作者、日期等
2. 为什么需要拿到这些信息 ?
[ ] 码流的封装格式 -> 解封装
[ ] 音频、视频的编码格式 -> 初始化解码器
[ ] 视频的分辨率、帧率、码率 -> 视频的渲染
[ ] 音频的采样率、位宽、通道数 -> 初始化音频播放器
[ ] 码流的总时长 -> 展示、拖动
[ ] 其他 Metadata 信息 -> 展示
3. 这些关键信息都藏在哪 ?
这些关键的媒体信息,被称作 “metadata”,常常记录在整个码流的开头或者结尾处,例如:wav 格式主要由 wav header 头来记录音频的采样率、通道数、位宽等关键信息;mp4 格式,则存放在 moov box 结构中;而 FLV 格式则记录在 onMetaData 中等等。
我们可以看看 FLV 格式的 onMetaData 记录的信息包含有哪些内容:
当然,并不是所有的码流都能简单地通过 "metadata" 解析出这些媒体信息,有些码流还需要通过试读、解码等一系列复杂的操作判断之后,才能准确地判断真实的媒体信息,在 ffmpeg 中,函数 avformat_find_stream_info 就是干这事的。
4. 如何从 ffmpeg 取出这些信息 ?
(1)首先打开码流,并解析“metadata”
播放器要完成的第一件事,就是 “打开码流”,然后再“ 解析码流信息”,在 ffmpeg 中,这两步任务主要通过 `avformat_open_input` 和 `avformat_find_stream_info` 函数来完成,前者负责服务器的连接和码流头部信息的拉取,后者则主要负责媒体信息的探测和分析工作,这两步的示例代码如下:
当这两步执行成功后,媒体信息就已经成功保存在了 ffmpeg 相关的结构体成员变量中了,下一步我们看看如何拿到这些信息,为我所用。
(2)利用 ffmpeg 系统函数 dump 码流信息
ffmpeg 提供了一个函数直接帮助你打印出解析到的媒体信息,用法如下:
例如,打印 “rtmp://live.hkstv.hk.lxdns.com/live/hks” 的结果如下:
不过,这样打印的信息还不够,我们希望能通过代码取到每一个关键的媒体信息。因此,下面我们看看如何直接从 AVFormatContext 上下文结构体中提取这些信息。
(3)手动从 ffmpeg 的上下文结构体中提取
首先,我们看看 AVFormatContext 变量有哪些跟媒体信息有关的成员变量:
由此可见,封装格式、总时长和总码率可以拿到了。另外,由于 AVStream **streams 还详细记录了每一路流的媒体信息,可以进一步挖一挖,看看它有哪些成员变量。
我们通过 av_find_best_stream 函数来取出指向特定指定路数的 AVStream 对象,比如视频流的 AVStream 和 音频流的 AVStream 对象分别通过如下方法来取到:
拿到了 video_stream 和 audio_stream ,我们就可以把 AVStream 结构体中的信息提取出来了,其关键的成员变量如下:
到这里,我们拿到了平均的帧率,其中,AVCodecContext 详细记录了每一路流的具体的编码信息,我们再进一步挖一挖,看看 AVCodecContext 有哪些成员变量。
原来我们最关心的编码类型、图片的宽高、音频的参数藏在这里了!经过层层解析后,我们想要的媒体信息,基本上在这些结构体变量中都找到了。
5. 代码示例
我们可以尝试手动把我们找到的媒体信息都打印出来看看,代码示例如下(你也可以到我的 Github 查看源代码: https://github.com/Jhuster/clib):
6.
免费学习更多精品课程,登录乐搏学院官网http://www.learnbo.com/
或关注我们的官方微博微信,还有更多惊喜哦~
本文出自 “Jhuster的专栏” 博客,请务必保留此出处http://ticktick.blog.51cto.com/823160/1869849
1. 码流中的哪些信息值得关注 ?
[ ] 是否包含:音频、视频
[ ] 码流的封装格式
[ ] 视频的编码格式
[ ] 音频的编码格式
[ ] 视频的分辨率、帧率、码率
[ ] 音频的采样率、位宽、通道数
[ ] 码流的总时长
[ ] 其他 Metadata 信息,如作者、日期等
2. 为什么需要拿到这些信息 ?
[ ] 码流的封装格式 -> 解封装
[ ] 音频、视频的编码格式 -> 初始化解码器
[ ] 视频的分辨率、帧率、码率 -> 视频的渲染
[ ] 音频的采样率、位宽、通道数 -> 初始化音频播放器
[ ] 码流的总时长 -> 展示、拖动
[ ] 其他 Metadata 信息 -> 展示
3. 这些关键信息都藏在哪 ?
这些关键的媒体信息,被称作 “metadata”,常常记录在整个码流的开头或者结尾处,例如:wav 格式主要由 wav header 头来记录音频的采样率、通道数、位宽等关键信息;mp4 格式,则存放在 moov box 结构中;而 FLV 格式则记录在 onMetaData 中等等。
我们可以看看 FLV 格式的 onMetaData 记录的信息包含有哪些内容:
当然,并不是所有的码流都能简单地通过 "metadata" 解析出这些媒体信息,有些码流还需要通过试读、解码等一系列复杂的操作判断之后,才能准确地判断真实的媒体信息,在 ffmpeg 中,函数 avformat_find_stream_info 就是干这事的。
4. 如何从 ffmpeg 取出这些信息 ?
(1)首先打开码流,并解析“metadata”
播放器要完成的第一件事,就是 “打开码流”,然后再“ 解析码流信息”,在 ffmpeg 中,这两步任务主要通过 `avformat_open_input` 和 `avformat_find_stream_info` 函数来完成,前者负责服务器的连接和码流头部信息的拉取,后者则主要负责媒体信息的探测和分析工作,这两步的示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 | AVFormatContext *ic = avformat_alloc_context(); if (avformat_open_input(&ic, url, NULL, NULL) < 0) { LOGE( "could not open source %s" , url); return -1; } if (avformat_find_stream_info(ic, NULL) < 0) { LOGE( "could not find stream information" ); return -1; } |
(2)利用 ffmpeg 系统函数 dump 码流信息
ffmpeg 提供了一个函数直接帮助你打印出解析到的媒体信息,用法如下:
1 | av_dump_format(ic, 0, ic->filename, 0); |
不过,这样打印的信息还不够,我们希望能通过代码取到每一个关键的媒体信息。因此,下面我们看看如何直接从 AVFormatContext 上下文结构体中提取这些信息。
(3)手动从 ffmpeg 的上下文结构体中提取
首先,我们看看 AVFormatContext 变量有哪些跟媒体信息有关的成员变量:
1 2 3 4 5 6 7 | - struct AVInputFormat *iformat; // 记录了封装格式信息 -unsigned int nb_streams; // 记录了该 URL 中包含有几路流 -AVStream **streams; // 一个结构体数组,每个对象记录了一路流的详细信息 -int64_t start_time; // 第一帧的时间戳 -int64_t duration; // 码流的总时长 - int bit_rate; // 码流的总码率,bps -AVDictionary *metadata; // 一些文件信息头,key/value 字符串 |
我们通过 av_find_best_stream 函数来取出指向特定指定路数的 AVStream 对象,比如视频流的 AVStream 和 音频流的 AVStream 对象分别通过如下方法来取到:
1 2 3 4 5 | int video_stream_idx = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); AVStream video_stream = ic->streams[video_stream_idx]; int audio_stream_idx = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); AVStream audio_stream = ic->streams[audio_stream_idx]; |
1 2 3 4 5 6 | -AVCodecContext *codec; // 记录了该码流的编码信息 -int64_t start_time; // 第一帧的时间戳 -int64_t duration; // 该码流的时长 -int64_t nb_frames; // 该码流的总帧数 -AVDictionary *metadata; // 一些文件信息头,key/value 字符串 -AVRational avg_frame_rate; // 平均帧率 |
1 2 3 4 5 6 7 8 9 10 11 12 13 | - const struct AVCodec *codec; // 编码的详细信息 - enum AVCodecID codec_id; // 编码类型 - int bit_rate; // 平均码率 /* video only */ - int width, height; // 图像的宽高尺寸,码流中不一定存在该信息,会由解码后覆盖 - enum AVPixelFormat pix_fmt; // 原始图像的格式,码流中不一定存在该信息,会由解码后覆盖 /* audio only */ - int sample_rate; // 音频的采样率 - int channels; // 音频的通道数 - enum AVSampleFormat sample_fmt; // 音频的格式,位宽 - int frame_size; // 每个音频帧的 sample 个数 |
5. 代码示例
我们可以尝试手动把我们找到的媒体信息都打印出来看看,代码示例如下(你也可以到我的 Github 查看源代码: https://github.com/Jhuster/clib):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #include <libavutil/log.h> #define LOGD(format, ...) av_log(NULL, AV_LOG_DEBUG, format, ##__VA_ARGS__); int ff_dump_stream_info( char * url) { AVFormatContext *ic = avformat_alloc_context(); if (avformat_open_input(&ic, url, NULL, NULL) < 0) { LOGD( "could not open source %s" , url); return -1; } if (avformat_find_stream_info(ic, NULL) < 0) { LOGD( "could not find stream information" ); return -1; } LOGD( "----------dumping stream info ----------" ); LOGD( "input format: %s" , ic->iformat->name); LOGD( "nb_streams: %d" , ic->nb_streams); int64_t start_time = ic->start_time / AV_TIME_BASE; LOGD( "start_time: %lld" , start_time); int64_t duration = ic->duration / AV_TIME_BASE; LOGD( "duration: %lld s" , duration); int video_stream_idx = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); if (video_stream_idx >= 0) { AVStream *video_stream = ic->streams[video_stream_idx]; LOGD( "video nb_frames: %lld" , video_stream->nb_frames); LOGD( "video codec_id: %d" , video_stream->codec->codec_id); LOGD( "video codec_name: %s" , avcodec_get_name(video_stream->codec->codec_id)); LOGD( "video width x height: %d x %d" , video_stream->codec->width, video_stream->codec->height); LOGD( "video pix_fmt: %d" , video_stream->codec->pix_fmt); LOGD( "video bitrate %lld kb/s" , (int64_t) video_stream->codec->bit_rate / 1000); LOGD( "video avg_frame_rate: %d fps" , video_stream->avg_frame_rate.num/video_stream->avg_frame_rate.den); } int audio_stream_idx = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); if (audio_stream_idx >= 0) { AVStream *audio_stream = ic->streams[audio_stream_idx]; LOGD( "audio codec_id: %d" , audio_stream->codec->codec_id); LOGD( "audio codec_name: %s" , avcodec_get_name(audio_stream->codec->codec_id)); LOGD( "audio sample_rate: %d" , audio_stream->codec->sample_rate); LOGD( "audio channels: %d" , audio_stream->codec->channels); LOGD( "audio sample_fmt: %d" , audio_stream->codec->sample_fmt); LOGD( "audio frame_size: %d" , audio_stream->codec->frame_size); LOGD( "audio nb_frames: %lld" , audio_stream->nb_frames); LOGD( "audio bitrate %lld kb/s" , (int64_t) audio_stream->codec->bit_rate / 1000); } LOGD( "----------dumping stream info ----------" ); avformat_close_input(&ic); } |
免费学习更多精品课程,登录乐搏学院官网http://www.learnbo.com/
或关注我们的官方微博微信,还有更多惊喜哦~
本文出自 “Jhuster的专栏” 博客,请务必保留此出处http://ticktick.blog.51cto.com/823160/1869849
相关文章推荐
- FFMPEG Tips (2) 如何提取码流的基本信息
- 如何提取码流的基本信息
- FFMPEG Tips (2) 如何提取码流的基本信息
- FFMPEG 提取码流的基本信息
- FFMPEG Tips (3) 如何读取每一帧的信息
- 如何在EXCEL中提取身份证号码里的出生年月日、性别等,相关信息
- 如何防止页面中的敏感信息被提取
- 如何有效合并两个文件:一个是1亿条的用户基本信息,另一个是用户每天看电影连续剧等的记录,5000万条。其中内存只有1G。
- FFMPEG Tips (1) 如何打印日志
- 如何有效合并两个文件:一个是1亿条的用户基本信息,另一个是用户每天看电影连续剧等的记录,5000万条。其中内存只有1G。
- GDAL库——读取图像并提取基本信息
- 教你如何从wireshark中的RTSP流媒体提取H.264码流数据
- Android基本数据信息提取
- 关于ffmpeg如何提取视频的关键帧的问题
- EXCEL中如何提取身份证出生日期和性别信息以及检验身份证号码的正确性
- 用ffmpeg提取mp4的h264码流写文件花屏
- 18 如何使用go来采集windows的基本硬件信息后发送到CMDB的服务器上
- 关于ffmpeg如何提取视频的关键帧的问题
- 微信公众号如何处理用户基本信息
- 如何获取ffmpeg返回的错误信息