mediacodec mp4v2应用
来源:互联网 发布:dbc2000 传奇数据库表 编辑:程序博客网 时间:2024/06/11 05:54
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 I420 COLOR_FormatYUV420SemiPlanar: YUV420SP NV12 YUV420P,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 =>YUV420P YV12: YYYYYYYY VV UU =>YUV420P NV12: YYYYYYYY UVUV =>YUV420SP NV21: 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 data public 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 MPEG4IP if (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 { // track m_videoId = MP4AddH264VideoTrack (hMp4File, m_nTimeScale, m_nTimeScale / m_nFrameRate, m_nWidth, // width m_nHeight, // height nalu.data[1], // sps[1] AVCProfileIndication nalu.data[2], // sps[2] profile_compat nalu.data[3], // sps[3] AVCLevelIndication 3); // 4 bytes length before each NAL unit if (m_videoId == MP4_INVALID_TRACK_ID) { printf("add video track failed.\n"); //MP4Close(mMp4File, 0); return 0; } MP4SetVideoProfileLevel(hMp4File, 1); // Simple Profile @ Level 3 MP4AddH264SequenceParameterSet(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 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); 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 file MP4FileHandle 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: Null 1: AAC Main 2: 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 1024 if(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 { // track m_videoId = MP4AddH264VideoTrack(fileHandle, m_nTimeScale, m_nTimeScale / m_nFrameRate, m_nWidth, // width m_nHeight, // height buf[5], // sps[1] AVCProfileIndication buf[6], // sps[2] profile_compat buf[7], // sps[3] AVCLevelIndication 3); // 4 bytes length before each NAL unit if (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 = 2 MP4AddH264SequenceParameterSet(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 keyframe if(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 :=
LOCAL_MODULE := mp4v2
LOCAL_SRC_FILES := libmp4v2.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_C_INCLUDES += \
LOCAL_SHARED_LIBRARIES := mp4v2
LOCAL_MODULE := mp
LOCAL_SRC_FILES := mp.c
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
0 0
- mediacodec mp4v2应用
- live555 mediacodec应用
- live555 server mediacodec应用
- mp4v2
- MP4V2
- MP4V2
- Mediacodec
- MediaCodec
- MediaCodec storeMetaDataInBuffers应用自己分配buffer设给编码器
- 编译MP4v2
- mp4v2接口
- 编译MP4v2
- 编译MP4v2
- MP4v2 Android
- MediaCodec Sample
- MediaCodec Demo
- MediaCodec类
- MediaCodec类
- javaScript调试(debugger;)
- Storm集成Kafka
- HTML5 拖拽
- SpringMVC
- MFC——基本概念的通俗理解1
- mediacodec mp4v2应用
- iOS性能优化:Instruments使用实战
- Rxjava与Rxandroid结合实现网络请求
- 【Android】不同的View引起软键盘无法关闭问题解决方案
- java线程&&并发-实践笔记
- JNDI详细配置及使用
- 又一段旅程
- bitmap 图像压缩方法
- 575. Distribute Candies