FFMPEG 提取码流的基本信息
原文:http://ticktick.blog.51cto.com/823160/1869849?utm_source=tuicool&utm_medium=referral
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;
}
|
当这两步执行成功后,媒体信息就已经成功保存在了 ffmpeg 相关的结构体成员变量中了,下一步我们看看如何拿到这些信息,为我所用。
(2)利用 ffmpeg 系统函数 dump 码流信息
ffmpeg 提供了一个函数直接帮助你打印出解析到的媒体信息,用法如下:
1
|
av_dump_format(ic, 0, ic->filename, 0);
|
例如,打印 “rtmp://live.hkstv.hk.lxdns.com/live/hks” 的结果如下:
不过,这样打印的信息还不够,我们希望能通过代码取到每一个关键的媒体信息。因此,下面我们看看如何直接从 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 字符串
|
由此可见,封装格式、总时长和总码率可以拿到了。另外,由于 AVStream **streams 还详细记录了每一路流的媒体信息,可以进一步挖一挖,看看它有哪些成员变量。
我们通过 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];
|
拿到了 video_stream 和 audio_stream ,我们就可以把 AVStream 结构体中的信息提取出来了,其关键的成员变量如下:
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; // 平均帧率
|
到这里,我们拿到了平均的帧率,其中,AVCodecContext 详细记录了每一路流的具体的编码信息,我们再进一步挖一挖,看看 AVCodecContext 有哪些成员变量。
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);
}
|
FFMPEG 提取码流的基本信息相关推荐
- python网盘提取码怎么用_Python 一键获取百度网盘提取码
''' 遇到python不懂的问题,可以加Python学习交流群:1004391443一起学习交流,群文件还有零基础入门的学习资料 ''' VERSION = "VERSION 1.0.0& ...
- 获赞23w+在B站一夜爆火,大写的牛B!程序员进阶网盘资源(有链接和提取码)...
这两天逛B站,在一个大佬那领到了一份资源,我看的时候视频刚上传不到24小时,当时已经有23w的点赞,大写的牛B(刚看了下那个视频已经删了),这套资料我当时保存到了网盘内,今天免费分享给大家!资源有多给 ...
- 爬虫图片href是html图片,xpath爬虫实例,爬取图片网站百度盘地址和提取码
某套图网站,套图以封面形式展现在页面,需要依次点击套图,点击广告盘链接,最后到达百度网盘展示页面. 这一过程通过爬虫来实现,收集百度网盘地址和提取码,采用xpath爬虫技术 1.首先分析图片列表页,该 ...
- 一码空传临时网盘源码-带提取码功能
介绍: 一码空传临时网盘源码,无数据库版V2.0,免费授权. 前端采用layui开发框架,后端是原生PHP,没有使用任何的开发框架. 采用了一个无数据库配置读写类,config文件读写代码来自网络. ...
- 记录一次网盘资源不给提取码的经历!另类编程思维,Python破之!
前言: 记录一次网盘资源不给提取码的经历!另类编程思维,Python破之!可能这个标题的意思不是所有人都能够理解,简单说明一下,就是好不容易在网上找资源,然而那个分享网盘的朋友忘记给提取码了..... ...
- python游戏编程入门下载-Python游戏编程入门 中文pdf扫描版|网盘下载内附地址提取码|...
Python是一种解释型.面向对象.动态数据类型的程序设计语言,在游戏开发领域,Python也得到越来越广泛的应用,并由此受到重视. 本书教授用Python开发精彩游戏所需的[]为重要的该你那.本书不 ...
- 使用Fiddler自定义百度云分享提取码
Fiddler是一款经典的Web调试工具.其工作原理是建立本地代理服务器,由于通信时产生的数据包都会通过该代理服务器,所以Fiddler可以用来监视并修改客户端与服务器的连接. 百度云是百度公司推出的 ...
- 【实用技巧】 修改度娘的提取码
摘选自幻想次元,有修改 大家是不是对随机的百度网盘的提取码不满意,想自己定制一个呢? 当你遇到"小v"这样的提取码的时候是不是感觉很神奇(提取码只要是四个字符就行了,汉字占三个字符 ...
- 链接:https://pan.baidu.com/s/1joRvLQ7nbti0Wp4t2CkX5w 提取码:ZMTD
链接:https://pan.baidu.com/s/1joRvLQ7nbti0Wp4t2CkX5w 提取码:ZMTD
最新文章
- 阿里巴巴人工智能实验室“黄”了
- Linux学习:文件属性函数
- 检测到磁盘可能为uefi引导_Win10创意者无法更新提示“磁盘布局不受uefi固件支持”怎么办?...
- 自定义控件_VIewPager显示多个Item
- twisted mysql_在Twisted下用MySQLadbapi获取自增id
- lua语言和python_[动态语言]python和lua中的三元操作符and-or
- DOM中cloneNode的使用之旅
- 2022年,北京将建设22.4公里的轨道交通
- 部署描述符[D:\apache-tomcat-9.0.38\conf\Catalina\localhost\books.xml]时出错
- 图神经网络(CNN)三
- 2023电工杯数学建模A题B题思路模型
- 深入理解生成对抗网络(GAN 基本原理,训练崩溃,训练技巧,DCGAN,CGAN,pix2pix,CycleGAN)
- 手机端网站服务器软件,手机端远程连接服务器的软件
- SNMP协议简单理解
- #2002 无法登录 MySQL 服务器,phpMyAdmin
- 面试专家陈建军出二十一组面试题(转)
- 最全软件著作权申请流程
- 计算机视觉考研电子科技大学,电子科技大学控制科学与工程考研
- DragonEnglish——COCA20000单词+音频+释义+例句及翻译内容聚合
- 深入理解Arrays.sort()