上一篇對攝像頭預覽,拍照做了大概的介紹,現在已經可以拿到視頻幀了,在加上 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\n", 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 Files\DevStudio\Vc 修改為 vs 的路徑 C:\Program Files\Microsoft Visual Studio 10.0\VC
LINK_OPTS_0 = $(linkdebug) msvcrt.lib
執行 genWindowsMakefiles
新建一個 vs_build.bat
call "C:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.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 Files\Microsoft Visual Studio 10.0\VC\vcvarsall.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 的查看器,調節正常了