目前在網絡傳輸視頻/音頻流都通常會採用H.264進行編碼,因此嘗試調用FFMPEG API完成Mac錄屏功能,同時編碼爲H.264格式。git
在上一篇文章中,經過調用FFmpeg API完成了Mac平臺下的錄屏功能。在本篇中,對上次的錄屏進行優化,將採集到的視頻流編碼爲H.264格式,同時設定FPS和分辨率。github
由於是對上次錄屏功能的優化,所以處理思路仍然分爲三部分:shell
和上次使用的API對比,本次主要增長了涉及到H.264參數設定和H.264 pts/dts 設定的API:網絡
仍然採用上篇中打開設備的方法:優化
av_find_input_format("avfoundation")
獲取AVInputFormat。avformat_open_input
打開指定的屏幕設備。而後FFmpeg會返回此設備中的數據流,而FFmpeg處理數據流通常都遵循:肯定codec(編碼 or 解碼)->初始化codec上下文參數->打開codec,這三步。 針對輸入設備也就是下面的順序:編碼
avcodec_find_decoder -> avcodec_alloc_context3 -> avcodec_open2
AVInputFormat
會有多個數據流(視頻流/音頻流),因此首先找到須要處理的流:code
codecpar->codec_type == AVMEDIA_TYPE_VIDEO
而後依次調用avcodec_find_decoder
,avcodec_alloc_context3
和avcodec_open2
來初始化codec。orm
最後是將視頻數據編碼爲H.264,並封裝到MP4容器中。因此文件名仍設定爲out.mp4
。視頻
打開輸出設備的方法和打開輸入設備方法相似:get
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編碼格式進行了一些特殊處理,除此以外大體流程徹底一致。