mediacodec + mp4v2

demo: https://github.com/wangzuxing/MyMp4V2H264

java端的camera preview data经mediacodec编码,送jni端, jni调用libmp4v2库相关函数处理、写入.mp4视频文件

Java端:
1、
MainActivity0

    static {System.loadLibrary("mp4v2"); //libmp4v2  .mp4视频文件封装库(即可以把.aac和.264文件封装成.mp4格式的视频文件的函数库)System.loadLibrary("mp");}public native boolean Mp4Start(String pcm); // 传入需创建的.mp4文件路径public native void Mp4PackV(byte[] array, int length, int keyframe); // mediacodec编码器输出的sps pps nalu数据传入jni端public native void Mp4PackA(byte[] array, int length);public native void Mp4End(); ///*packed formats:将Y、U、V值储存成Macro Pixels阵列,和RGB的存放方式类似。planar formats:将Y、U、V的三个份量分别存放在不同的矩阵中。COLOR_FormatYUV420Planar:    YUV420P I420COLOR_FormatYUV420SemiPlanar:   YUV420SP NV12YUV420P,Y,U,V三个分量都是平面格式,分为I420和YV12。I420格式和YV12格式的不同处在U平面和V平面的位置不同。在I420格式中,U平面紧跟在Y平面之后,然后才是V平面(即:YUV);但YV12则是相反(即:YVU)。YUV420SP, Y分量平面格式,UV打包格式, 即NV12。 NV12与NV21类似,U 和 V 交错排列,不同在于UV顺序。I420: YYYYYYYY UU VV    =>YUV420PYV12: YYYYYYYY VV UU    =>YUV420PNV12: YYYYYYYY UVUV     =>YUV420SPNV21: YYYYYYYY VUVU     =>YUV420SP*///yv12 =》 yuv420p : yvu -> yuv  private void swapYV12toI420(byte[] yv12bytes, byte[] i420bytes, int width, int height)   {        System.arraycopy(yv12bytes, 0, i420bytes, 0,width*height);  System.arraycopy(yv12bytes, width*height+width*height/4, i420bytes, width*height,width*height/4);  System.arraycopy(yv12bytes, width*height, i420bytes, width*height+width*height/4,width*height/4);    } //编码camera preview datapublic void onFrame(byte[] buf, int length) {   swapYV12toI420(buf, h264, width, height); // H.264编码器只支持YUV视频格式输入ByteBuffer[] inputBuffers = mediaCodec0.getInputBuffers();ByteBuffer[] outputBuffers = mediaCodec0.getOutputBuffers();int inputBufferIndex = mediaCodec0.dequeueInputBuffer(-1);if (inputBufferIndex >= 0) {ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];inputBuffer.clear();inputBuffer.put(h264, 0, length);mediaCodec0.queueInputBuffer(inputBufferIndex, 0, length, 0, 0);}MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();int outputBufferIndex = mediaCodec0.dequeueOutputBuffer(bufferInfo,0);while (outputBufferIndex >= 0) {ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];byte[] outData = new byte[bufferInfo.size];outputBuffer.get(outData);if(mp4fFlag){if(outData.length==21){ Log.i("Encoder", "--------- pps sps set---------");/*不同的camera width、height、fps等参数,编码其输出的sps pps不同,需实测而定,若想暂停然后新录制.MP4文件,则需保存该sps pps参数,直接在首个IDR帧前写入:sps pps idr*///int length = outData.length;for (int ix = 0; ix < 21; ++ix) {System.out.printf("%02x ", outData[ix]);}System.out.println("\n----------");//00 00 00 01 67 42 80 1e e9 01 40 7b 20 00 00 00 01 68 ce 06 e2 byte[] outData0 = new byte[13]; byte[] outData1 = new byte[8]; System.arraycopy(outData, 0,  outData0, 0, 13);  System.arraycopy(outData, 13, outData1, 0, 8); Mp4PackV(outData0, 13, keyFrame);Mp4PackV(outData1, 8, keyFrame);}else{if(outData[4] == 0x65) //key frame 0x65{ keyFrame = 1;}Mp4PackV(outData, outData.length, keyFrame);}}mediaCodec0.releaseOutputBuffer(outputBufferIndex, false);outputBufferIndex = mediaCodec0.dequeueOutputBuffer(bufferInfo, 0);}}  

JNI端:
2、mp.c

uint8_t MP4AdtsFindSamplingRateIndex(uint32_t samplingRate)
{uint8_t i;for(i = 0; i < NUM_ADTS_SAMPLING_RATES; i++) {if (samplingRate == AdtsSamplingRates[i]) {return i;}}return NUM_ADTS_SAMPLING_RATES - 1;
}bool MP4AacGetConfiguration(uint8_t** ppConfig,uint32_t* pConfigLength,uint8_t profile,uint32_t samplingRate,uint8_t channels)
{/* create the appropriate decoder config */uint8_t* pConfig = (uint8_t*)malloc(2);if (pConfig == NULL) {return false;}uint8_t samplingRateIndex = MP4AdtsFindSamplingRateIndex(samplingRate);pConfig[0] = ((profile + 1) << 3) | ((samplingRateIndex & 0xe) >> 1);pConfig[1] = ((samplingRateIndex & 0x1) << 7) | (channels << 3);/* LATER this option is not currently used in MPEG4IPif (samplesPerFrame == 960) {pConfig[1] |= (1 << 2);}*/*ppConfig = pConfig;*pConfigLength = 2;return true;
}int ReadOneNaluFromBuf(const unsigned char *buffer,unsigned int nBufferSize,unsigned int offSet,MP4ENC_NaluUnit *nalu)
{int i = offSet;while(i<nBufferSize){if(buffer[i++] == 0x00 &&buffer[i++] == 0x00 &&buffer[i++] == 0x00 &&buffer[i++] == 0x01){int pos = i;while (pos<nBufferSize){if(buffer[pos++] == 0x00 &&buffer[pos++] == 0x00 &&buffer[pos++] == 0x00 &&buffer[pos++] == 0x01){break;}}if(pos == nBufferSize){nalu->size = pos-i;}else{nalu->size = (pos-4)-i;}nalu->type = buffer[i]&0x1f;nalu->data =(unsigned char*)&buffer[i];LOGI("     nalu type = %d, size = %d    ", nalu->type, nalu->size);return (nalu->size+i-offSet);}}return 0;
}int WriteH264Data(MP4FileHandle hMp4File,const unsigned char* pData,int size)
{if(hMp4File == NULL){return -1;}if(pData == NULL){return -1;}MP4ENC_NaluUnit nalu;int pos = 0, len = 0;while (len = ReadOneNaluFromBuf(pData, size, pos, &nalu)){if(nalu.type == 0x07) // sps{// trackm_videoId = MP4AddH264VideoTrack(hMp4File,m_nTimeScale,m_nTimeScale / m_nFrameRate,m_nWidth,     // widthm_nHeight,    // 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_videoId == MP4_INVALID_TRACK_ID){printf("add video track failed.\n");//MP4Close(mMp4File, 0);return 0;}MP4SetVideoProfileLevel(hMp4File, 1); //  Simple Profile @ Level 3MP4AddH264SequenceParameterSet(hMp4File,m_videoId,nalu.data,nalu.size);LOGI("              write sps                ");}else if(nalu.type == 0x08) // pps{MP4AddH264PictureParameterSet(hMp4File,m_videoId,nalu.data,nalu.size);LOGI("              write pps                ");}else{int datalen = nalu.size+4;unsigned char data[datalen];// MP4 Naludata[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);if(!MP4WriteSample(hMp4File, m_videoId, data, datalen,MP4_INVALID_DURATION, 0, 1)){LOGI("              MP4_INVALID_TRACK_ID = %d               ",m_samplesWritten);// MP4DeleteTrack(mMp4File, video);return 0;}}pos += len;}return pos;
}MP4FileHandle CreateMP4File(const char *pFileName,int width,int height)
{if(pFileName == NULL){return false;}// create mp4 fileMP4FileHandle hMp4file = MP4Create(pFileName, 0);if (hMp4file == MP4_INVALID_FILE_HANDLE){//printf("ERROR:Open file fialed.\n");LOGI("              MP4_INVALID_FILE_HANDLE                ");return false;}m_nWidth  = width;m_nHeight = height;m_nTimeScale = 90000;m_nFrameRate = 15;MP4SetTimeScale(hMp4file, m_nTimeScale);return hMp4file;
}void CloseMP4File(MP4FileHandle hMp4File)
{if(hMp4File){MP4Close(hMp4File,0);hMp4File = NULL;}
}//直接打包h.264文件为.mp4文件
bool WriteH264File(const char* pFile264,const char* pFileMp4)
{if(pFile264 == NULL || pFileMp4 == NULL){return false;}MP4FileHandle hMp4File = CreateMP4File(pFileMp4, 640, 480);//240,320);if(hMp4File == NULL){//printf("ERROR:Create file failed!");LOGI("              MP4_INVALID_FILE_HANDLE                ");return false;}FILE *fp = fopen(pFile264, "rb");if(!fp){//printf("ERROR:open file failed!");LOGI("              h264 fopen error                ");return false;}LOGI("              h264 fopen                 ");fseek(fp, 0, SEEK_SET);unsigned char buffer[BUFFER_SIZE];int pos = 0;LOGI("       mp4Encoder start %s      ",pFile264);while(1){int readlen = fread(buffer+pos, sizeof(unsigned char), BUFFER_SIZE-pos, fp);if(readlen<=0){break;}readlen += pos;int writelen = 0;int i;for(i = readlen-1; i>=0; i--){if(buffer[i--] == 0x01 &&buffer[i--] == 0x00 &&buffer[i--] == 0x00 &&buffer[i--] == 0x00){writelen = i+5;break;}}LOGI("          mp4Encoder writelen = %d     ",writelen);writelen = WriteH264Data(hMp4File,buffer,writelen);if(writelen<=0){break;}memcpy(buffer,buffer+writelen,readlen-writelen+1);pos = readlen-writelen+1;}fclose(fp);CloseMP4File(hMp4File);LOGI("              mp4Encoder end                ");LOGI("              mp4Encoder end                ");return true;
}JNIEXPORT bool JNICALL Java_com_example_mymp4v2h264_MainActivity0_Mp4Start(JNIEnv *env, jclass clz, jstring mp4)
{const char* mp4_title = (*env)->GetStringUTFChars(env,mp4, NULL);if(mp4_title == NULL){return false;}//video width=640, height=480, 若需要可以改写MainActivity0.java的Mp4Start(..., width, height)参数表,传入编码视频的高、宽fileHandle = CreateMP4File(mp4_title, 640, 480);if(fileHandle == NULL){//printf("ERROR:Create file failed!");LOGI("              MP4_INVALID_FILE_HANDLE NULL             ");(*env)->ReleaseStringUTFChars(env, mp4, mp4_title);return false;}uint32_t samplesPerSecond;uint8_t profile;uint8_t channelConfig;samplesPerSecond = 44100;profile = 2; // AAC LC/*0: Null1: AAC Main2: AAC LC (Low Complexity)3: AAC SSR (Scalable Sample Rate)4: AAC LTP (Long Term Prediction)5: SBR (Spectral Band Replication)6: AAC Scalable*/channelConfig = 1;uint8_t* pConfig = NULL;uint32_t configLength = 0;//m_audio = MP4AddAudioTrack(m_file, 44100, 1024, MP4_MPEG2_AAC_MAIN_AUDIO_TYPE );audio = MP4AddAudioTrack(fileHandle, 44100, 1024, MP4_MPEG2_AAC_LC_AUDIO_TYPE);//MP4_MPEG4_AUDIO_TYPE);//MP4_MPEG2_AAC_LC_AUDIO_TYPE//MP4_MPEG2_AAC_LC_AUDIO_TYPE);//16000 1024if(audio == MP4_INVALID_TRACK_ID){MP4Close(fileHandle, 0);return false;}MP4SetAudioProfileLevel(fileHandle, 0x02);LOGI("              MP4AddAudioTrack ok                ");MP4AacGetConfiguration(&pConfig, &configLength, profile, samplesPerSecond, channelConfig);//free(pConfig);MP4SetTrackESConfiguration(fileHandle, audio, pConfig, configLength);(*env)->ReleaseStringUTFChars(env, mp4, mp4_title);return true;
}//添加视频帧的方法
JNIEXPORT void JNICALL Java_com_example_mymp4v2h264_MainActivity0_Mp4PackV
(JNIEnv *env, jclass clz, jbyteArray data, jint size, jint keyframe)
{unsigned char *buf = (unsigned char *)(*env)->GetByteArrayElements(env, data, JNI_FALSE);unsigned char type;type = buf[4]&0x1f;//LOGI(" 0x%x 0x%x 0x%x 0x%x 0x%x ",buf[0],buf[1],buf[2],buf[3], type);if(type == 0x07) // sps{// trackm_videoId = MP4AddH264VideoTrack(fileHandle,m_nTimeScale,m_nTimeScale / m_nFrameRate,m_nWidth,     // widthm_nHeight,    // heightbuf[5], // sps[1] AVCProfileIndicationbuf[6], // sps[2] profile_compatbuf[7], // sps[3] AVCLevelIndication3);           // 4 bytes length before each NAL unitif (m_videoId == MP4_INVALID_TRACK_ID){printf("add video track failed.\n");//MP4Close(mMp4File, 0);//return 0;}else{MP4SetVideoProfileLevel(fileHandle, 0x7F); //  Simple Profile @ Level 3 = 2MP4AddH264SequenceParameterSet(fileHandle, m_videoId, &buf[4], size-4);LOGI("              write sps                ");}}else if(type == 0x08) // pps{MP4AddH264PictureParameterSet(fileHandle, m_videoId, &buf[4], size-4);m_samplesWritten = 0;m_lastTime = 0;LOGI("              write pps                ");}else{int nalsize = size-4;bool ret = false;/*buf[0] = (nalsize >> 24) & 0xff;buf[1] = (nalsize >> 16) & 0xff;buf[2] = (nalsize >> 8)& 0xff;buf[3] =  nalsize & 0xff;*///LOGI(" 0x%02x 0x%02x 0x%02x 0x%02x %d ", buf[0],buf[1],buf[2],buf[3],nalsize);buf[0] = (nalsize&0xff000000)>>24;buf[1] = (nalsize&0x00ff0000)>>16;buf[2] = (nalsize&0x0000ff00)>>8;buf[3] = nalsize&0x000000ff;/*// method 2 时间戳计算方式m_samplesWritten++;double thiscalc;thiscalc = m_samplesWritten;thiscalc *= m_nTimeScale;thiscalc /= m_nFrameRate;m_thisTime = (MP4Duration)thiscalc;MP4Duration dur;dur = m_thisTime - m_lastTime;*///ret = MP4WriteSample(fileHandle, video, buf, size, dur, 0, keyframe); //MP4_INVALID_DURATION keyframeif(keyframe){LOGI("       type = %d, size = %d, %d       ",type, size, keyframe);}ret = MP4WriteSample(fileHandle, m_videoId, buf, size, MP4_INVALID_DURATION, 0, keyframe);//method 2//ret = MP4WriteSample(fileHandle, m_videoId, buf, size, dur, 0, keyframe);//m_lastTime = m_thisTime;if(!ret){//fprintf(stderr,   "can't write video frame %u\n", m_samplesWritten );LOGI("              MP4_INVALID_TRACK_ID = %d               ",ret);//MP4DeleteTrack(fileHandle, m_videoId);//return MP4_INVALID_TRACK_ID;}}(*env)->ReleaseByteArrayElements(env, data, (jbyte *)buf, 0);
}//添加音频帧的方法
JNIEXPORT void JNICALL Java_com_example_mymp4v2h264_MainActivity0_Mp4PackA
(JNIEnv *env, jclass clz, jbyteArray data, jint size)
{uint8_t *bufaudio = (uint8_t *)(*env)->GetByteArrayElements(env, data, JNI_FALSE);//LOGI("       Mp4PackA = %d       ", size);//MP4WriteSample(fileHandle, audio, &bufaudio[7], size-7);MP4WriteSample(fileHandle, audio, &bufaudio[7], size-7, MP4_INVALID_DURATION, 0, 1);/*bool MP4WriteSample(MP4FileHandle hFile,MP4TrackId trackId,const u_int8_t* pBytes,u_int32_t numBytes,MP4Duration duration DEFAULT(MP4_INVALID_DURATION),MP4Duration renderingOffset DEFAULT(0),bool isSyncSample DEFAULT(true));*///减去7为了删除adts头部的7个字节(*env)->ReleaseByteArrayElements(env, data, (jbyte *)bufaudio, 0);
}//视频录制结束调用
JNIEXPORT void JNICALL Java_com_example_mymp4v2h264_MainActivity0_Mp4End
(JNIEnv *env, jclass clz)
{MP4Close(fileHandle, 0);fileHandle = NULL;LOGI("              mp4close              ");LOGI("              mp4close              ");
}

3、Android.mk

LOCAL_PATH := (callmy−dir)include(call my-dir) include (CLEAR_VARS)
LOCAL_MODULE := mp4v2
LOCAL_SRC_FILES := libmp4v2.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_C_INCLUDES += \
(LOCALPATH) (LOCAL_PATH)\ (LOCAL_PATH)/mp4v2

LOCAL_SHARED_LIBRARIES := mp4v2
LOCAL_MODULE := mp
LOCAL_SRC_FILES := mp.c
LOCAL_LDLIBS += -llog

include $(BUILD_SHARED_LIBRARY)

mediacodec mp4v2应用相关推荐

  1. Android使用Mp4v2用h264流和aac流合成mp4

    H264/AAC实时流 录制成MP4格式的本地视频 GITHUB: https://github.com/chezi008/mp4muxer 建议使用场景 一般视频流有如下两种途径获取: Androi ...

  2. MediaCodeC解码视频指定帧,迅捷、精确

    原创文章,转载请联系作者 若待明朝风雨过,人在天涯!春在天涯 原文地址 提要 最近在整理硬编码MediaCodec相关的学习笔记,以及代码文档,分享出来以供参考.本人水平有限,项目难免有思虑不当之处, ...

  3. MediaCodec 解码后数据对齐导致的绿边问题

    前言 本文从简书迁移,原文地址:www.jianshu.com/p/ac53e9595- Android 使用 MediaCodec 解码 h264 数据后会有个数据对齐的问题. 简单说就是 Medi ...

  4. 研究Android音视频-3-在Android设备上采集音视频并使用MediaCodec编码为H.264

    原文 : https://juejin.cn/post/69601302052266311754 本文解决的问题 本文主要使用 MediaCodec 硬编码器对 Android 设备采集的音视频编码 ...

  5. MediaCodec问题汇总

    参考:http://blog.csdn.net/mincheat/article/details/51385144 MediaCodec的基本用法,网上一大把,这里就不写了 1, 获取支持分辨率问题 ...

  6. Android平台MediaCodec避坑指北

    https://www.jianshu.com/p/5d62a3cf0741 最近使用MediaCodec做编解码H264,写一点东西以免自己再次掉坑. 先说一下具体环境,使用的是,Windows10 ...

  7. MediaCodec在Android视频硬解码组件的应用

    https://yq.aliyun.com/articles/632892 云栖社区> 博客列表> 正文 MediaCodec在Android视频硬解码组件的应用 cheenc 2018- ...

  8. 使用MediaCodeC将图片集编码为视频

    原文地址 原创文章,转载请联系作者 绿生莺啼春正浓,钗头青杏小,绿成丛. 玉船风动酒鳞红.歌声咽,相见几时重? 提要 这是MediaCodeC系列的第三章,主题是如何使用MediaCodeC将图片集编 ...

  9. mp4v2再学习 -- H264视频编码成MP4文件

    一.H264视频编码成MP4文件 参看:H264视频编码成MP4文件 参看:mp4v2在VS2010下的编译与在项目中的使用 最近做项目需要将H264文件封装为mp4文件,从网上找到了MP4V2库,下 ...

最新文章

  1. 学习软件测试需要掌握哪些内容?这篇文章告诉你
  2. github搜索技巧:快速搜到你想要的!
  3. 搭建LVS_DR模型
  4. vscode 编辑器快捷键
  5. C++函数如何操作堆栈指针esp
  6. android 自动化测试之monkeyrunner学习(三),自动化测试之Monkeyrunner
  7. 2016软考网络规划设计师论文写作
  8. springboot如何使用外部tomcat容器
  9. 漫画:“排序算法” 大总结
  10. 解决文件不显示后缀名的问题
  11. android iphone 记事本,手机上用什么记事本软件好?iPhone求推荐一款便签记事本app...
  12. 桌面便签软件哪个比较好 好用的手机桌面便签软件推荐
  13. php 文章页面阅读全文,纯代码为WordPress文章内容页增加阅读全文展开功能
  14. 除了缓存,浏览器还有哪些存储数据的方式?
  15. 教你如何用Canvas绘制整身的哆啦A梦
  16. 我是如何用最简单的前端技术揭示那些灰色产业背后的原理
  17. 人工智能可以像人类一样学习吗?
  18. PFM与PWM的技术总结
  19. 年底了,经理们忙着做绩效评价
  20. android 微信6.2.0 AndroidManifest.xml

热门文章

  1. C++程序启动时报“0xC000007B”无法启动的问题排查
  2. 狂神 Linux 学习笔记
  3. 阿ken的HTML、CSS的学习笔记_CSS3选择器(笔记四)
  4. 窗口特征(Window Features)
  5. 尚邮——Wopus周六聚会大图分享(2009-12-28 11:50:01)
  6. Linux网络-UDP/TCP协议详解
  7. barbie黄佳丽--华侨大学
  8. r7 5800h 怎么样 相当于什么水平
  9. java noi和io
  10. 高人、天才、牛逼…从柳传志到雷军、马云,几乎没人不服任正非!