IP Camera代码分析
2013-1-14
一、大概流程
1. new一个MediaRecorder并开始录制,2s后停掉?
2. 检测"/sdcard/ipcam/myvideo.mp4"中mdat的位置和SPS/PPS信息;
3. 建立camera和http的localsocket接口并传递至C++层;
4. 通知doCapture和doStreaming在localsocket准备收/发数据并处理(recv/send);
5. new一个MediaRecorder,并把数据输出至先前建立的localsocket接口;
6. doCapture会直接从mdat数据开始位置接收NAL并自己打上时间戳,AMR未做处理。
二、代码跟踪
private void setup()
btnStart.setOnClickListener(startAction);
private OnClickListener startAction = newOnClickListener() {
@Override
public void onClick(View v) {
doAction();
}
};
private void doAction() {
if( inServer == false) {
myCamView.PrepareMedia(targetWid,targetHei);// myMediaRecorder = new MediaRecorder();
boolean ret =myCamView.StartRecording(checkingFile);..................................... .........................................1
myCamView.StopMedia();
if ( NativeAgent.NativeCheckMedia(targetWid, targetHei, checkingFile) ) ....................................................2
startServer();............................................................................................................................................3
}
}
1. 录制成本地文件"/sdcard/ipcam/myvideo.mp4"
public boolean StartRecording(StringtargetFile) {
myMediaRecorder.setOutputFile(targetFile);
returnrealyStart();
}
private boolean realyStart()
myMediaRecorder.prepare();
myMediaRecorder.start();
2. 从本地文件得知mdat的位置并搜索SPS/PPS
static public boolean NativeCheckMedia(int wid,int hei, String filename)
nativeCheckMedia(wid,hei, filename)
JNIEXPORT jint JNICALLJNIDEFINE(nativeCheckMedia)(JNIEnv* env, jclass clz, jint wid, jint hei,jstring file_path)
CheckMedia(wid,hei, mp4_file);
//找到mdat的位置;
//获取SPS/PPS
3. 启动camera loop和http loop
private void startServer()
strServer= new StreamingServer(8080, resourceDirectory);
strServer.setOnRequestListen(streamingRequest);
StreamingServer.OnRequestListenstreamingRequest = new StreamingServer.OnRequestListen()
startStreaming()
private boolean startStreaming()
cameraLoop.InitLoop();
httpLoop.InitLoop();
nativeAgt.NativeStartStreamingMedia(cameraLoop.getReceiverFileDescriptor(), httpLoop.getSenderFileDescriptor());...................................4
myCamView.PrepareMedia(targetWid, targetHei); // myMediaRecorder = newMediaRecorder();
boolean ret =myCamView.StartStreaming(cameraLoop.getSenderFileDescriptor());...........................................................................................5
4. 向C层传递cameracapture socket fd和httpd发送socket fd
static public voidNativeStartStreamingMedia(FileDescriptor in, FileDescriptor out)
nativeStartStreamingMedia(in, out);
JNIEXPORT jint JNICALL JNIDEFINE(nativeStartStreamingMedia)(JNIEnv*env, jclass clz, jobject infdesc, jobject outfdesc)
intinfd = getNativeFd(env, clz, infdesc);
int outfd = getNativeFd(env, clz, outfdesc);
StartStreamingMedia(infd, outfd);
int StartStreamingMedia(int infd, intoutfd)
mediaBuffer= new MediaBuffer(32, 120, MAX_VIDEO_PACKAGE, 1024);
mediaStreamer= new MediaStreamer(infd, outfd);
mediaStreamer->Start();
void MediaStreamer::Start()
captureThread->Post(this, MSG_BEGIN_CAPTURE_TASK);
streamingThread->Post(this,MSG_BEGIN_STREAMING_TASK);
开始捕捉camera过来的数据和向外发送打包过的只有视频的flv数据
voidMediaStreamer::OnMessage(talk_base::Message *msg) {
switch( msg->message_id) {
case MSG_BEGIN_CAPTURE_TASK:
doCapture();......................................................................................6
break;
case MSG_BEGIN_STREAMING_TASK:
doStreaming();..................................................................................7
break;
default:
break;
}
}
5. 又new一个MediaRecorder并设置为流式输出
public boolean StartStreaming(FileDescriptortargetFd)
myMediaRecorder.setOutputFile(targetFd);
myMediaRecorder.setMaxDuration(9600000); // Set max duration 4 hours
//myMediaRecorder.setMaxFileSize(1600000000);// Set max file size 16G
myMediaRecorder.setOnInfoListener(streamingEventHandler);
returnrealyStart();
private boolean realyStart()
myMediaRecorder.prepare();
myMediaRecorder.start();
6.
void MediaStreamer::doCapture()
if( buf[0] == 0x00 ) {
//recvH264 NAL单元;
//重新计算每个NAL单元的时间;
}else{//参见8.
//recvAMR数据,但并没有PushBuffer,也就是没有把AMR数据送给httpd;
}
详细代码:
// this method only support H.264 + AMR_NB
void MediaStreamer::doCapture() {
unsigned char *buf;
buf = new unsigned char[MAX_VIDEO_PACKAGE];
unsigned int aseq = 0;
unsigned int vseq = 0;
const unsigned int STACK_SIZE = 128;
std::list
unsigned long last_ts = 0;
// skip none useable heaer bytes
fillBuffer( buf, mediaInfo.begin_skip);
// fectching real time video data from camera
while(1) {
if ( fillBuffer(buf, 4) < 0)
break;
checking_buffer:
if ( buf[0] == 0x00 ) {
unsigned int vpkg_len = (buf[1] << 16) + (buf[2] << 8) +buf[3];
if ( fillBuffer(&buf[4], vpkg_len ) < 0)
break;
vpkg_len += 4;
if ( vpkg_len > (unsigned int)MAX_VIDEO_PACKAGE ) {
LOGD("ERROR: Drop big videoframe....");
vseq++;
fillBuffer(vpkg_len);
continue;
}
int slice_type = 0;
if ( (buf[5] & 0xF8 ) == 0xB8) {
slice_type = 1;
} else if ( ((buf[5] & 0xFF) == 0x88)
&& ((buf[5] &0x80) == 0x80) ) {
slice_type = 1;
} else if ( (buf[5] & 0xE0) == 0xE0) {
slice_type = 0;
} else if ( (buf[5] & 0xFE) == 0x9A) {
slice_type = 0;
}
buf[0] = 0x00;
buf[1] = 0x00;
buf[2] = 0x00;
buf[3] = 0x01;
#if 1
// computing the current package's timestamp
int64_t cts = getCurrentTime() / 1000;
if( timestack.size() >= STACK_SIZE) {
timestack.pop_back();
timestack.push_front(cts);
} else {
timestack.push_front(cts);
}
if ( timestack.size() cts = (timestack.size() - 1) *100; // default = 10 fps last_ts = (unsigned long)cts; } else { unsigned long total_ms; total_ms = timestack.front() -timestack.back(); cts = last_ts + total_ms /(STACK_SIZE - 1); last_ts = cts; } mediaBuffer->PushBuffer( buf, vpkg_len, last_ts, slice_type ?MEDIA_TYPE_VIDEO_KEYFRAME : MEDIA_TYPE_VIDEO); #else vseq ++; mediaBuffer->PushBuffer( buf, vpkg_len, vseq * 1000 /mediaInfo.video_frame_rate, slice_type ? MEDIA_TYPE_VIDEO_KEYFRAME :MEDIA_TYPE_VIDEO); #endif } else { // fetching AMR_NB audio package static const unsigned char packed_size[16] = {12, 13, 15, 17, 19, 20,26, 31, 5, 0, 0, 0, 0, 0, 0, 0}; unsigned int mode = (buf[0]>>3) & 0x0F; unsigned int size = packed_size[mode] + 1; if ( size > 4) { if ( fillBuffer(&buf[4], size -4) < 0) break; aseq ++; //SignalNewPackage(buf, 32,ats, MEDIA_TYPE_AUDIO); } else { fillBuffer(&buf[4], size ); for(int i = 0; i < 4; i++) buf[i] = buf[size+i]; //SignalNewPackage(buf, 32,ats, MEDIA_TYPE_AUDIO); goto checking_buffer; } } } delete buf; } 7. doStreaming主要是打包成flv向外发数据,不做细究。 8. 此表说明AMR的头字节不可能为0x00 规格 帧大小 帧头(1字节) AMR 4.75 13 04 (0000 0100) AMR 5.15 14 0C (0000 1100) AMR 5.9 16 14 (0001 0100) AMR 6.7 18 1C (0001 1100) AMR 7.4 20 24 (0010 0100) AMR 7.95 21 2C (0010 1100) AMR 10.12 27 34 (0011 0100) AMR 12.2 32 3C (0011 1100) 三、实验数据