【iOS】FFmpeg視頻錄制的實現


  用FFmpeg錄制視頻,這里的需求是點擊錄制按鈕后保存屏幕上的視頻。在【iOS】FFmpeg編譯+h264解碼+yuv渲染中已經實現了對h264文件的一幀幀讀取,實現的思路就是將讀取的一幀幀AVPacket未解碼的h264文件存儲起來,錄制就算完成了。需要注意的是FFMPEG只是幫你將視頻寫到合理的地方,文件頭還是要寫的,不然系統不知道這是個什么文件。
  還是老話要放在前邊,有不足的地方還希望批評指正。

  我們可以把錄制視頻的過程拆分為兩個步驟,輸入文件和輸出文件,輸入文件就是我們讀取h264文件的過程,輸出文件就是我們錄制的文件,從輸入文件到輸出文件就涉及到一個拷貝的過程, avcodec_copy_context這個拷貝函數就是橋接我們輸入文件和輸出文件的關鍵所在,即我們的目的視頻的錄制。
視頻錄制的大致流程是:

1.avformat_open_input():打開輸入文件,初始化輸入視頻碼流的AVFormatContext。
2.av_read_frame():從輸入文件中讀取一個AVPacket。
3.avformat_alloc_output_context2():初始化輸出視頻碼流的AVFormatContext。
4.avformat_new_stream():創建輸出碼流的AVStream。
avcodec_copy_context():拷貝輸入視頻碼流的AVCodecContex的數值t到輸出視頻的AVCodecContext。
5.avio_open():打開輸出文件。
6.avformat_write_header():寫文件頭(對於某些沒有文件頭的封裝格式,不需要此函數。比如說MPEG2TS)。
7.av_interleaved_write_frame():將AVPacket(存儲視頻壓縮碼流數據)寫入文件。
8.av_write_trailer():寫文件尾(對於某些沒有文件頭的封裝格式,不需要此函數。比如說MPEG2TS)。

上代碼:

//初始化一些參數,以及指定輸出位置及文件名 const char *in_filename, *out_filename; ofmt = NULL; ifmt_ctx = NULL; ofmt_ctx = NULL; NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; formatter.dateFormat = @"YYYY-MM-dd hh:mm:ss"; NSString *date = [formatter stringFromDate:[NSDate date]]; out_filename = [[NSString stringWithFormat:@"%@/Video/%@.mp4", [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] ,date] cStringUsingEncoding:NSASCIIStringEncoding];//Output file URL
    //1.Input if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) { printf( "Could not open input file."); goto end; } if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) { printf( "Failed to retrieve input stream information"); goto end; } av_dump_format(ifmt_ctx, 0, in_filename, 0);
    //輸出(Output) avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename); if (!ofmt_ctx) { printf( "Could not create output context\n"); ret = AVERROR_UNKNOWN; goto end; }
    ofmt = ofmt_ctx->oformat; for (int i = 0; i < ifmt_ctx->nb_streams; i++) { //根據輸入流創建輸出流(Create output AVStream according to input AVStream) AVStream *in_stream = ifmt_ctx->streams[i]; AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec); if (!out_stream) { printf( "Failed allocating output stream\n"); ret = AVERROR_UNKNOWN; goto end; } //復制AVCodecContext的設置(Copy the settings of AVCodecContext) ret = avcodec_copy_context(out_stream->codec, in_stream->codec); if (ret < 0) { printf( "Failed to copy context from input to output stream codec context\n"); goto end; } out_stream->codec->codec_tag = 0; if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; }
    //輸出格式 av_dump_format(ofmt_ctx, 0, out_filename, 1);
    //打開輸出文件(Open output file) if (!(ofmt->flags & AVFMT_NOFILE)) { ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE); if (ret < 0) { printf( "Could not open output file '%s'", out_filename); goto end; } }
    //寫文件頭(Write file header) ret = avformat_write_header(ofmt_ctx, NULL); if (ret < 0) { printf( "Error occurred when opening output file\n"); goto end; } int frame_index=0; while (1) { AVStream *in_stream, *out_stream; //獲取一個AVPacket(Get an AVPacket) ret = av_read_frame(ifmt_ctx, &pkt); if (ret < 0) break; in_stream = ifmt_ctx->streams[pkt.stream_index]; out_stream = ofmt_ctx->streams[pkt.stream_index]; /* copy packet */ //轉換PTS/DTS(Convert PTS/DTS) pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)); pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)); pkt.duration = (int)av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base); pkt.pos = -1; //寫入(Write) ret = av_interleaved_write_frame(ofmt_ctx, &pkt); if (ret < 0) { printf( "Error muxing packet\n"); break; } printf("Write %8d frames to output file\n",frame_index); av_free_packet(&pkt); frame_index++; }
    //寫文件尾(Write file trailer) av_write_trailer(ofmt_ctx); end: avformat_close_input(&ifmt_ctx); /* close output */ if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE)) avio_close(ofmt_ctx->pb); avformat_free_context(ofmt_ctx); if (ret < 0 && ret != AVERROR_EOF) { printf( "Error occurred.\n"); return NO; }

 


免責聲明!

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



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