视诀项目的目标是视频美颜,对视频的处理先要解码为YUV420P图片,然后将处理好的图片再编码为mp4文件。在使用ffmpeg编码mp4过程中发现过于复杂,所以换了下思路,首先使用x264将YUV420编码为h264,然后使用mp4v2将编码后的h264编为mp4文件。

x264将YUV编码为H264

x264编码相关的资源封装

typedef struct encoder_x264_t{x264_param_t    param;x264_picture_t  pic_in;x264_picture_t  pic_out;x264_t *        h;int             iframe;int             iframe_size;x264_nal_t *    nal;int             inal;
}encoder_x264_t;

初始化

主要为encoder_x264_t的配置,以及相关资源的申请。要注意帧率的设置及slice的设置。如果slice大于1,那么在i帧就会有多个0x65,slice的数量大于1,在mp4v2在转mp4过程中会有问题。

int encoder_x264_init(encoder_x264_t* x, int width, int height, int fps_num, int fps_den){//if(x264_param_default_preset( &x->param, "medium", NULL ) < 0){// 不保留缓冲buffer,这样后面不需要调用x264_encoder_delayed_framesif(x264_param_default_preset( &x->param, "veryfast", "zerolatency" ) < 0){return -1;}// YUV420    x->param.i_csp = X264_CSP_I420;x->param.i_width  = width;x->param.i_height = height;x->param.b_vfr_input = 0;x->param.b_repeat_headers = 1;x->param.b_annexb = 1;// 帧率x->param.i_fps_num = fps_num;x->param.i_fps_den = fps_den;// 指定处理线程,如果不为1,slice设置将失效x->param.i_threads = 1;// 依赖i_threads的设置,否则此处设置一个slice将失效x->param.i_slice_count = 1;x->param.i_slice_count_max = 1;x->param.rc.f_rf_constant = 24;x->param.rc.i_rc_method = X264_RC_CRF;// baseline通用性更好,很多播放器对highline支持不好if( x264_param_apply_profile( &x->param, "baseline" ) < 0 ){return -1;}// 申请图片空间if(x264_picture_alloc( &x->pic_in, x->param.i_csp, x->param.i_width, x->param.i_height ) < 0){return -1;}x->h = x264_encoder_open( &x->param );if( !x->h ){x264_picture_clean(&x->pic_in);return -1;}return 0;
}

YUV420编码为h264

编码的过程比较简单,代码如下:

int encoder_x264_encode(encoder_x264_t* x, const char* buffer){int luma_size = x->param.i_width * x->param.i_height;int chroma_size = luma_size / 4;memcpy(x->pic_in.img.plane[0], buffer, luma_size);memcpy(x->pic_in.img.plane[1], buffer+luma_size, chroma_size);memcpy(x->pic_in.img.plane[2], buffer+luma_size+chroma_size, chroma_size);x->pic_in.i_pts = x->iframe++;x->iframe_size = x264_encoder_encode( x->h, &x->nal, &x->inal, &x->pic_in, &x->pic_out );return x->iframe_size;
}

X264资源的释放

void encoder_x264_close(encoder_x264_t* x){/* init函数中已经设置不保留缓冲,所以此处无需调用while( x264_encoder_delayed_frames( x->h ) ){x->iframe_size = x264_encoder_encode( x->h, &x->nal, &x->inal, NULL, &x->pic_out );if(x->iframe_size){printf("rear!!!\n");}}*/x264_encoder_close(x->h);x264_picture_clean(&x->pic_in);
}

MP4V2将H264转为MP4

调用方法

  • 建立一个MP4Encoder对象,参数分别为带路径的mp4文件名、宽、高、帧率
  • 根据场景选择以下两个方法将帧写入MP4文件。 -- writeYuv420Data:该方法先调用encoder_x264_encode编码为H264,然后调用writeH264Data写入MP4文件 -- writeH264Data:将H264帧写入MP4文件
  • 调用close flush文件及释放资源

源代码:

MP4Encoder.h

