mediacodec mp4v2应用
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应用相关推荐
- Android使用Mp4v2用h264流和aac流合成mp4
H264/AAC实时流 录制成MP4格式的本地视频 GITHUB: https://github.com/chezi008/mp4muxer 建议使用场景 一般视频流有如下两种途径获取: Androi ...
- MediaCodeC解码视频指定帧,迅捷、精确
原创文章,转载请联系作者 若待明朝风雨过,人在天涯!春在天涯 原文地址 提要 最近在整理硬编码MediaCodec相关的学习笔记,以及代码文档,分享出来以供参考.本人水平有限,项目难免有思虑不当之处, ...
- MediaCodec 解码后数据对齐导致的绿边问题
前言 本文从简书迁移,原文地址:www.jianshu.com/p/ac53e9595- Android 使用 MediaCodec 解码 h264 数据后会有个数据对齐的问题. 简单说就是 Medi ...
- 研究Android音视频-3-在Android设备上采集音视频并使用MediaCodec编码为H.264
原文 : https://juejin.cn/post/69601302052266311754 本文解决的问题 本文主要使用 MediaCodec 硬编码器对 Android 设备采集的音视频编码 ...
- MediaCodec问题汇总
参考:http://blog.csdn.net/mincheat/article/details/51385144 MediaCodec的基本用法,网上一大把,这里就不写了 1, 获取支持分辨率问题 ...
- Android平台MediaCodec避坑指北
https://www.jianshu.com/p/5d62a3cf0741 最近使用MediaCodec做编解码H264,写一点东西以免自己再次掉坑. 先说一下具体环境,使用的是,Windows10 ...
- MediaCodec在Android视频硬解码组件的应用
https://yq.aliyun.com/articles/632892 云栖社区> 博客列表> 正文 MediaCodec在Android视频硬解码组件的应用 cheenc 2018- ...
- 使用MediaCodeC将图片集编码为视频
原文地址 原创文章,转载请联系作者 绿生莺啼春正浓,钗头青杏小,绿成丛. 玉船风动酒鳞红.歌声咽,相见几时重? 提要 这是MediaCodeC系列的第三章,主题是如何使用MediaCodeC将图片集编 ...
- mp4v2再学习 -- H264视频编码成MP4文件
一.H264视频编码成MP4文件 参看:H264视频编码成MP4文件 参看:mp4v2在VS2010下的编译与在项目中的使用 最近做项目需要将H264文件封装为mp4文件,从网上找到了MP4V2库,下 ...
最新文章
- 学习软件测试需要掌握哪些内容?这篇文章告诉你
- github搜索技巧:快速搜到你想要的!
- 搭建LVS_DR模型
- vscode 编辑器快捷键
- C++函数如何操作堆栈指针esp
- android 自动化测试之monkeyrunner学习(三),自动化测试之Monkeyrunner
- 2016软考网络规划设计师论文写作
- springboot如何使用外部tomcat容器
- 漫画:“排序算法” 大总结
- 解决文件不显示后缀名的问题
- android iphone 记事本,手机上用什么记事本软件好?iPhone求推荐一款便签记事本app...
- 桌面便签软件哪个比较好 好用的手机桌面便签软件推荐
- php 文章页面阅读全文,纯代码为WordPress文章内容页增加阅读全文展开功能
- 除了缓存,浏览器还有哪些存储数据的方式?
- 教你如何用Canvas绘制整身的哆啦A梦
- 我是如何用最简单的前端技术揭示那些灰色产业背后的原理
- 人工智能可以像人类一样学习吗?
- PFM与PWM的技术总结
- 年底了,经理们忙着做绩效评价
- android 微信6.2.0 AndroidManifest.xml
热门文章
- C++程序启动时报“0xC000007B”无法启动的问题排查
- 狂神 Linux 学习笔记
- 阿ken的HTML、CSS的学习笔记_CSS3选择器(笔记四)
- 窗口特征(Window Features)
- 尚邮——Wopus周六聚会大图分享(2009-12-28 11:50:01)
- Linux网络-UDP/TCP协议详解
- barbie黄佳丽--华侨大学
- r7 5800h 怎么样 相当于什么水平
- java noi和io
- 高人、天才、牛逼…从柳传志到雷军、马云,几乎没人不服任正非!