樽中酒不空
分类: c/c
2014-09-19 13:56:25
classcrtspsession
{
public:
crtspsession();
virtual~crtspsession();
intstartrtspclient(charconst*progname,charconst*rtspurl,intdebuglevel);
intstoprtspclient();
intopen;
};rtspclient*m_rtspclient;
chareventloopwatchvariable;pthread_ttid;
boolm_running;stringm_rtspurl;stringm_progname;intm_debuglevel;staticvoid*rtsp_thread_fun(void*param);voidrtsp_fun();
crtspsession::crtspsession()
{
m_rtspclient=null;
m_running=false;
eventloopwatchvariable=0;
}
crtspsession::~crtspsession()
{
}
intcrtspsession::startrtspclient(charconst*progname,charconst*rtspurl,intdebuglevel)
{
m_progname=progname;
m_rtspurl=rtspurl;
m_debuglevel=debuglevel;
eventloopwatchvariable=0;
intr=pthread_create(&tid,null,rtsp_thread_fun,this);
if(r)
{
perror("pthread_create()");
return-1;
}
return0;
}
intcrtspsession::stoprtspclient()
{
eventloopwatchvariable=1;
return0;
}
void*crtspsession::rtsp_thread_fun(void*param)
{
crtspsession*pthis=(crtspsession*)param;
pthis->rtsp_fun();
returnnull;
}
voidcrtspsession::rtsp_fun()
{
//::startrtsp(m_progname.c_str(),m_rtspurl.c_str(),m_ndebuglever);
taskscheduler*scheduler=basictaskscheduler::createnew();
usageenvironment*env=basicusageenvironment::createnew(*scheduler);
if(open,m_rtspurl.c_str(),m_debuglevel)==0)
{
m_nstatus=1;
env->taskscheduler().doeventloop(&eventloopwatchvariable);
m_running=false;
eventloopwatchvariable=0;
if(m_rtspclient)
{
shutdownstream(m_rtspclient,0);
}
m_rtspclient=null;
}
env->reclaim();
env=null;
deletescheduler;
scheduler=null;
m_nstatus=2;
}
intcrtspsession::open
{
m_rtspclient=ourrtspclient::createnew(env,rtspurl,debuglevel,progname);
if(m_rtspclient==null)
{
env<<"failedtocreateartspclientforurl\""<<rtspurl<<"\":"<<env.getresultmsg()<<"\n";
return-1;
}
((ourrtspclient*)m_rtspclient)->m_nid=m_nid;
m_rtspclient->senddescribecommand(continueafterdescribe);
return0;
}
//afunctionthatoutputsastringthatidentifieseachstream(fordebuggingoutput).modifythisifyouwish:
usageenvironment&operator<<(usageenvironment&env,constrtspclient&rtspclient){
returnenv<<"[url:\""<<rtspclient.<<"\"]:";
}
//afunctionthatoutputsastringthatidentifieseachsubsession(fordebuggingoutput).modifythisifyouwish:
usageenvironment&operator<<(usageenvironment&env,constmediasubsession&subsession){
returnenv<<subsession.mediumname()<<"/"<<subsession.codecname();
}
voidusage(usageenvironment&env,charconst*progname){
env<<"usage:"<<progname<<"... \n" ;
env<<"\t(whereeachisa\"rtsp://\"url)\n";
}
这个简单的class,是在testrtspclient.cpp上简单修改的,其他的函数都保持不变,只是把open和shutdown合在了一个class里面,然后启动一个线程。因为这里的
env->taskscheduler().doeventloop(&eventloopwatchvariable);是阻塞的。当eventloopwatchvariable为1的时候,live的doeventloop结束循环。
testrtspclient.cpp里的做法是,当eventloopwatchvariable为1的时候,结束所有流。而实际的客户端可以任意选择某一路停止,其他还是播放,所以为每一路创建一个线程,这样可以控制只停止该路。最后,
dummysink::aftergettingframe
这里取到媒体数据后,可以通过自己设计的回调传出来。可以用回调函数,可以用抽象基类的方法,甚至都可以sendmessage直接发到某个窗口上。另外,其实live555的doeventloop设计的很灵活的,完全可以做成非阻塞。但本文的目的是帮助live555的初学者,在还没完全掌握的情况下,自己可以简单做一个工具,用来实现rtsp的接收处理。通过这个实例,也能更方便地理解rtsp的工作方式。顺便说说上面class的调用:
crtspsession*prtsp=newcrtspsession;if(prtsp->startrtspclient(progname,rtspurl,debuglevel))
{deleteprtsp;
prtsp=null;
return-1;}停止的时候:prtsp->stoprtspclient();deleteprtsp;prtsp=null;顺便把收到的视频解码也简易封装一下:
classcdecodecb{public:
virtualvoidvideocb(intwidth,intheight,uint8_t*buff,intlen)=0;};
classcffmpegdecode{public:
cffmpegdecode();
~cffmpegdecode();
intinitffmpeg();
intopendecoder(intwidth,intheight,cdecodecb*pcb);intclosedecoder();
intdecode_rtsp_frame(uint8_t*input,intnlen,boolbwaitiframe/*=false*/);private:boolm_binit;avcodec*decode_codec;
avcodeccontext*decode_c;
avframe*decode_picture;
structswscontext*img_convert_ctx;cdecodecb*m_pcb;intm_nwidth;intm_nheight;};
staticintsws_flags=sws_bicubic;static int sws_flags = sws_bicubic; cffmpegdecode::cffmpegdecode() { m_binit = false; img_convert_ctx = null; } cffmpegdecode::~cffmpegdecode() { av_lockmgr_register(null); } int cffmpegdecode::initffmpeg() { //m_state = rc_state_init; avcodec_register_all(); av_register_all(); //avformat_network_init(); //if (av_lockmgr_register(lockmgr)) { // m_state = rc_state_init_error; // return -1; } return 0; } int cffmpegdecode::opendecoder(int width, int height,cdecodecb* pcb) { m_nwidth = width; m_nheight = height; m_pcb = pcb; if (m_binit) return -1; decode_codec = avcodec_find_decoder(codec_id_h264); if (!decode_codec) { fprintf(stderr, "codec not found\n"); return -2; } decode_c= avcodec_alloc_context3(decode_codec); decode_c->codec_id= codec_id_h264; decode_c->codec_type = avmedia_type_video; decode_c->pix_fmt = pix_fmt_yuv420p; decode_picture= avcodec_alloc_frame(); if (avcodec_open2(decode_c, decode_codec, null) < 0) { // fprintf(stderr, "could not open codec\n"); return -3; } m_binit = true; return 0; } int cffmpegdecode::closedecoder() { if(decode_c) { avcodec_close(decode_c); av_free(decode_c); } if(decode_picture) av_free(decode_picture); m_binit = false; } int cffmpegdecode::decode_rtsp_frame(uint8_t* input,int nlen,bool bwaitiframe /*= false*/) { if(!m_binit) return -1; if(input == null || nlen <= 0) return -2; try{ int got_picture; int size = nlen; avpacket avpkt; av_init_packet(&avpkt); avpkt.size = size; avpkt.data = input; //while (avpkt.size > 0) { int len = avcodec_decode_video2(decode_c, decode_picture, &got_picture, &avpkt); if(len == -1) { return -3; } if (got_picture) { int w = decode_c->width; int h = decode_c->height; int numbytes=avpicture_get_size(pix_fmt_rgb24, w,h); uint8_t * buffer=(uint8_t *)av_malloc(numbytes*sizeof(uint8_t)); avframe *pframergb = avcodec_alloc_frame(); avpicture_fill((avpicture *)pframergb, buffer,pix_fmt_rgb24, w, h); img_convert_ctx = sws_getcachedcontext(img_convert_ctx, w, h, (pixelformat)(decode_picture->format), w, h,pix_fmt_rgb24, sws_flags, null, null, null); if (img_convert_ctx == null) { fprintf(stderr, "cannot initialize the conversion context\n"); //exit(1); return -4; } sws_scale(img_convert_ctx, decode_picture->data, decode_picture->linesize, 0, h, pframergb->data, pframergb->linesize); if (m_pcb) { m_pcb->videocb(w, h, pframergb->data[0], numbytes*sizeof(uint8_t)); } av_free(buffer); av_free(pframergb); return 0; if (avpkt.data) { avpkt.size -= len; avpkt.data = len; } } else { return -5; } //return 0; } //return 0; } catch(...) { } return -6; }代码参考ffplay.c, decode_encode.c。如果多线程下有问题,记得av_lockmgr_register。
sxcong2015-06-30 18:42:57
:博主你好,我现在创建多个线程使用ffmpeg,在avcodec_open2的时候会返回失败,看了你的av_lockmgr_register,请问这是怎么用的呢,里面的参数lockmgr是什么,在哪里定义的,望大神指点!
ffplay.c里面有详细的使用,可以看看代码。
|2015-06-30 15:39:43
sxcong:这是两种设计思路。文中是stop退出后同时退出live555的循环。
也可以按你的方法,只stop某个流,live还在运行。
博主你好,我现在创建多个线程使用ffmpeg,在avcodec_open2的时候会返回失败,看了你的av_lockmgr_register,请问这是怎么用的呢,里面的参数lockmgr是什么,在哪里定义的,望大神指点!
|sxcong2015-06-24 08:28:15
:在stop的时候,直接赋值eventloopvariable = 1,退出doeventloop循环,然后再shutdownstream?不应该是先shutdownstream然后再退出循环吗?望博主不吝赐教!
这是两种设计思路。文中是stop退出后同时退出live555的循环。
也可以按你的方法,只stop某个流,live还在运行。
2015-06-23 15:54:28
在stop的时候,直接赋值eventloopvariable = 1,退出doeventloop循环,然后再shutdownstream?不应该是先shutdownstream然后再退出循环吗?望博主不吝赐教!
sxcong2015-01-15 13:45:51
:sxcong老师,您代码里面的 ((ourrtspclient*)m_rtspclient)->m_nid = m_nid;中左边您说是您自己加的成员变量,但=右边的m_nid哪里来的?能具体说下m_nid的用意吗?谢谢
m_nid是自己设置的,在ourrtspclient里面。也就是稍稍修改一下官方的testrtspclient.cpp的代码,在里面加上一个变量进行区别。
|