#ifndef __MP4ENCODER_H_
#define __MP4ENCODER_H_#include "mp4v2/mp4v2.h"
#include "video_encode.h"// NALU单元
typedef struct _MP4ENC_NaluUnit
{int type;int size;unsigned char *data;
}MP4ENC_NaluUnit;typedef struct _MP4ENC_Metadata
{// video, must be h264 typeunsigned int    nSpsLen;unsigned char   Sps[1024];unsigned int    nPpsLen;unsigned char   Pps[1024];} MP4ENC_Metadata,*LPMP4ENC_Metadata;class MP4Encoder
{
public:MP4Encoder();MP4Encoder(const char *pFileName,int width,int height, int fps_num=25, int fps_den=1);public:MP4FileHandle create(const char *fileName,int width,int height, int fps_num=25, int fps_den=1);bool writeH264Meta(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata);/*** 写入YUV420P数据,data长度必须为width*height*1.5*/int writeYuv420Data(const unsigned char* data);/*** 写入h264数据*/int writeH264Data(const unsigned char* data,int size, bool first);void close();static bool praseMeta(const unsigned char* pData,int size,MP4ENC_Metadata &metadata);
private:static int readOneNaluFromBuf(const unsigned char *buffer,unsigned int nBufferSize,unsigned int offSet,MP4ENC_NaluUnit &nalu);
private:int             m_width;int             m_height;int             m_frame_rate;int             m_time_scale;MP4TrackId      m_video_id;unsigned char   m_sps[1024];unsigned char   m_pps[4096];int             m_sps_size;int             m_pps_size;MP4FileHandle   m_file;// x264编码encoder_x264_t  m_encode;
};#endif

MP4Encoder.cpp

#include "MP4Encoder.h"
#include <string.h>#define BUFFER_SIZE  (1024*1024)MP4Encoder::MP4Encoder():m_video_id(NULL),
m_width(0),
m_height(0),
m_time_scale(0),
m_frame_rate(0),
m_sps_size(0),
m_pps_size(0){}MP4Encoder::MP4Encoder(const char *pFileName,int width,int height, int fps_num, int fps_den){m_video_id = NULL;m_file = create(pFileName, width, height, fps_num, fps_den);
}MP4FileHandle MP4Encoder::create(const char *pFileName,int width,int height, int fps_num, int fps_den)
{if(pFileName == NULL){return NULL;}// create mp4 filem_file = MP4Create(pFileName);if (m_file == MP4_INVALID_FILE_HANDLE){fprintf(stderr, "MP4Encoder ERROR: Open file[%s] fialed.\n", pFileName);return NULL;}m_width = width;m_height = height;m_time_scale = 90000;m_frame_rate = fps_num;MP4SetTimeScale(m_file, m_time_scale);if(encoder_x264_init(&m_encode, width, height, fps_num, fps_den)!=0){return NULL;}return m_file;
}bool MP4Encoder::writeH264Meta(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata)
{m_video_id = MP4AddH264VideoTrack(hMp4File,m_time_scale,m_time_scale / m_frame_rate,m_width, // widthm_height,// heightlpMetadata->Sps[1], // sps[1] AVCProfileIndicationlpMetadata->Sps[2], // sps[2] profile_compatlpMetadata->Sps[3], // sps[3] AVCLevelIndication3);           // 4 bytes length before each NAL unitif (m_video_id == MP4_INVALID_TRACK_ID){printf("add video track failed.\n");return false;}MP4SetVideoProfileLevel(hMp4File, 0x01); //  Simple Profile @ Level 3// write spsMP4AddH264SequenceParameterSet(hMp4File,m_video_id,lpMetadata->Sps,lpMetadata->nSpsLen);// write ppsMP4AddH264PictureParameterSet(hMp4File,m_video_id,lpMetadata->Pps,lpMetadata->nPpsLen);return true;
}int MP4Encoder::writeYuv420Data(const unsigned char* data){int size = encoder_x264_encode(&m_encode, (const char*)data);if (size>0) {return writeH264Data(encoder_x264_frame(&m_encode), m_encode.iframe_size, m_encode.iframe==1);}return size;
}int MP4Encoder::writeH264Data(const unsigned char* pData,int size, bool first)
{if(m_file == NULL){return -1;}if(pData == NULL){return -1;}MP4ENC_NaluUnit nalu;int pos = 0, len = 0;printf("INPUT BUFFER SIZE: %d\n", size);while ((len = readOneNaluFromBuf(pData,size,pos,nalu))!=0){printf("pos[%d] len[%d] Nalu: type[%d] size[%d]\n", pos, len, nalu.type, nalu.size);if(first) {if(nalu.type != 0x67) {printf("sps not found!\n");return -1;}// 添加h264 trackm_video_id = MP4AddH264VideoTrack(m_file,m_time_scale,m_time_scale / m_frame_rate,m_width,     // widthm_height,    // heightnalu.data[1], // sps[1] AVCProfileIndicationnalu.data[2], // sps[2] profile_compatnalu.data[3], // sps[3] AVCLevelIndication3);           // 4 bytes length before each NAL unitif (m_video_id == MP4_INVALID_TRACK_ID){printf("add video track failed.\n");return 0;}MP4SetVideoProfileLevel(m_file, 1); //  Simple Profile @ Level 3m_sps_size = m_pps_size = 0;first = false;}if(nalu.type == 0x67) // sps{if(!m_sps_size) {memcpy(m_sps, nalu.data,nalu.size);m_sps_size = nalu.size;}}else if(nalu.type == 0x68) // pps{if(!m_pps_size) {memcpy(m_pps, nalu.data,nalu.size);m_pps_size = nalu.size;}}else{int key = -1;if(nalu.type == 0x65) { // I frameif(!m_sps_size||!m_pps_size) {printf("sps/pps not found!\n");return 0;}MP4AddH264SequenceParameterSet(m_file,m_video_id,m_sps, m_sps_size);MP4AddH264PictureParameterSet (m_file,m_video_id,m_pps, m_pps_size);key = 1;}else if(nalu.type == 0x41) { // P framekey = 0;}if(key >= 0) {int datalen = nalu.size+4;unsigned char *data = new unsigned char[datalen];// MP4 Nalu前四个字节表示Nalu长度data[0] = nalu.size>>24;data[1] = nalu.size>>16;data[2] = nalu.size>>8;data[3] = nalu.size&0xff;memcpy(data+4,nalu.data,nalu.size);int success = MP4WriteSample(m_file, m_video_id, data, datalen,MP4_INVALID_DURATION, 0, key);delete[] data;if(!success){printf("MP4WriteSample() failed!\n");return 0;}}}pos += len;}return pos;
}int MP4Encoder::readOneNaluFromBuf(const unsigned char *buffer,unsigned int nBufferSize,unsigned int offSet,MP4ENC_NaluUnit &nalu)
{int i = offSet+2;if(i < 2)return 0;while(++ i < nBufferSize){if( buffer[i-3] == 0x00 &&buffer[i-2] == 0x00 &&buffer[i-1] == 0x01){int pos = i+3;while(++ pos < nBufferSize){if(   buffer[pos-3] == 0x00 &&buffer[pos-2] == 0x00 &&((0xFE&buffer[pos-1]) == 0x00)){break;}}if(pos >= nBufferSize){nalu.size = nBufferSize-i;}else{nalu.size = pos-3-i;}nalu.type = buffer[i]&0xFF;nalu.data =(unsigned char*)&buffer[i];return (nalu.size+i-offSet);}}return 0;
}void MP4Encoder::close()
{if(m_file){MP4Close(m_file);m_file = NULL;encoder_x264_close(&m_encode);}
}bool MP4Encoder:: praseMeta(const unsigned char* pData,int size,MP4ENC_Metadata &metadata)
{if(pData == NULL || size<4){return false;}MP4ENC_NaluUnit nalu;int pos = 0;bool bRet1 = false,bRet2 = false;while (int len = readOneNaluFromBuf(pData,size,pos,nalu)){if(nalu.type == 0x07){memcpy(metadata.Sps,nalu.data,nalu.size);metadata.nSpsLen = nalu.size;bRet1 = true;}else if((nalu.type == 0x08)){memcpy(metadata.Pps,nalu.data,nalu.size);metadata.nPpsLen = nalu.size;bRet2 = true;}pos += len;}if(bRet1 && bRet2){return true;}return false;
}

x264+mp4v2编码YUV420为mp4相关推荐

  1. 软编码Flv 到Mp4 容器(五) fmp4 ftyp box 和moovmvhd box详解

    https://github.com/332065255/flv2fmp4 代码库 软编码Flv 到Mp4 容器(一) 软编码Flv 到Mp4 容器(二) flv tag拆解 软编码Flv 到Mp4 ...

  2. 软编码Flv 到Mp4 容器(十三) fmp4 生成ftyp和moov所必要的 flv数据

    https://github.com/332065255/flv2fmp4 代码库 软编码Flv 到Mp4 容器(一) 软编码Flv 到Mp4 容器(二) flv tag拆解 软编码Flv 到Mp4 ...

  3. H264编码 封装成MP4格式 视频流 RTP封包

    From:http://www.cnblogs.com/ghw-NO1/archive/2012/08/28/2660848.html 一.概述 本文讲述的是对H264编码且封装成MP4格式的视频流进 ...

  4. linux mp4v2编译,Android 编译mp4 v2 2.0.0生成动态库

    6.最后在jni目录下的终端中输入ndk-build,结果如下: root@zhangjie:/home/zhangjie/mp4v2-2.0.0/jni# ndk-build Android NDK ...

  5. x264 2pass编码说明

    1 操作方法 ./x264 tt.yuv --input-res 576x1024 --bitrate 1000 --pass 1 --vbv-adapt 1 -o tt_out.264 ./x264 ...

  6. FFMPEG音视频同步-音频实时采集编码封装成MP4

    https://blog.csdn.net/quange_style/article/details/90083173

  7. 缆索护栏cad_干燥的rb可以提供更好的护栏服务

    缆索护栏cad The idea of using service objects came into my mind when I first read about the DCI architec ...

  8. x264编码指南——码率控制

    x264是一个 H.264/MPEG4 AVC 编码器,本指南将指导新手如何创建高质量的H.264视频. 对于普通用户通常有两种码率控制模式:crf(Constant Rate Factor)和Two ...

  9. ffmpeg与x264编码指南

    x264是一个 H.264/MPEG4 AVC 编码器,本指南将指导新手如何创建高质量的H.264视频. 对于普通用户通常有两种码率控制模式:crf(Constant Rate Factor)和Two ...

最新文章

  1. shadow fight 1.6.0 内购
  2. 会计的思考(3):通过公司例会制度加强财务管理职能
  3. oracle bbed 使用,Oracle BBED使用 四步快速启动Oracle BBED
  4. 前端学习(1395):多人管理项目15建立请求
  5. SLF4JLoggerContext cannot be cast to LoggerContext
  6. three.js glb 多个_奔驰GLB外观完美!内饰酷炫!动力倍儿棒
  7. Zephyr下计算耗时
  8. IPv6 gre隧道、路由协议综合实验(华为设备)
  9. vue监听数组元素属性的变化_为什么Vue3.0不再使用defineProperty实现数据监听?
  10. rust : rustup切换stable、nightly
  11. STM32-GPRS模块连接系统主站
  12. 深圳市计算机软件著作权资助,深圳市计算机软件著作权登记资助管理实施细则...
  13. 阿里巴巴sentinel限流
  14. 洗料系列-编程语言专题-Java 19【线程×,协程√】
  15. 嵌入式设备开发专题《NB-IoT开发,解读SIM卡、USIM卡、UICC卡、eSIM卡的区别》
  16. 「游戏建模」如何使用zbrush为模型制作衣服上的褶皱
  17. 计算机基础及excel,S102-计算机基础(06)-EXCEL简介及基本操作
  18. 根据简化真值表绘制电路
  19. 图标设计五条黄金法则
  20. 失无所失的伤感空间日志分享:我会一直这样爱着你,心甘情愿

热门文章

  1. 【不忘初心,坚持梦想】云和恩墨电子期刊2017第9期
  2. 超类(superclass)
  3. 解决OpenCV不可以读取中文路径问题
  4. ftp服务器的安装原理,ftp原理及配置笔记
  5. 凤阳中学2021高考成绩查询,安徽各地市2021年高考报名人数、考点信息公布!
  6. 物联网芯片的协议之Zigbee及其调制
  7. (附源码)计算机毕业设计SSM中小型医院综合管理系统
  8. PostgreSQL-模糊查询
  9. 2023年,3D视觉算法岗求职难吗?
  10. [激光器原理与应用-6]:Q开关元件与Q驱动电路板