ffmpeg 將yuv轉換成H264
阿新 • • 發佈:2019-02-04
http://download.csdn.net/detail/sz76211822/9698824#include "stdafx.h" #ifdef _WIN32 //Windows extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "libavutil/avutil.h" }; #else //Linux... #ifdef __cplusplus extern "C" { #endif #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/opt.h> #include <libavutil/parseutils.h> #include <libavutil/avutil.h> #ifdef __cplusplus }; #endif #endif void encoder() { const char *pOutFile = "ds.h264"; FILE*pFile = fopen("ds_480x272.yuv", "rb"); if (!pFile){ return; } // 註冊 ffmpeg 中的所有的封裝、解封裝 和 協議等 av_register_all(); // 用作之後寫入視訊幀並編碼成 h264,貫穿整個工程當中 AVFormatContext *pFormatContext = avformat_alloc_context(); // 通過這個函式可以獲取輸出檔案的編碼格式, 那麼這裡我們的 fmt 為 h264 格式(AVOutputFormat *) AVOutputFormat *pOutputFormat = av_guess_format(NULL, pOutFile, NULL); pFormatContext->oformat = pOutputFormat; //將輸出檔案中的資料讀入到程式的 buffer 當中,方便之後的資料寫入,也可以說快取資料寫入 avio_open(&pFormatContext->pb, pOutFile, AVIO_FLAG_READ_WRITE); // 通過媒體檔案控制者獲取輸出檔案的流媒體資料,這裡 AVCodec * 寫 0 , 預設會為我們計算出合適的編碼格式 AVStream *pStream = avformat_new_stream(pFormatContext, 0); if (!pStream){ return; } // 設定 25 幀每秒 ,也就是 fps 為 25。(其實不設定也可以) pStream->time_base.num = 1; pStream->time_base.den = 25; // 使用者儲存編碼所需的引數格式等等 AVCodecContext*pCodecContext = NULL; // 從媒體流中獲取到編碼結構體,他們是一一對應的關係,一個 AVStream 對應一個 AVCodecContext pCodecContext = pStream->codec; // 設定編碼器的 id,每一個編碼器都對應著自己的 id,例如 h264 的編碼 id 就是 AV_CODEC_ID_H264 pCodecContext->codec_id = pOutputFormat->video_codec; // 設定編碼型別為 視訊編碼 pCodecContext->codec_type = AVMEDIA_TYPE_VIDEO; // 設定畫素格式為 yuv 格式 pCodecContext->pix_fmt = PIX_FMT_YUVJ420P; // 設定視訊的寬高 pCodecContext->width = 480; pCodecContext->height = 272; // 設定 25 幀每秒 ,也就是 fps 為 25 pCodecContext->time_base.num = 1; pCodecContext->time_base.den = 25; // 設定位元率,每秒傳輸多少位元數 bit,位元率越高,傳送速度越快,也可以稱作位元速率, // 視訊中的位元是指由模擬訊號轉換為數字訊號後,單位時間內的二進位制資料量。 pCodecContext->bit_rate = 400000; // 設定影象組層的大小。 // 影象組層是在 MPEG 編碼器中存在的概念,影象組包 若干幅影象, 組頭包 起始碼、GOP 標誌等,如視訊磁帶記錄器時間、控制碼、B 幀處理碼等; pCodecContext->gop_size = 250; pCodecContext->qmin = 10; pCodecContext->qmax = 51; pCodecContext->qcompress = 1; // 設定 B 幀最大的數量,B幀為視訊圖片空間的前後預測幀, B 幀相對於 I、P 幀來說,壓縮率比較大,也就是說相同位元速率的情況下, // 越多 B 幀的視訊,越清晰,現在很多打視訊網站的高清視訊,就是採用多編碼 B 幀去提高清晰度, // 但同時對於編解碼的複雜度比較高,比較消耗效能與時間 pCodecContext->max_b_frames = 3; AVDictionary *param = 0; if(pCodecContext->codec_id == AV_CODEC_ID_H264) {// 可選設定 // 通過--preset的引數調節編碼速度和質量的平衡。 av_dict_set(¶m, "preset", "slow", 0); // 通過--tune的引數值指定片子的型別,是和視覺優化的引數,或有特別的情況。 // zerolatency: 零延遲,用在需要非常低的延遲的情況下,比如電視電話會議的編碼 av_dict_set(¶m, "tune", "zerolatency", 0); } // 通過 codec_id 找到對應的編碼器 AVCodec *pCodec = avcodec_find_encoder(pCodecContext->codec_id); if (!pCodec){ return; } // 開啟編碼器,並設定引數 param avcodec_open2(pCodecContext, pCodec, ¶m); AVFrame *pFrame = av_frame_alloc(); // 通過畫素格式(這裡為 YUV)獲取圖片的真實大小,例如將 480 * 720 轉換成 int 型別 int nPicSize = avpicture_get_size(pCodecContext->pix_fmt, pCodecContext->width, pCodecContext->height); // 將 picture_size 轉換成位元組資料,byte uint8_t *buf = (uint8_t*)av_malloc(nPicSize); // 設定原始資料 AVFrame 的每一個frame 的圖片大小,AVFrame 這裡儲存著 YUV 非壓縮資料 avpicture_fill((AVPicture*)pFrame, buf, pCodecContext->pix_fmt, pCodecContext->width, pCodecContext->height); // 編寫 h264 封裝格式的檔案頭部,基本上每種編碼都有著自己的格式的頭部,想看具體實現的同學可以看看 h264 的具體實現 avformat_write_header(pFormatContext, NULL); //建立編碼後的資料 AVPacket 結構體來儲存 AVFrame 編碼後生成的資料 AVPacket pkt; av_new_packet(&pkt, nPicSize); int i = 0; int y_size = pCodecContext->width * pCodecContext->height; while (true){ if (fread(buf, 1, y_size*3/2, pFile) <= 0){ printf("Could not read input file."); break; } else if(feof(pFile)){ break; } pFrame->data[0] = buf; // 亮度Y pFrame->data[1] = buf+ y_size; // U pFrame->data[2] = buf+ y_size*5/4; // V pFrame->pts = i++; //Encode int got_picture = 0; int ret = avcodec_encode_video2(pCodecContext, &pkt, pFrame, &got_picture); if(ret < 0){ printf("Encode Error.\n"); break; } if (got_picture == 1){// 編碼成功後寫入 AVPacket 到 輸入輸出資料操作著 pFormatCtx 中,當然,記得釋放記憶體 pkt.stream_index = pStream->index; ret = av_write_frame(pFormatContext, &pkt); av_free_packet(&pkt); } } av_free_packet(&pkt); // 寫入資料流尾部到輸出檔案當中,並釋放檔案的私有資料 av_write_trailer(pFormatContext); if (pStream){ avcodec_close(pStream->codec);// 關閉編碼器 } av_free(pFrame); av_free(buf); avio_close(pFormatContext->pb); avformat_free_context(pFormatContext);// 關閉輸入資料的快取 fclose(pFile); return ; } int _tmain(int argc, _TCHAR* argv[]) { encoder(); return 0; }