live555 mediacodec应用
来源:互联网 发布:sql三大范式 编辑:程序博客网 时间:2024/06/09 19:20
live555 + MediaCodec
demo: https://github.com/wangzuxing/MyLive555Mp4
jni端live555接收视频数据(改写testRTSPClient.cpp客户端程序实现rtsp视频数据的接收, 处理,处理后的数据可写入.264文档,作测试验证),通过反射调用java端的pushBuf函数,把接收处理后的nalu推入java端的缓存列表中,然后由java端调用mediacodec进行解码、显示
JAVA端:
MainActivity0:
static { ... System.loadLibrary("live555"); //liblive555 System.loadLibrary("rtspclient"); } public native void RtspClient(String program, String sdp); //program: live555接收视频数据写入文档的路径,sdp:视频服务器端的rtsp地址 public native void RtspEnd(); private MediaCodec mCodec; public void initDecoder() { mCodec = MediaCodec.createDecoderByType(MIME_TYPE); //MIME_TYPE = “video/avc” MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE, 352, 288); mCodec.configure(mediaFormat, surface, null, 0); mCodec.start(); } //MediaCodec同步解码 public boolean onFrame(byte[] buf, int length) { // Get input buffer index ByteBuffer[] inputBuffers = mCodec.getInputBuffers(); int inputBufferIndex = mCodec.dequeueInputBuffer(5); // 4 data buffer if (inputBufferIndex >= 0) { ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; inputBuffer.clear(); inputBuffer.put(buf, 0, length); mCodec.queueInputBuffer(inputBufferIndex, 0, length, mCount*TIME_INTERNAL, 0); // 添加pts,便于正常显示,TIME_INTERNAL = 25 fps mCount++; } else { return false; } // Get output buffer index MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); int outputBufferIndex = mCodec.dequeueOutputBuffer(bufferInfo, 100); while (outputBufferIndex >= 0) { mCodec.releaseOutputBuffer(outputBufferIndex, true); //绑定surface显示 outputBufferIndex = mCodec.dequeueOutputBuffer(bufferInfo, 0); } return true; } startBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v){ // Do something in response to button click //访问的rtsp sdp地址 final String sdppath = "rtsp://218.204.223.237:554/live/1/67A7572844E51A64/f68g2mj7wjua3la7.sdp"; //"rtsp://218.204.223.237:554/live/1/67A7572844E51A64/f68g2mj7wjua3la7.sdp"; if(!istarted) { received_count = 0; received_Flag = true; mList.clear(); close(); initDecoder(); // 初始化video/avc 解码器 istarted = true; MainActivity0.istarted0 = false; //建立写入文件 final File f = new File(Environment.getExternalStorageDirectory(), "rtsp0.h264"); try { if(!f.exists()){ f.createNewFile(); }else{ if(f.delete()){ f.createNewFile(); } } } catch (IOException e) { e.printStackTrace(); } new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub RtspClient(f.getPath(), sdppath); // 传入接收数据写入文件的地址,rtsp sdp地址 } }).start(); //开启解码显示线程 readFileThread = new Thread(readFile); readFileThread.start(); } } }); stopBtn = (Button) findViewById(R.id.conver_btn0); stopBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v){ // Do something in response to button click if(istarted){ istarted = false; RtspEnd(); readFileThread.interrupt(); close(); } } }); //live555接收视频的nalu数据存储列表 public static ArrayList<ByteBuffer> mList = new ArrayList<ByteBuffer>(); private int received_count; public void pushBuf(byte[] buf,int len) { ByteBuffer buffer = ByteBuffer.allocate(len); buffer.put(buf,0,len); mList.add(buffer); } // Runnable readFile = new Runnable() { @Override public void run() { boolean readFlag = true; boolean readFlag0 = true; boolean flag = false; byte[] buffer; int video_count = 0; while (!Thread.interrupted() && readFlag) { while(mList.size() > 0){ ByteBuffer sendBuf = mList.get(0); buffer = sendBuf.array(); if(readFlag0){ if(buffer.length>5 && buffer[4]==0x67){ // sps flag = onFrame(buffer,buffer.length); // 初始化解码器参数 if(flag) { mList.remove(0); video_count++; } } if(buffer.length>5 && buffer[4]==0x68){ // pps flag = onFrame(buffer, buffer.length); // 初始化解码器参数 if(flag) { mList.remove(0); video_count++; } readFlag0 = false; } }else{ if(buffer.length>5 ){ // 有效Nalu flag = onFrame(buffer, buffer.length); // 解码显示 mList.remove(0); } } try { Thread.sleep(TIME_INTERNAL); } catch (InterruptedException e) { readFlag = false; } } try { Thread.sleep(TIME_INTERNAL); } catch (InterruptedException e) { readFlag = false; } } } };
JNI端:
testRTSPClient0.cpp
//DummySink 的afterGettingFrame函数是sink端接收到完成一帧后端回调,则此处理接收到的数据即可
void DummySink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime, unsigned durationInMicroseconds) { // We've just received a frame of data. (Optionally) print out information about it: int size = 0; unsigned char *buffer=NULL; if(runningFlag==1){ if(fReceiveBuffer[0] == 0x65){ LOGI(" sps pps start"); runningFlag = 0; if(methodid == 0) { //FindClass函数用于查找java类,参数为类的包名形式 clazz = _env->FindClass("com/example/mylive555mp4/MainActivity0"); if (clazz == 0) { LOGI("can't find clazz"); }else{ LOGI("find clazz"); } _obj = _env->AllocObject(clazz); //methodid = _env->GetMethodID(clazz, "onFrame","([BI)V"); //获取非静态方法id,根据方法名称和方法参数、返回值类型 methodid = _env->GetMethodID(clazz, "pushBuf", "([BI)V"); //"([BI)Z"); if (methodid == 0) { LOGI("can't find methodid"); }else{ send_up = 100; LOGI(" find methodid start"); jbyte *jy=(jbyte*)sps; jbyteArray jbarray = _env->NewByteArray(12);//jbarray _env->SetByteArrayRegion(jbarray, 0, 12, jy); _env->CallVoidMethod(_obj, methodid, jbarray, 12); //jni端调用java端,上传sps到缓冲列表 jbyte *jy0=(jbyte*)pps; jbyteArray jbarray0 = _env->NewByteArray(8);//jbarray _env->SetByteArrayRegion(jbarray0, 0, 8, jy0); _env->CallVoidMethod(_obj, methodid, jbarray0, 8); //jni端调用java端,上传pps到缓冲列表 LOGI(" find methodid end"); } } size = frameSize+4; buffer = (unsigned char *)malloc(size); //添加start code,便于播放 buffer[0] = 0x0; buffer[1] = 0x0; buffer[2] = 0x0; buffer[3] = 0x1; memcpy(buffer+4, fReceiveBuffer, frameSize); /* Nalu_type: 0x67 (0 11 00111) SPS 非常重要 type = 7 0x68 (0 11 01000) PPS 非常重要 type = 8 0x65 (0 11 00101) IDR 关键帧 type = 5 0x61 (0 10 00001) I帧 重要 type = 1 0x41 (0 10 00001) P帧 type = 1 0x01 (0 00 00001) B帧 type = 1 0x06 (0 00 00110) SEI type = 6 */ LOGI(" IDR = %d", frameSize); //写入H.264文件, live555 接收到的是nalu数据需要添加start code,存储的文件播放器才能播放 //int ret = fwrite(buffer, 1, size, fp); } }else { size = frameSize+4; buffer = (unsigned char *)malloc(size); //添加start code,便于播放 buffer[0] = 0x0; buffer[1] = 0x0; buffer[2] = 0x0; buffer[3] = 0x1; memcpy(buffer+4, fReceiveBuffer, frameSize); //写入H.264文件 //int ret = fwrite(buffer, 1, size, fp); } if(runningFlag==0 && methodid != 0){ jbyte *jy=(jbyte*)buffer; jbyteArray jbarray = _env->NewByteArray(size);//jbarray _env->SetByteArrayRegion(jbarray, 0, size, jy); int len = _env->GetArrayLength(jbarray); jboolean flag = false; //上传nalu到缓冲列表 _env->CallVoidMethod(_obj, methodid, jbarray, size); //CallBooleanMethod jbarray } //usleep(30000); // 30ms free(buffer); continuePlaying();}Boolean DummySink::continuePlaying() { if (fSource == NULL) return False; // sanity check (should not happen) // Request the next frame of data from our input source. // "afterGettingFrame()" will get called later, when it arrives: fSource->getNextFrame(fReceiveBuffer, DUMMY_SINK_RECEIVE_BUFFER_SIZE, afterGettingFrame, this, onSourceClosure, this); return True;}//传入写入文件地址,rtsp sdp地址JNIEXPORT void JNICALL Java_com_example_mylive555mp4_MainActivity0_RtspClient (JNIEnv *env, jclass clz, jstring program, jstring sdp) { const char* sdp_title = env->GetStringUTFChars(sdp, NULL); const char* program_title = env->GetStringUTFChars(program, NULL); _env = env; runningFlag = 1; methodid = 0; fp = fopen(program_title, "wb"); if(!fp){ LOGI(" h264 fopen error "); return ; } signed char ret = doRtspClient(program_title, sdp_title); //openURL() 创建RTSPClient // rtspClient->sendDescribeCommand(continueAfterDESCRIBE); // continueAfterDESCRIBE()中创建MediaSession // rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP, False, REQUEST_STREAMING_OVER_TCP); // continueAfterSETUP(): // scs.subsession->sink = DummySink::createNew(env, *scs.subsession, rtspClient->url()); // Sink // scs.subsession->sink->startPlaying(*(scs.subsession->readSource()), subsessionAfterPlaying, scs.subsession); // Source // scs.subsession->rtcpInstance()->setByeHandler(subsessionByeHandler, scs.subsession); // Rtcp 实例 // rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY); env->ReleaseStringUTFChars(sdp, sdp_title); env->ReleaseStringUTFChars(program, program_title); }//视频录制结束调用JNIEXPORT void Java_com_example_mylive555mp4_MainActivity0_RtspEnd(JNIEnv *env, jclass clz){ shutdownStream(rtspClient);}
Android.mk:
include $(CLEAR_VARS)LOCAL_MODULE := ffmpegLOCAL_SRC_FILES := libffmpeg.soinclude $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS)LOCAL_MODULE := live555LOCAL_SRC_FILES := liblive555.soinclude $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS)LOCAL_C_INCLUDES += \ ... $(LOCAL_PATH)/live555 \ $(LOCAL_PATH)/live555/BasicUsageEnvironment/include \ $(LOCAL_PATH)/live555/liveMedia/include \ $(LOCAL_PATH)/live555/groupsock/include \ $(LOCAL_PATH)/live555/UsageEnvironment/includeLOCAL_SHARED_LIBRARIES := ffmpeg mp3lame faac rtmp x264 live555LOCAL_MODULE := rtspclientLOCAL_SRC_FILES := testRTSPClient0.cppLOCAL_LDLIBS += -llog -lzinclude $(BUILD_SHARED_LIBRARY)
0 0
- live555 mediacodec应用
- live555 server mediacodec应用
- mediacodec mp4v2应用
- live555+MediaCodec实现获取并解码RTSP视频(H264)
- Mediacodec
- MediaCodec
- MediaCodec storeMetaDataInBuffers应用自己分配buffer设给编码器
- live555
- live555
- live555
- live555
- live555
- live555
- live555
- live555
- live555
- live555
- LIVE555
- Android Canvas总结
- LeetCode刷题(C++)——Search Insert Position(Easy)
- oracle11g和arcsde
- ExpandableListView的Adapter--MealExpandableListAdapter
- oracle创建数据库和用户
- live555 mediacodec应用
- text-align的妙用
- Selenium、firefox和wordpress配置出现的问题
- 微软亚洲研究院面试编程题之判断两个链表是否相交
- 数据存储:数据备份:测试备份和还原
- hibernate-mapping set 简单的配置问题
- 巧用MBean 实现大挪移
- redis与memcache的区别
- 为什么用ls和du显示出来的文件大小有差别?