一、音视频基础
- mux封装:复用,按一定格式组织原音视频流,例如进行时间同步等
demux解封装:解复用,按一定格式解出音视频流 - ES流,原始流,直接从编码器出来的数据流。
PES流,P(packet),ES形成的分组称为PES分组,是用来传递ES的一种数据布局。
TS流:ES形成的分组称为TS分组,是用来传递ES的一种数据布局。可以被任意截断
rtsp流:RTSP(Real Time Streaming Protocol),RFC2326,实时流传输协议,是TCP/IP协议
体系中的一个应用层协议。
rtmp流:Real Time Messaging Protocol(实时消息传输协议),是abobe公司的协议
hls流:苹果流,是Apple的动态码率自适应技术。主要用于PC和Apple终端的音视频服务。
包括一个m3u8索引文件,TS媒体分片文件。 - 服务端:为客户端提供服务,提供数据服务
客户端:为客户提供本地服务
流媒体:采用酒店式传输方式在Internet播放的媒体格式。 - 推模式:当通知消息来之时,把所有样信息都通过参数的形式"推给"观察者
拉模式:当通知消息来之时,通知的函数不带任何相关的信息,而是要观察者主动
去"拉"信息
实时流:Real Time stream实时传输的音视频流
二、FFmpeg基础
- 开源库,支持Windows,Android,IOS等
- 音视频处理
- 开发语言C
- 源码下载:https://ffmpeg.zeranoe.com/builds/win64/static/
可以下载源码,动态库,静态库等 - 基础流程:
input file---demuxer--->encoded data packets---decoder--->decoded frames---encoder--->encoded data packets---muxer--->output file - 为什么用ffmpeg?例如手机获取rtsp视频流保存到本地,要实现以下方案:
1)实现rtsp客户端,接收音视频包
2)解视频包(rtp->h264)
3)解音频包(rtp->PCM(alaw))
4)音频转码(PCM->AAC)
5)重新封装音视频包
而ffmpeg的调用流程:
1)avformat_open_input 打开文件
2)avforamt_find_stream_info 读取文件格式信息
3)av_read_frame
4)avforamt_alloc_output_context2 创建输出上下文
5)av_write_frame 写输出
三、环境搭建
- 下载源码,下载windows源码及动态库
- 新建VS项目,把源码中的include文件夹下面的头文件,lib下面的静态库.lib,以及下载的动态库中的dll库文件拷贝到工程目录下
分别新建include,lib,bin目录存放。 - 项目中引入静态库:
A、添加工程的头文件目录:工程---属性---配置属性---c/c++---常规---附加包含目录:加上头文件存放目录。
B、添加文件引用的lib静态库路径:工程---属性---配置属性---链接器---常规---附加库目录:加上lib文件存放目录。
C 然后添加工程引用的lib文件名:工程---属性---配置属性---链接器---输入---附加依赖项:加上lib文件名 - 写测试代码,例如引入extern "C" {#include "libavcodec/avcodec.h"},main方法调用av_register_all(),运行报错找不到dll动态库,
这时需要把动态库拷贝到工程目录的运行目录,也就是生成.exe的目录。 - 注意,ffmpeg是C语言开发的,而VS工程是cpp语言,所以引入头文件应该使用extern "C"
Demo源码的github地址
四、保存网络流到本地
ffmpeg基本工作流程:
ffmpeg方法流程:
1. avformat_alloc_context();//创建输入上下文 2. avformat_open_input(); //打开输入流 3. avformat_find_stream_info(); //查找音视频信息 4. avforamt_alloc_output_context2(); //创建输出上下文 5. avio_open(); //打开avio 6. avformat_new_stream(); //从输入流创建输出流 avcodec_copy_context(); //拷贝到输出流,根据nb_streams循环 7. avformat_write_header(); //写头信息 8. while(true){ av_init_packet(); //初始化packet,用来存放编码过的数据 av_read_frame();//读取帧数据 av_interleaved_write_frame();//写数据 } 9. avformat_close_input();//关闭输入输出流 avcodec_close();
一些异常处理:
ffmpeg avformat_open_input always returns “Protocol not found” rv=(-1330794744)
,在初始化的时候忘记调用av_register_all();VS引发了异常: 读取访问权限冲突
,这是数组越界报的错误,点击调用堆栈可以跳到出错位置。
关于RTSP流:
RTSP流地址可以在网上查找,应该有很多的,当然也不仅仅限于rtsp,其它形式的流也是可以的,我使用的是:rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov
,是可以使用的,但是过一段时间就不确定了,所以还是要多找找。如果不确定某个网络流是否可用,可以ping一下IP或者网址,也可以使用potPlayer打开,能ping通或者可以播放就证明流地址没问题。
下面附上本章的Demo github地址
五、网络流转发
网络流转发的基本流程与上一章的网络流保存到本地类似,只不过最后输出是网络流。基本流程如下:
转发网络流到本地UDP端口
- 基于四章的工程,只需要修改OpenOutput(outputUrl)的地址即可,代码如下:
//写到udp端口 OpenOutput("udp://127.0.0.1:1234"); //打开输出流
- 还记得刚开始下载的ffmpeg的动态库吗,里面的bin目录下有ffplay.ext文件,这时使用cmd执行
ffplay.exe udp://127.0.0.1:1234
,来测试转发,
但是报了如下错误:
[udp @ 00000173c61ab540] bind failed: Error number -10048 occurred udp://127.0.0.1:1234: I/O error
这是因为OpenOutput方法中的如下代码导致的:
ret = avio_open2(&outputContext->pb, outputUrl.c_str(), AVIO_FLAG_READ_WRITE, nullptr, nullptr);
我们其实只需要写权限即可,改成如下:
ret = avio_open2(&outputContext->pb, outputUrl.c_str(), AVIO_FLAG_WRITE, nullptr, nullptr);
再次重新运行就能播放转发的UDP流了。如下图所示:
红箭头指的窗口即为从UDP端口读取的流数据进行播放。
- 当然也可以使用其它播放器进行测试,我使用PotPlayer测试的,如下:
打开输入地址:播放效果:
转发为RTMP流
需要修改两个地方:
- 修改OpenInput的地址
OpenOutput("rtmp://127.0.0.1:1935/live/stream0"); //打开输出流
- 修改OpenInut方法中avformat_alloc_output_context2中的视频格式为flv
int ret = avformat_alloc_output_context2(&outputContext, nullptr, "flv", outputUrl.c_str());
另外如果要转发rtmp流,需要打开rtmp server,关于rtmp server的详情后面再述,现提供一个现成的可执行文件下载地址,打开crtmpserver.exe后,再使用ffplay.exe或者potplayer等播放器测试即可。
附上本节github源码地址,upd-stream分支是转发为udp的,master是转发为rtmp的。
使用ffmpeg的命令行实现上述功能
其实ffmpeg的命令行即可完成我们所做的保存网络流,网络流转码等功能。
进入下载的动态库的bin目录下cmd执行:
- 保存网络流命令
./ffmpeg.exe -i rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov -vcodec copy -acodec copy -f mpegts C:/Users/bian/Desktop/test2.mp4
命令解释:
-vcodec copy 表示视频格式直接拷贝,不做转码
-acodec copy 表示音频格式直播拷贝,不做转码
-f mpegts 表示输出格式
最后加保存文件路径
2.转发为UDP流
./ffmpeg.exe -i rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov -vcodec copy -acodec copy -f mpegts udp://127.0.0.1:1234
- 转发为rtmp流
注意在转发前也要先运行crtmpserver.exe
./ffmpeg.exe -i rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov -vcodec copy -acodec copy -f flv rtmp://127.0.0.1:1935/live/stream0