ffmpeg視頻錄制


前面講了一下ffmpeg的一些基本概念(ffmpeg中的基本概念),這里說一下如何使用ffmpeg進行視頻錄制。

 

錄制視頻的基本步驟是:

1. 初始化ffmpeg的基本對象,並將這些對象關聯起來,然后打開文件並寫入文件頭。

2. 編碼視頻,並將編碼后數據存寫到文件中。

3. 寫入文件尾,並清理ffmpeg對象。

 

首先,需要初始化ffmpeg的一些對象,初始化的順序為:

創建並初始化AVOutputFormat, 基於AVOutputFormat創建並初始化AVFormatContext。

然后查找AVCodec, 基於找到的AVCodec創建並初始化AVCodecContext,打開AVCodec。

然后基於找到的AVCodec創建AVStream。

然后創建並初始化AVIOContext。

其中AVStream, AVCodec, AVCodecContext可能會有兩組,一組用來錄制音頻,一組用來錄制視頻,如下:

 

AVOutputFormat和AVFormatContext可以通過avformat_alloc_output_context函數來初始化。

AVCodec通過avcodec_find_encoder函數來查找

AVCodecContext通過avcodec_alloc_context3來分配

AVCodecContext初始化完成后,可以通過avcodec_open2打開編碼器

AVStream通過avformat_new_stream來分配

以上對象初始化完成后,需要將codec的信息拷貝到AVFormatContext對象中,以便與將編碼器信息存儲到文件中,這個操作可以通過avcodec_parameters_from_context操作

最后通過avio_open打開文件並初始化AVIOContext。

最后通過avformat_write_header寫入文件頭,整個初始化階段就算是完成了

以下初始化代碼供參考:

 1     avformat_alloc_output_context2(&format_context_, nullptr, nullptr, file_path.c_str());
 2     if(format_context_ == nullptr){
 3         avformat_alloc_output_context2(&format_context_, nullptr, "mpeg", file_path.c_str());
 4     }
 5 
 6     if(format_context_ == nullptr){
 7         return false;
 8     }
 9 
10     AVOutputFormat *output_format = format_context_->oformat;
11     output_format->video_codec = AV_CODEC_ID_H264;
12 
13     AVCodec *codec = avcodec_find_encoder(output_format->video_codec);
14 
15     codec_context_ = avcodec_alloc_context3(codec);
16     codec_context_->codec_id = output_format->video_codec;
17     codec_context_->pix_fmt = AV_PIX_FMT_YUV420P;
18     codec_context_->width = width;
19     codec_context_->height = height;
20     codec_context_->time_base = {1, 1000};
21     codec_context_->gop_size = 12;
22     if (codec_context_->codec_id == AV_CODEC_ID_MPEG2VIDEO){
23         codec_context_->max_b_frames = 2;
24     }
25     if (codec_context_->codec_id == AV_CODEC_ID_MPEG1VIDEO){
26         codec_context_->mb_decision = 2;
27     }
28 
29     if (output_format->flags & AVFMT_GLOBALHEADER)
30         codec_context_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
31 
32     int ret = avcodec_open2(codec_context_, codec, nullptr);
33     if(ret != 0){
34         return false;
35     }
36 
37     video_stream_ = avformat_new_stream(format_context_, codec);
38     if(video_stream_ == nullptr){
39         return false;
40     }
41 
42     ret = avcodec_parameters_from_context(video_stream_->codecpar, codec_context_);
43     if(ret != 0){
44         return false;
45     }
46 
47     ret = avio_open(&format_context_->pb, file_path.c_str(), AVIO_FLAG_WRITE);
48     if(ret != 0){
49         return false;
50     }
51 
52     ret = avformat_write_header(format_context_, nullptr);
53     if(ret != 0){
54         return false;
55     }

注意,有些編碼器只支持一些固定的幀率,對於這樣的編碼器,AVCodecContext中的time_base是不能隨便設置的,當寫文件頭失敗時,可以檢查一下這一點

 

初始化完成后,就可以進行視頻編碼錄制了,跟初始化相比,編碼錄制的過程要簡單的多,核心函數就三個:

avcodec_send_frame進行視頻編碼

avcodec_receive_packet用於獲取編碼后的數據

av_write_frame用於將編碼后的數據寫入文件

以下代碼供參考:

 1 av_image_fill_arrays(src_frame_->data, src_frame_->linesize, data,
 2                          AV_PIX_FMT_RGB24, src_frame_->width, src_frame_->height, 1);
 3 
 4     sws_scale(sws_context_, src_frame_->data, src_frame_->linesize, 0, src_frame_->height,
 5               dst_frame_->data, dst_frame_->linesize);
 6 
 7     auto now_time = std::chrono::steady_clock::now();
 8     dst_frame_->pts = std::chrono::duration_cast<std::chrono::milliseconds>(now_time - start_time_point_).count();
 9 
10     int ret = avcodec_send_frame(codec_context_, dst_frame_);
11     if(ret == 0){
12         AVPacket packet;
13         av_init_packet(&packet);
14         ret = avcodec_receive_packet(codec_context_, &packet);
15         if(ret == 0){
16             av_packet_rescale_ts(&packet, codec_context_->time_base, video_stream_->time_base);
17             av_write_frame(format_context_, &packet);
18         }
19         av_packet_unref(&packet);
20     }

這里的第16行注意一下,將編碼后的數據寫入文件之前,一定要進行時間轉換,否則播放視頻時會出現視頻播放速度太快的問題

 

最后就是結束錄制了,這個過程就不用多說了,看代碼:

 1 if(format_context_ != nullptr){
 2         av_write_trailer(format_context_);
 3     }
 4 
 5     if(sws_context_ != nullptr){
 6         sws_freeContext(sws_context_);
 7         sws_context_ = nullptr;
 8     }
 9 
10     if(codec_context_ != nullptr){
11         avcodec_close(codec_context_);
12         avcodec_free_context(&codec_context_);
13     }
14 
15     if(format_context_ != nullptr){
16         avio_close(format_context_->pb);
17 
18         avformat_free_context(format_context_);
19         format_context_ = nullptr;
20     }

 

(注意:以上所有代碼都是僅供參考,並不是完整代碼)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM