Qt开发笔记之编码h264码流并封装mp4
《Qt开发笔记之编码h264码流并封装mp4(一):x264介绍、windows平台x264库编译》
《Qt开发笔记之编码h264码流并封装mp4(二):windows平台x264添加mp4支持,gpac库的介绍与编译》
《Qt开发笔记之编码h264码流并封装mp4(三):Qt使用x264库对.yuv文件编码为.h264文件》
《Qt开发笔记之编码h264码流并封装mp4(四):mp4v2库的介绍和windows平台编译》
《Qt开发笔记之编码h264码流并封装mp4(五):ubuntu平台编译x264》
《Qt开发笔记之编码h264码流并封装mp4(六):ubuntu平台编译mp4v2并封装mp4》
前言
前面成功在windows下使用mingw32编译x264,受mp4v2库影响,遂决定先转换到ubuntu平台继续。
Ubuntu下x264库编译
步骤一:下载、放入编译文件夹并解压
步骤二:配置configure
./configure --enable-shared --disable-asm
步骤三:编译make
(使用mingw32-make一样,因为使用的是同样的编译器g++编译)
步骤四:安装make install
Demo演示
该demo测试从yuv格式编码出h264文件。
关键代码
bool X264Manager::testYuvToH264(QString yuvFile, QString destFile) { bool ret = false; // 检测输入文件 if(!yuvFile.endsWith(".yuv")) { qDebug() << __FILE__ << __LINE__ << "Failed to recgnize ext:" << yuvFile; return ret; } // 检测输出文件 if(destFile.isEmpty()) { destFile = yuvFile; destFile.truncate(destFile.lastIndexOf(".yuv")); destFile += ".h264"; }else if(!destFile.endsWith(".h264")) { qDebug() << __FILE__ << __LINE__ << "Failed to recgnize ext:" << destFile; return ret; } qDebug() << __FILE__ << __LINE__ << yuvFile << "to" << destFile; // 打开输入文件 FILE *fpSrc = fopen(yuvFile.toUtf8().data(), "rb"); if(!fpSrc) { qDebug() << __FILE__ << __LINE__ << "Failed to open file:" << yuvFile; } // 打开输出文件 FILE *fpDst = fopen(destFile.toUtf8().data(), "wb"); if(!fpDst) { qDebug() << __FILE__ << __LINE__ << "Failed to open file:" << destFile; } x264_nal_t * pX264NalT; // 编码参数 int width = 640; // 宽度 int height = 360; // 高度 x264_param_t x264ParamT; // 编码参数 x264_picture_t x264PictureTIn; // 帧缓存 x264_picture_t x264PictureTOut; // 帧缓存 int inFrames = 0; int x264NalT; // 给结构体赋默认值 x264_param_default(&x264ParamT); // 初始化编码参数 // 日志登记 x264ParamT.i_log_level = X264_LOG_DEBUG; // 自动选择最佳前瞻线程缓冲区大小 x264ParamT.i_threads = X264_SYNC_LOOKAHEAD_AUTO; // 颜色深度 x264ParamT.i_bitdepth = 8; // 输入为420 x264ParamT.i_csp = X264_CSP_I420; // 宽度 x264ParamT.i_width = width; // 高度 x264ParamT.i_height = height; // 设置帧率(分子) x264ParamT.i_fps_num = 30; // 设置帧率时间1s(分母) x264ParamT.i_fps_den = 1; //在此间隔设置IDR关键帧的数量 x264ParamT.i_keyint_max = 20; // 码率控制方法,CQP(恒定质量),CRF(恒定码率,缺省值23),ABR(平均码率) // x264ParamT.rc.i_rc_method = X264_RC_CQP; // 设置后,会导致参数应用失败 x264ParamT.rc.i_rc_method = X264_RC_CRF; // 设置后,会导致参数应用失败 或者 编码器打开失败 // x264ParamT.rc.i_rc_method = X264_RC_CQP; // 设置后,会导致参数应用失败 // 应用参数 if(x264_param_apply_profile(&x264ParamT, "baseline") < 0) { qDebug() << __FILE__ << __LINE__ << "Failed to x264_param_apply_profile"; return ret; } // 初始化删除 x264_picture_init(&x264PictureTOut); // 分配帧缓存 if(x264_picture_alloc(&x264PictureTIn, x264ParamT.i_csp, x264ParamT.i_width, x264ParamT.i_height) < 0) { qDebug() << __FILE__ << __LINE__ << "Failed to x264_picture_alloc"; return ret; } // 获取编码器 x264_t *pX264T = 0; // 检测输入文件 if(!yuvFile.endsWith(".yuv")) { qDebug() << __FILE__ << __LINE__ << "Failed to recgnize ext:" << yuvFile; return ret; } // 检测输出文件 if(destFile.isEmpty()) { destFile = yuvFile; destFile.truncate(destFile.lastIndexOf(".yuv")); destFile += ".h264"; }else if(!destFile.endsWith(".h264")) { qDebug() << __FILE__ << __LINE__ << "Failed to recgnize ext:" << destFile; return ret; } qDebug() << __FILE__ << __LINE__ << yuvFile << "to" << destFile; pX264T = x264_encoder_open(&x264ParamT); if(!pX264T) { qDebug() << __FILE__ << __LINE__ << "Failed to x264_encoder_open"; x264_picture_clean(&x264PictureTIn); return ret; } // 总点大小 int framesPixelCount = x264ParamT.i_width * x264ParamT.i_height; // 判断帧的数量(文件大小/每帧大小) if(inFrames == 0) { fseek(fpSrc, 0, SEEK_END); switch (x264ParamT.i_csp) { case X264_CSP_I444: inFrames = ftell(fpSrc) / (framesPixelCount * 3); break; case X264_CSP_I422: inFrames = ftell(fpSrc) / (framesPixelCount * 2); break; case X264_CSP_I420: inFrames = ftell(fpSrc) / (framesPixelCount * 3 / 2); break; default: break; } fseek(fpSrc, 0, SEEK_SET); } // 循环编码 for(int index = 0; index < inFrames; index++) { switch (x264ParamT.i_csp) { case X264_CSP_I444: fread(x264PictureTIn.img.plane[0], framesPixelCount, 1, fpSrc); // Y fread(x264PictureTIn.img.plane[1], framesPixelCount, 1, fpSrc); // U fread(x264PictureTIn.img.plane[2], framesPixelCount, 1, fpSrc); // V break; case X264_CSP_I422: { int i = 0; int y_i=0,u_i=0,v_i=0; for(i = 0 ; i < framesPixelCount * 2 ;) { fread(&x264PictureTIn.img.plane[0][y_i++], 1, 1, fpSrc); //Y fread(&x264PictureTIn.img.plane[1][u_i++], 1, 1, fpSrc); //U fread(&x264PictureTIn.img.plane[0][y_i++], 1, 1, fpSrc); //Y fread(&x264PictureTIn.img.plane[2][v_i++], 1, 1, fpSrc); //V }break; } break; case X264_CSP_I420: fread(x264PictureTIn.img.plane[0], framesPixelCount, 1, fpSrc); // Y fread(x264PictureTIn.img.plane[1], framesPixelCount/4, 1, fpSrc); // U fread(x264PictureTIn.img.plane[2], framesPixelCount/4, 1, fpSrc); // V break; default: break; } // 当前帧数 x264PictureTIn.i_pts = index; qDebug() << __FILE__ << __LINE__ << "Succeed to get frame:" << index << "/" << inFrames; int iRet = x264_encoder_encode(pX264T, &pX264NalT, &x264NalT, &x264PictureTIn, &x264PictureTOut); if(iRet < 0) { qDebug() << __FILE__ << __LINE__ << "Failed to x264_encoder_encode"; return ret; } qDebug() << __FILE__ << __LINE__ << "Succeed to encode frame:" << index; #if 1 // 此处是一边拿到编码器一边编码写入到文件中 int i = 0; // 编码器编码存入文件 for(int j = 0; j < x264NalT; j++) { fwrite(pX264NalT[j].p_payload, 1, pX264NalT[j].i_payload, fpDst); } #endif } #if 0 // 此处是全部拿到编码器里面后,再循环拿取所有,然后写入 int i = 0; while(true) { int iRet = x264_encoder_encode(pX264T, &pX264NalT, &x264NalT, NULL, &x264PictureTOut); if(iRet == 0) { break; } qDebug() << __FILE__ << __LINE__ << i << inFrames << iRet; // 编码器编码存入文件 for(int j = 0; j < x264NalT; j++) { fwrite(pX264NalT[j].p_payload, 1, pX264NalT[j].i_payload, fpDst); } i++; } #endif qDebug() << __FILE__ << __LINE__ << "It's finished!!!"; x264_picture_clean(&x264PictureTIn); x264_encoder_close(pX264T); pX264T = 0; fclose(fpSrc); fclose(fpDst); }