iOS硬編碼實(shí)現(xiàn)
前言
- 在上一篇中安接,我們已經(jīng)知道iOS編碼的一些概念知識(shí)雾家,從現(xiàn)在開始,我們可以正式對(duì)采集到的視頻進(jìn)行編碼
- 這里我們重點(diǎn)介紹硬編碼的使用方式芯咧,也就是VideoToolBox框架的使用
- 編碼的流程:采集--> 獲取到視頻幀--> 對(duì)視頻幀進(jìn)行編碼 --> 獲取到視頻幀信息 --> 將編碼后的數(shù)據(jù)以NALU方式寫入到文件
視頻采集
- 視頻采集我們已經(jīng)在前面進(jìn)行了介紹和學(xué)習(xí),所有這里就直接貼代碼敬飒,只是我對(duì)采集過程進(jìn)行了一些簡(jiǎn)單的封裝
視頻采集.png
視頻硬件編碼
- 初始化壓縮編碼會(huì)話(VTCompressionSessionRef)
- 在VideoToolbox框架的使用過程中鹃共,基本都是C語言函數(shù)
- 初始化后通過
VTSessionSetProperty
設(shè)置對(duì)象屬性- 編碼方式:H.264編碼
- 幀率:每秒鐘多少幀畫面
- 碼率:?jiǎn)挝粫r(shí)間內(nèi)保存的數(shù)據(jù)量
- 關(guān)鍵幀(GOPsize)間隔:多少幀為一個(gè)GOP
- 準(zhǔn)備編碼
- 代碼如下:
- (void)setupVideoSession {
// 1.用于記錄當(dāng)前是第幾幀數(shù)據(jù)(畫面幀數(shù)非常多)
self.frameID = 0;
// 2.錄制視頻的寬度&高度
int width = [UIScreen mainScreen].bounds.size.width;
int height = [UIScreen mainScreen].bounds.size.height;
// 3.創(chuàng)建CompressionSession對(duì)象,該對(duì)象用于對(duì)畫面進(jìn)行編碼
// kCMVideoCodecType_H264 : 表示使用h.264進(jìn)行編碼
// didCompressH264 : 當(dāng)一次編碼結(jié)束會(huì)在該函數(shù)進(jìn)行回調(diào),可以在該函數(shù)中將數(shù)據(jù),寫入文件中
VTCompressionSessionCreate(NULL, width, height, kCMVideoCodecType_H264, NULL, NULL, NULL, didCompressH264, (__bridge void *)(self), &_compressionSession);
// 4.設(shè)置實(shí)時(shí)編碼輸出(直播必然是實(shí)時(shí)輸出,否則會(huì)有延遲)
VTSessionSetProperty(self.compressionSession, kVTCompressionPropertyKey_RealTime, kCFBooleanTrue);
// 5.設(shè)置期望幀率(每秒多少幀,如果幀率過低,會(huì)造成畫面卡頓)
int fps = 30;
CFNumberRef fpsRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &fps);
VTSessionSetProperty(self.compressionSession, kVTCompressionPropertyKey_ExpectedFrameRate, fpsRef);
// 6.設(shè)置碼率(碼率: 編碼效率, 碼率越高,則畫面越清晰, 如果碼率較低會(huì)引起馬賽克 --> 碼率高有利于還原原始畫面,但是也不利于傳輸)
int bitRate = 800*1024;
CFNumberRef bitRateRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &bitRate);
VTSessionSetProperty(self.compressionSession, kVTCompressionPropertyKey_AverageBitRate, bitRateRef);
NSArray *limit = @[@(bitRate * 1.5/8), @(1)];
VTSessionSetProperty(self.compressionSession, kVTCompressionPropertyKey_DataRateLimits, (__bridge CFArrayRef)limit);
// 7.設(shè)置關(guān)鍵幀(GOPsize)間隔
int frameInterval = 30;
CFNumberRef frameIntervalRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &frameInterval);
VTSessionSetProperty(self.compressionSession, kVTCompressionPropertyKey_MaxKeyFrameInterval, frameIntervalRef);
// 8.基本設(shè)置結(jié)束, 準(zhǔn)備進(jìn)行編碼
VTCompressionSessionPrepareToEncodeFrames(self.compressionSession);
}
- 將輸入的幀進(jìn)行編碼
- 將CMSampleBufferRef轉(zhuǎn)成CVImageBufferRef
- 開始對(duì)CVImageBufferRef進(jìn)行編碼
- (void)encodeSampleBuffer:(CMSampleBufferRef)sampleBuffer {
// 1.將sampleBuffer轉(zhuǎn)成imageBuffer
CVImageBufferRef imageBuffer = (CVImageBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);
// 2.根據(jù)當(dāng)前的幀數(shù),創(chuàng)建CMTime的時(shí)間
CMTime presentationTimeStamp = CMTimeMake(self.frameID++, 1000);
VTEncodeInfoFlags flags;
// 3.開始編碼該幀數(shù)據(jù)
OSStatus statusCode = VTCompressionSessionEncodeFrame(self.compressionSession,
imageBuffer,
presentationTimeStamp,
kCMTimeInvalid,
NULL, (__bridge void * _Nullable)(self), &flags);
if (statusCode == noErr) {
NSLog(@"H264: VTCompressionSessionEncodeFrame Success");
}
}
- 當(dāng)編碼成功后,將編碼后的碼流寫入文件
- 編碼成功后會(huì)回調(diào)之前輸入的函數(shù)
- 1> 先判斷是否是關(guān)鍵幀:
- 如果是關(guān)鍵幀驶拱,則需要在寫入關(guān)鍵幀之前霜浴,先寫入PPS、SPS的NALU
- 取出PPS蓝纲、SPS數(shù)據(jù)阴孟,并且封裝成NALU單元,寫入文件
- 2> 將I幀税迷、P幀永丝、B幀分別封裝成NALU單元寫入文件
- 寫入后,數(shù)據(jù)存儲(chǔ)方式:
數(shù)據(jù)存儲(chǔ)方式.png
- 代碼如下:
// 編碼完成回調(diào)
void didCompressH264(void *outputCallbackRefCon, void *sourceFrameRefCon, OSStatus status, VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer) {
// 1.判斷狀態(tài)是否等于沒有錯(cuò)誤
if (status != noErr) {
return;
}
// 2.根據(jù)傳入的參數(shù)獲取對(duì)象
VideoEncoder* encoder = (__bridge VideoEncoder*)outputCallbackRefCon;
// 3.判斷是否是關(guān)鍵幀
bool isKeyframe = !CFDictionaryContainsKey( (CFArrayGetValueAtIndex(CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, true), 0)), kCMSampleAttachmentKey_NotSync);
// 判斷當(dāng)前幀是否為關(guān)鍵幀
// 獲取sps & pps數(shù)據(jù)
if (isKeyframe)
{
// 獲取編碼后的信息(存儲(chǔ)于CMFormatDescriptionRef中)
CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer);
// 獲取SPS信息
size_t sparameterSetSize, sparameterSetCount;
const uint8_t *sparameterSet;
CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 0, &sparameterSet, &sparameterSetSize, &sparameterSetCount, 0 );
// 獲取PPS信息
size_t pparameterSetSize, pparameterSetCount;
const uint8_t *pparameterSet;
CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 1, &pparameterSet, &pparameterSetSize, &pparameterSetCount, 0 );
// 裝sps/pps轉(zhuǎn)成NSData箭养,以方便寫入文件
NSData *sps = [NSData dataWithBytes:sparameterSet length:sparameterSetSize];
NSData *pps = [NSData dataWithBytes:pparameterSet length:pparameterSetSize];
// 寫入文件
[encoder gotSpsPps:sps pps:pps];
}
// 獲取數(shù)據(jù)塊
CMBlockBufferRef dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
size_t length, totalLength;
char *dataPointer;
OSStatus statusCodeRet = CMBlockBufferGetDataPointer(dataBuffer, 0, &length, &totalLength, &dataPointer);
if (statusCodeRet == noErr) {
size_t bufferOffset = 0;
static const int AVCCHeaderLength = 4; // 返回的nalu數(shù)據(jù)前四個(gè)字節(jié)不是0001的startcode慕嚷,而是大端模式的幀長(zhǎng)度length
// 循環(huán)獲取nalu數(shù)據(jù)
while (bufferOffset < totalLength - AVCCHeaderLength) {
uint32_t NALUnitLength = 0;
// Read the NAL unit length
memcpy(&NALUnitLength, dataPointer + bufferOffset, AVCCHeaderLength);
// 從大端轉(zhuǎn)系統(tǒng)端
NALUnitLength = CFSwapInt32BigToHost(NALUnitLength);
NSData* data = [[NSData alloc] initWithBytes:(dataPointer + bufferOffset + AVCCHeaderLength) length:NALUnitLength];
[encoder gotEncodedData:data isKeyFrame:isKeyframe];
// 移動(dòng)到寫一個(gè)塊,轉(zhuǎn)成NALU單元
// Move to the next NAL unit in the block buffer
bufferOffset += AVCCHeaderLength + NALUnitLength;
}
}
}
- (void)gotSpsPps:(NSData*)sps pps:(NSData*)pps
{
// 1.拼接NALU的header
const char bytes[] = "\x00\x00\x00\x01";
size_t length = (sizeof bytes) - 1;
NSData *ByteHeader = [NSData dataWithBytes:bytes length:length];
// 2.將NALU的頭&NALU的體寫入文件
[self.fileHandle writeData:ByteHeader];
[self.fileHandle writeData:sps];
[self.fileHandle writeData:ByteHeader];
[self.fileHandle writeData:pps];
}
- (void)gotEncodedData:(NSData*)data isKeyFrame:(BOOL)isKeyFrame
{
NSLog(@"gotEncodedData %d", (int)[data length]);
if (self.fileHandle != NULL)
{
const char bytes[] = "\x00\x00\x00\x01";
size_t length = (sizeof bytes) - 1; //string literals have implicit trailing '\0'
NSData *ByteHeader = [NSData dataWithBytes:bytes length:length];
[self.fileHandle writeData:ByteHeader];
[self.fileHandle writeData:data];
}
}
iOS視頻軟編碼
軟編碼介紹
- 軟編碼主要是利用CPU進(jìn)行編碼的過程, 具體的編碼通常會(huì)用FFmpeg+x264
- FFmpeg
- FFmpeg是一個(gè)非常強(qiáng)大的音視頻處理庫,包括視頻采集功能毕泌、視頻格式轉(zhuǎn)換喝检、視頻抓圖、給視頻加水印等撼泛。
- FFmpeg在Linux平臺(tái)下開發(fā)挠说,但它同樣也可以在其它操作系統(tǒng)環(huán)境中編譯運(yùn)行,包括Windows愿题、Mac OS X等损俭。
- X264
- H.264是ITU制定的視頻編碼標(biāo)準(zhǔn)
- 而x264是一個(gè)開源的H.264/MPEG-4 AVC視頻編碼函數(shù)庫[1] 蛙奖,是最好的有損視頻編碼器,里面集成了非常多優(yōu)秀的算法用于視頻編碼.
- 關(guān)于軟編碼推薦博客(雷霄驊)
Mac安裝/使用FFmpeg
- 安裝
- ruby -e "$(curl -fsSkL raw.github.com/mxcl/homebrew/go)"
- brew install ffmpeg
- 簡(jiǎn)單使用
- 轉(zhuǎn)化格式: ffmpeg -i story.webm story.mp4
- 分離視頻: ffmpeg -i story.mp4 -vcodec copy -an demo.mp4
- 分離音頻: ffmpeg -i story.mp4 -acodec copy -vn demo.aac
編譯FFmpeg(iOS)
- 下載編譯FFmpeg所需要的腳本文件gas-preprocessor.pl
- 下載地址: https://github.com/mansr/gas-preprocessor
- 復(fù)制gas-preprocessor.pl到/usr/sbin下,(這個(gè)應(yīng)該是復(fù)制到/usr/local/bin)
- 修改文件權(quán)限:chmod 777 /usr/local/bin/gas-preprocessor.pl
- 下載腳本FFmpeg腳本
- 地址: https://github.com/kewlbear/FFmpeg-iOS-build-script
- 解壓杆兵,找到文件 build-ffmpeg.sh
- 執(zhí)行服本文件:./build-ffmpeg.sh
編譯X264
- 下載x264
- x264官網(wǎng) 下載x264源碼雁仲,將其文件夾名稱改為x264
- http://www.videolan.org/developers/x264.html
- 下載gas-preprocessor(FFmpeg編譯時(shí)已經(jīng)下載過)
- 下載x264 build shell
- 下載build-x264.sh 將文件build-x264.sh放在x264同一級(jí)目錄里面,注意不是放在x264文件夾里面琐脏。
- https://github.com/kewlbear/x264-ios
- 修改權(quán)限/執(zhí)行腳本
- sudo chmod u+x build-x264.sh
- sudo ./build-x264.sh
iOS項(xiàng)目中集成FFmpeg
- 將編譯好的文件夾拖入到工程中
- 添加依賴庫: libiconv.dylib/libz.dylib/libbz2.dylib/CoreMedia.framework/AVFoundation.framework
- 初始化編碼器
/*
* 設(shè)置X264
*/
- (int)setX264ResourceWithVideoWidth:(int)width height:(int)height bitrate:(int)bitrate
{
// 1.默認(rèn)從第0幀開始(記錄當(dāng)前的幀數(shù))
framecnt = 0;
// 2.記錄傳入的寬度&高度
encoder_h264_frame_width = width;
encoder_h264_frame_height = height;
// 3.注冊(cè)FFmpeg所有編解碼器(無論編碼還是解碼都需要該步驟)
av_register_all();
// 4.初始化AVFormatContext: 用作之后寫入視頻幀并編碼成 h264骆膝,貫穿整個(gè)工程當(dāng)中(釋放資源時(shí)需要銷毀)
pFormatCtx = avformat_alloc_context();
// 5.設(shè)置輸出文件的路徑
fmt = av_guess_format(NULL, out_file, NULL);
pFormatCtx->oformat = fmt;
// 6.打開文件的緩沖區(qū)輸入輸出阅签,flags 標(biāo)識(shí)為 AVIO_FLAG_READ_WRITE 政钟,可讀寫
if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0){
printf("Failed to open output file! \n");
return -1;
}
// 7.創(chuàng)建新的輸出流, 用于寫入文件
video_st = avformat_new_stream(pFormatCtx, 0);
// 8.設(shè)置 20 幀每秒 养交,也就是 fps 為 20
video_st->time_base.num = 1;
video_st->time_base.den = 25;
if (video_st==NULL){
return -1;
}
// 9.pCodecCtx 用戶存儲(chǔ)編碼所需的參數(shù)格式等等
// 9.1.從媒體流中獲取到編碼結(jié)構(gòu)體碎连,他們是一一對(duì)應(yīng)的關(guān)系驮履,一個(gè) AVStream 對(duì)應(yīng)一個(gè) AVCodecContext
pCodecCtx = video_st->codec;
// 9.2.設(shè)置編碼器的編碼格式(是一個(gè)id)玫镐,每一個(gè)編碼器都對(duì)應(yīng)著自己的 id,例如 h264 的編碼 id 就是 AV_CODEC_ID_H264
pCodecCtx->codec_id = fmt->video_codec;
// 9.3.設(shè)置編碼類型為 視頻編碼
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
// 9.4.設(shè)置像素格式為 yuv 格式
pCodecCtx->pix_fmt = PIX_FMT_YUV420P;
// 9.5.設(shè)置視頻的寬高
pCodecCtx->width = encoder_h264_frame_width;
pCodecCtx->height = encoder_h264_frame_height;
// 9.6.設(shè)置幀率
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 15;
// 9.7.設(shè)置碼率(比特率)
pCodecCtx->bit_rate = bitrate;
// 9.8.視頻質(zhì)量度量標(biāo)準(zhǔn)(常見qmin=10, qmax=51)
pCodecCtx->qmin = 10;
pCodecCtx->qmax = 51;
// 9.9.設(shè)置圖像組層的大小(GOP-->兩個(gè)I幀之間的間隔)
pCodecCtx->gop_size = 250;
// 9.10.設(shè)置 B 幀最大的數(shù)量杜跷,B幀為視頻圖片空間的前后預(yù)測(cè)幀葛闷, B 幀相對(duì)于 I孵运、P 幀來說治笨,壓縮率比較大,也就是說相同碼率的情況下旷赖,
// 越多 B 幀的視頻等孵,越清晰,現(xiàn)在很多打視頻網(wǎng)站的高清視頻果录,就是采用多編碼 B 幀去提高清晰度弱恒,
// 但同時(shí)對(duì)于編解碼的復(fù)雜度比較高棋恼,比較消耗性能與時(shí)間
pCodecCtx->max_b_frames = 5;
// 10.可選設(shè)置
AVDictionary *param = 0;
// H.264
if(pCodecCtx->codec_id == AV_CODEC_ID_H264) {
// 通過--preset的參數(shù)調(diào)節(jié)編碼速度和質(zhì)量的平衡爪飘。
av_dict_set(¶m, "preset", "slow", 0);
// 通過--tune的參數(shù)值指定片子的類型,是和視覺優(yōu)化的參數(shù)默终,或有特別的情況穷蛹。
// zerolatency: 零延遲昼汗,用在需要非常低的延遲的情況下顷窒,比如視頻直播的編碼
av_dict_set(¶m, "tune", "zerolatency", 0);
}
// 11.輸出打印信息,內(nèi)部是通過printf函數(shù)輸出(不需要輸出可以注釋掉該局)
av_dump_format(pFormatCtx, 0, out_file, 1);
// 12.通過 codec_id 找到對(duì)應(yīng)的編碼器
pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
if (!pCodec) {
printf("Can not find encoder! \n");
return -1;
}
// 13.打開編碼器鸦做,并設(shè)置參數(shù) param
if (avcodec_open2(pCodecCtx, pCodec,¶m) < 0) {
printf("Failed to open encoder! \n");
return -1;
}
// 13.初始化原始數(shù)據(jù)對(duì)象: AVFrame
pFrame = av_frame_alloc();
// 14.通過像素格式(這里為 YUV)獲取圖片的真實(shí)大小泼诱,例如將 480 * 720 轉(zhuǎn)換成 int 類型
avpicture_fill((AVPicture *)pFrame, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
// 15.h264 封裝格式的文件頭部治筒,基本上每種編碼都有著自己的格式的頭部,想看具體實(shí)現(xiàn)的同學(xué)可以看看 h264 的具體實(shí)現(xiàn)
avformat_write_header(pFormatCtx, NULL);
// 16.創(chuàng)建編碼后的數(shù)據(jù) AVPacket 結(jié)構(gòu)體來存儲(chǔ) AVFrame 編碼后生成的數(shù)據(jù)
av_new_packet(&pkt, picture_size);
// 17.設(shè)置 yuv 數(shù)據(jù)中 y 圖的寬高
y_size = pCodecCtx->width * pCodecCtx->height;
return 0;
}
- 編碼每一幀數(shù)據(jù)
/*
* 將CMSampleBufferRef格式的數(shù)據(jù)編碼成h264并寫入文件
*
*/
- (void)encoderToH264:(CMSampleBufferRef)sampleBuffer
{
// 1.通過CMSampleBufferRef對(duì)象獲取CVPixelBufferRef對(duì)象
CVPixelBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
// 2.鎖定imageBuffer內(nèi)存地址開始進(jìn)行編碼
if (CVPixelBufferLockBaseAddress(imageBuffer, 0) == kCVReturnSuccess) {
// 3.從CVPixelBufferRef讀取YUV的值
// NV12和NV21屬于YUV格式友多,是一種two-plane模式域滥,即Y和UV分為兩個(gè)Plane启绰,但是UV(CbCr)為交錯(cuò)存儲(chǔ)沟使,而不是分為三個(gè)plane
// 3.1.獲取Y分量的地址
UInt8 *bufferPtr = (UInt8 *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer,0);
// 3.2.獲取UV分量的地址
UInt8 *bufferPtr1 = (UInt8 *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer,1);
// 3.3.根據(jù)像素獲取圖片的真實(shí)寬度&高度
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
// 獲取Y分量長(zhǎng)度
size_t bytesrow0 = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer,0);
size_t bytesrow1 = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer,1);
UInt8 *yuv420_data = (UInt8 *)malloc(width * height *3/2);
/* convert NV12 data to YUV420*/
// 3.4.將NV12數(shù)據(jù)轉(zhuǎn)成YUV420數(shù)據(jù)
UInt8 *pY = bufferPtr ;
UInt8 *pUV = bufferPtr1;
UInt8 *pU = yuv420_data + width*height;
UInt8 *pV = pU + width*height/4;
for(int i =0;i<height;i++)
{
memcpy(yuv420_data+i*width,pY+i*bytesrow0,width);
}
for(int j = 0;j<height/2;j++)
{
for(int i =0;i<width/2;i++)
{
*(pU++) = pUV[i<<1];
*(pV++) = pUV[(i<<1) + 1];
}
pUV+=bytesrow1;
}
// 3.5.分別讀取YUV的數(shù)據(jù)
picture_buf = yuv420_data;
pFrame->data[0] = picture_buf; // Y
pFrame->data[1] = picture_buf+ y_size; // U
pFrame->data[2] = picture_buf+ y_size*5/4; // V
// 4.設(shè)置當(dāng)前幀
pFrame->pts = framecnt;
int got_picture = 0;
// 4.設(shè)置寬度高度以及YUV各式
pFrame->width = encoder_h264_frame_width;
pFrame->height = encoder_h264_frame_height;
pFrame->format = PIX_FMT_YUV420P;
// 5.對(duì)編碼前的原始數(shù)據(jù)(AVFormat)利用編碼器進(jìn)行編碼,將 pFrame 編碼后的數(shù)據(jù)傳入pkt 中
int ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);
if(ret < 0) {
printf("Failed to encode! \n");
}
// 6.編碼成功后寫入 AVPacket 到 輸入輸出數(shù)據(jù)操作著 pFormatCtx 中叽唱,當(dāng)然微宝,記得釋放內(nèi)存
if (got_picture==1) {
framecnt++;
pkt.stream_index = video_st->index;
ret = av_write_frame(pFormatCtx, &pkt);
av_free_packet(&pkt);
}
// 7.釋放yuv數(shù)據(jù)
free(yuv420_data);
}
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
}
- 釋放資源
/*
* 釋放資源
*/
- (void)freeX264Resource
{
// 1.釋放AVFormatContext
int ret = flush_encoder(pFormatCtx,0);
if (ret < 0) {
printf("Flushing encoder failed\n");
}
// 2.將還未輸出的AVPacket輸出出來
av_write_trailer(pFormatCtx);
// 3.關(guān)閉資源
if (video_st){
avcodec_close(video_st->codec);
av_free(pFrame);
}
avio_close(pFormatCtx->pb);
avformat_free_context(pFormatCtx);
}
int flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index)
{
int ret;
int got_frame;
AVPacket enc_pkt;
if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &
CODEC_CAP_DELAY))
return 0;
while (1) {
enc_pkt.data = NULL;
enc_pkt.size = 0;
av_init_packet(&enc_pkt);
ret = avcodec_encode_video2 (fmt_ctx->streams[stream_index]->codec, &enc_pkt,
NULL, &got_frame);
av_frame_free(NULL);
if (ret < 0)
break;
if (!got_frame){
ret=0;
break;
}
ret = av_write_frame(fmt_ctx, &enc_pkt);
if (ret < 0)
break;
}
return ret;
}