上一篇对摄像头预览,拍照做了大概的介绍,现在已经可以拿到视频帧了,在加上 RTSP 实现,就是直播的雏形,当然还要加上一些 WEB 管理和手机平台的支援,就是一整套直播软件。
介绍一些基础概念:RTP RTSP RTMP
RTP 实时传输协议,RTMP 以前 flash 用的视频协议,RTSP 目前比较流行的 直播协议
H264 基础概念:SPS 、PSS 、NAl 、NALU、 I帧 P 帧B 帧、fps 、pts 、dts
SPS Sequence Parameter Set 序列参数设置 PPS Picture Parameter Set 图片参数设置
SPS PSS 在容器 mkv 文件中保存时仅存在于开头时不是每一帧都会附加,但是在 RTSP 实时直播时,需要填充到 sdp 传输。
用到的软件和第三方库:ffmpeg live555 VLC
VLC 全平台播放器,win ubuntu mac os android 各个平台都有,功能强大,UI美观,还没有广告。
live555 开源 RTP RTSP 项目
ffmpeg 开源编解码器,多种格式转换,加水印,ffplay 更是全能播放器(就是控制做的不行),解码器需要自行编译加入。
ffplay 在 win 上使用时,需要加一个环境变量,否则没声音 set SDL_AUDIODRIVER=directsound
1,在本机发布一个 ts 流,用 VLC 和 手机浏览器, 进行播放。
ffmpeg -i 1-21.rm -profile:v baseline -level 3.0 -s 640x360 -start_number 0 -hls_time 10 -hls_list_size 0 -f hls 1-12.m3u8
把 ffmpeg 拆分好的文件,复制到 webroot 目录里面,然后使用 VLC 播放,也可以通过 html5 的 video 在手机上播放,手机上浏览器支持 ts 流比较好
2,RTSP 流 使用 live555 发布
http://live555.com/liveMedia/public/ 下载源码,编译安装,不得不说有的时候在 ubuntu 上开发的确比 win 上简单。
下载解压后,执行 ./genMakefiles linux 在 make 会生成 mediaServer/live555MediaServer 运行它,在它的目录放一些文件,这里放的是 mkv 的文件,这里还写了支持的其它类型的文件。
然后使用 ffplay 进行播放。
使用 wireshark 抓包查看数据包
RTSP 文档 https://www.rfc-editor.org/rfc/rfc2326.html 自己对照着看吧,如果完全自己从0开发,这些是需要知道的。
3,H264 ACC 编码
要先找一些原始数据,才能开始编码。直接从 DirectShow 中,的确是可以拿到数据,每次启动什么的还是有点麻烦,所以先生成一些数据,使用 ffmpeg 提取视频为图片
ffmpeg -i 1.mp4 -r 25 -q:v 2 -f image2 image-%5d.jpg
从 1.mp4 中提取了图片,帧率是 25 。
win 平台下载编译好的 lib 比较省心 libffmpeg libjpeg
https://ffmpeg.zeranoe.com/builds/ 下载 dev 、shared 2个,为啥要下载2个,因为这个运行的时候,需要 dll,(注意,里面没有包含 x264)
linux 可以自行编译,需要下载很多库 x264 x265 啥的。
参考例子 ffmpeg-4.1/doc/examples$
编译
gcc encode_video.c -lavcodec -lavutil -o encode_video
gcc muxing.c -lavcodec -lavutil -lswscale -lswresample -lavformat -lm -o muxing
执行 ./encode_video 1.mp4 libx264 ./muxing 2.mp4
使用 ffplay 播放器打开
实际上这个是动的,不过 GIF 录的不好。
H264 中要求是 YUV420P 格式,JPG 默认解码 RGB 也可以解码为 JCS_YCbCr 。YCbCr 和 YUV 几种格式的区别,ffmpeg 中有以下几种:
AV_PIX_FMT_YUV444P
AV_PIX_FMT_YUV422P
AV_PIX_FMT_YUV420P
1 //rgb24 to yun420p 2 sws_ctx = sws_getContext(frame->width, frame->height, AV_PIX_FMT_RGB24, 3 frame->width, frame->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, 4 NULL,NULL,NULL); 5 6 struct dirent **namelist; 7 int n; 8 9 n = scandir(dir_path, &namelist, NULL, alphasort); 10 for(i = 0; i < n; i++) 11 { 12 if(0 != strcmp(".", namelist[i]->d_name) && 0 != strcmp("..", namelist[i]->d_name)) 13 { 14 snprintf(file_image, sizeof(file_image), "%s/%s", dir_path, namelist[i]->d_name); 15 16 printf("file_image:%s ", file_image); 17 read_jpeg(file_image, &video_width, &video_height, &image_buff); 18 19 uint8_t *indata[AV_NUM_DATA_POINTERS] = { 0 }; 20 indata[0] = image_buff; 21 int inlinesize[AV_NUM_DATA_POINTERS] = { 0 }; 22 inlinesize[0] = frame->width * 3; 23 24 ret = sws_scale(sws_ctx, indata, inlinesize, 0, frame->height, frame->data, frame->linesize); 25 26 /* make sure the frame data is writable */ 27 ret = av_frame_make_writable(frame); 28 if (ret < 0) exit(1); 29 30 frame->pts = i; 31 32 /* encode the image */ 33 encode(c, frame, pkt, f); 34 35 free(image_buff); 36 } 37 free(namelist[i]); 38 } 39 free(namelist); 40 sws_freeContext(sws_ctx); 41 42 closedir(dir);
这个是 修改自 encode_video.c 把 上面拆分的 jpg 图片合成 264 编码,编译方式:gcc encode_video_h264.c -lavcodec -lavutil -lswscale -lswresample -lavformat -ljpeg
这里使用读取文件夹内的所有 jpg ,read_jpeg() 是一个用 libjpeg 实现的,得到 jpeg 解码 RGB 数据的方法,但是 编码器需要 YUV420P 所以使用 sws_scale 进行转换。
将转换好的 xin.264 文件复制到 mediaServer 下面,启动 live555MediaServer 用 VLC 播放。
vs2010 编译 live555 下载并解压 live.2020.03.06.tar.gz
方法1,编辑 win32config
TOOLS32 = c:Program FilesDevStudioVc 修改为 vs 的路径 C:Program FilesMicrosoft Visual Studio 10.0VC
LINK_OPTS_0 = $(linkdebug) msvcrt.lib
执行 genWindowsMakefiles
新建一个 vs_build.bat
call "C:Program FilesMicrosoft Visual Studio 10.0VCvcvarsall.bat" cd liveMedia nmake /B -f liveMedia.mak cd ../groupsock nmake /B -f groupsock.mak cd ../UsageEnvironment nmake /B -f UsageEnvironment.mak cd ../BasicUsageEnvironment nmake /B -f BasicUsageEnvironment.mak cd ../testProgs nmake /B -f testProgs.mak cd ../mediaServer nmake /B -f mediaServer.mak
运行这个批处理,完成以后,就会生成 库文件和exe
新建一个空白的 vc++ 工程,复制,生成的头文件和库 和 testOnDemandRTSPServer.cpp 配置好工程,编译生成 exe 放个测试 test.264 运行,能正常播放。
方法2,直接使用 vs2010 新建一个 win32 项目 ,选择 静态库 ,无编译头
添加一些头文件。这里4个 lib 项目,合在一起了,添加一个 NO_OPENSSL 宏 禁用 openssl 。
编译出来了,比那个 方法1强多了,修改调试都方便。
编译 jpegsr6.zip 解压 jconfig.vc 改名为 jconfig.h 新建 vs_build.bat
1 call "C:Program FilesMicrosoft Visual Studio 10.0VCvcvarsall.bat" 2 nmake /f makefile.vc nodebug=1
libjpeg.lib 和一些头文件
重新编序 ffmpeg 并加入 https://www.videolan.org/developers/x264.html
windows 下编译的问题:
1,找不到 dirent.h 解决方法,https://github.com/jacksoja/dirent 添加头文件
2,error C1083: 无法打开包括文件:“inttypes.h”: No such file or directory 解决方法, https://github.com/chemeris/msinttypes 添加头文件 删除 stdint.h
live555 源码添加 live 直播支持,以 h264 为例 H264VideoFileServerMediaSubsession
createNewStreamSource() 这里是生成 source 数据源的,里面使用一个 ByteStreamFileSource 进行构造。
Medium => MediaSource => FramedSource => FramedFileSource => ByteStreamFileSource => File
Medium => MediaSource => FramedSource => FramedFilter => MPEGVideoStreamFramer => H264or5VideoStreamFramer => H264VideoStreamFramer => ByteStreamFileSource
添加为 H264VideoStreamFramer 从 实时流中读取数据的方法。
添加类 class LiveStreamH264Source: public FramedSource 用来提供数据 ,提供数据录入,数据读出,数据缓存。
添加类 H264VideoLiveFramer: public MPEGVideoStreamFramer 用来处理 source 获取 sps pps ,sdp
添加类 class H264VideoLiveMediaSubsession: public OnDemandServerMediaSubsession 用来管理会话
重新 编译运行
新添加的 live h264 已经出来了 ,重新实现 LiveStreamH264Source 中提供,读流,写流的操作方法即可。
视频帧 RGB 转为 H264 在填充流,参考上面的 RGB 编码 h264。
总结:
ffmpeg 实现 RGB 的 h264编码 和 PCM aac 编码
DirectShow 实现 windows 平台下,视频帧图像采集,音频 PCM 录制
送到 live555 通过 RTSP RTP 传输,在理想点就是实现 P2P 以减少服务器压力。
最终成果 gif 动画:
VLC 播放的有点色块,ffplay 的没事。
后记:
为啥图像是倒的?
这说明真的是用 DirectShow 做的,SampleGrabberCallback::BufferCB 采集到的数据对 BMP 亲合力比较强, 而 BMP 是比左下角开始扫的,而且每行做了4整数对齐。
解决方法是吗? 多简单,把摄像头反着装就行了,哈哈。
副产品,基于 SDL2 的查看器,调节正常了