1、背景说明:
MJPG-streamer是一个开源软件,它可从一个输入插件复制JPG帧到多个输出插件。它的输入插件从摄像头、文件系统或其它输入插件读取视频数据,并复制到内存中,它有多个输出插件将这些视频数据经过处理,其中最重要的输出插件是网站服务器插件,它将视频帧以M-JPEG流的形式通过基于ip的网络传输到浏览器如Firefox,Cambozola,VLC播放器,Windows的移动设备或者其他拥有浏览器的移动设备。MJPG-streamer的工作就是将其中的一个输入插件和多个输出插件绑定在一起,所有的工作都是通过它的各个插件完成的。
2、代码分析
本例中输入插件实现从摄像头抓取视频帧,并复制到一个全局缓存;输出插件实现从该缓存中读取帧,并保存为当地视频文件。
input_run/output_run、input_stop/output_stop均在主函数中被调用。每次调用时input_run/output_run都创建一个新线程,input_stop/output_stop终止该线程。void *cam_thread(void *arg)与void *worker_thread(void *arg)分别为其对应的线程启动函数。其中使用了互斥量+条件变量对线程进行同步。
2.1输入插件关键实现代码
/******************************************************************************
Description.: spins of a worker thread
******************************************************************************/
intinput_run(int id)
{
/* create thread and pass context to thread function */
pthread_create(&(cams[id].threadID), NULL, cam_thread, &(cams[id]));
pthread_detach(cams[id].threadID);
return 0;
}
/******************************************************************************
Description.: this thread worker grabs a frame from camera and copies it to the global buffer
******************************************************************************/
void*cam_thread(void *arg)
{
/* set cleanup handler to cleanup allocated ressources */
pthread_cleanup_push(cam_cleanup, pcontext);
while(!pglobal->stop) {
/* copy JPG picture to global buffer */
pthread_mutex_lock(&pglobal->in[pcontext->id].db);
pglobal->in[pcontext->id].size = memcpy_picture(pglobal->in[pcontext->id].buf, pcontext->videoIn->tmpbuffer, pcontext->videoIn->buf.bytesused);
/* copy this frame's timestamp to user space */
pglobal->in[pcontext->id].timestamp = pcontext->videoIn->buf.timestamp;
/* signal fresh_frame */
pthread_cond_broadcast(&pglobal->in[pcontext->id].db_update);
pthread_mutex_unlock(&pglobal->in[pcontext->id].db);
}
DBG("leaving input thread, calling cleanup function now\n");
pthread_cleanup_pop(1);
}
/******************************************************************************
Description.: Stops the execution of worker thread
******************************************************************************/
intinput_stop(int id)
{
DBG("will cancel camera thread #%02d\n", id);
pthread_cancel(cams[id].threadID);
return 0;
}
2.2输出插件关键实现代码
/******************************************************************************
Description.: calling this function creates and starts the worker thread
******************************************************************************/
intoutput_run(int id)
{
DBG("launching worker thread\n");
pthread_create(&worker, 0, worker_thread, NULL);
pthread_detach(worker);
return 0;
}
/******************************************************************************
Description.: this is the main worker thread
it loops forever, grabs a fresh frame from gloable buffer and stores it to file
******************************************************************************/
void*worker_thread(void *arg)
{
/* set cleanup handler to cleanup allocated ressources */
pthread_cleanup_push(worker_cleanup, NULL);
while(ok >= 0 && !pglobal->stop) {
DBG("waiting for fresh frame\n");
pthread_mutex_lock(&pglobal->in[input_number].db);
pthread_cond_wait(&pglobal->in[input_number].db_update, &pglobal->in[input_number].db);
/* copy frame to our local buffer now */
memcpy(frame, pglobal->in[input_number].buf, frame_size);
/* allow others to access the global buffer again */
pthread_mutex_unlock(&pglobal->in[input_number].db);
}
/* cleanup now */
pthread_cleanup_pop(1);
}
/******************************************************************************
Description.: calling this function stops the worker thread
******************************************************************************/
intoutput_stop(int id)
{
DBG("will cancel worker thread\n");
pthread_cancel(worker);
return 0;
}
—wicoboy
2013/8/24