新手學習FFmpeg - 呼叫API完成錄屏並進行H.264編碼
Screen Record H.264
目前在網路傳輸視訊/音訊流都一般會採用H.264進行編碼,所以嘗試呼叫FFMPEG API完成Mac錄屏功能,同時編碼為H.264格式。
在上一篇文章中,通過呼叫FFmpeg API完成了Mac平臺下的錄屏功能。在本篇中,對上次的錄屏進行優化,將採集到的視訊流編碼為H.264格式,同時設定FPS和解析度。
因為是對上次錄屏功能的優化,因此處理思路仍然分為三部分:
- 開啟輸入裝置(預設的螢幕裝置)
- 初始化輸出裝置(mp4檔案)
- 內容轉碼
和上次使用的API對比,本次主要增加了涉及到H.264引數設定和H.264 pts/dts 設定的API:
- avcodec_parameters_from_context
- av_rescale_q
初始化輸入裝置
仍然採用上篇中開啟裝置的方法:
- 通過
av_find_input_format("avfoundation")
獲取AVInputFormat。 - 通過
avformat_open_input
開啟指定的螢幕裝置。
然後FFmpeg會返回此裝置中的資料流,而FFmpeg處理資料流一般都遵循:確定codec(編碼 or 解碼)->初始化codec上下文引數->開啟codec,這三步。 針對輸入裝置也就是下面的順序:
avcodec_find_decoder -> avcodec_alloc_context3 -> avcodec_open2
AVInputFormat
會有多個數據流(視訊流/音訊流),所以首先找到需要處理的流:
codecpar->codec_type == AVMEDIA_TYPE_VIDEO
然後依次呼叫avcodec_find_decoder
,avcodec_alloc_context3
和avcodec_open2
來初始化codec。
初始化輸出裝置
最後是將視訊資料編碼為H.264,並封裝到MP4容器中。所以檔名仍設定為out.mp4
。
開啟輸出裝置的方法和開啟輸入裝置方法類似:
avcodec_find_encoder -> avcodec_alloc_context3 -> avcodec_open2 -> avformat_write_header
最後的avformat_write_header
不是必須的,只有當容器格式要求寫Header時才會呼叫。與上篇中不同的時,明確指定輸出CodecContext的編碼器型別:
outCodecContext->codec_id = AV_CODEC_ID_H264;
outCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;
outCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;
outCodecContext->bit_rate = 400000; // 2500000
outCodecContext->width = 1920;
outCodecContext->height = 1080;
同時H.264對pts和dts有要求,因此需要設定timebase:
outCodecContext->time_base = (AVRational) {1, 25};
轉碼
有了上次的經驗之後,處理轉碼就會稍顯簡單。 處理流程大致為:
while av_read_frame
|
+---> avcodec_send_packet
|
+----> while avcodec_receive_frame
| 對每一資料幀進行解碼
| 通過`sws_scale`進行源幀和目標幀的資料轉換
|
+----> avcodec_send_frame
|
+---> while avcodec_receive_packet
|
|
+--->av_interleaved_write_frame (寫入到輸出裝置)
轉碼最重要的環節就是在avcodec_receive_frame
之後的邏輯。 上面說過H.264對pts有要求,因此這裡需要對每一幀新增pts值。
int64_t now = av_gettime();
const AVRational codecTimebase = outStream->time_base;
oframe->pts = av_rescale_q(now, (AVRational) {1, 1000000}, codecTimebase);
在最後寫入到輸出裝置之前,仍需要修改pts值:
if (opacket->pts != AV_NOPTS_VALUE)
opacket->pts = av_rescale_q(opacket->pts, outCodecContext->time_base, outStream->time_base);
至此就完成了對視訊進行H.264編碼的過程。可以看到和上篇處理過程大致相同,唯一不同的地方就是針對H.264編碼格式進行了一些特殊處理,除此之外大致流程完全一致。
原始碼請點選 https://andy-zhangtao.github.io/ffmpeg-exampl