rtsp客户端 & ffmpeg解码(基于live555)
2017-07-19 11:38
197 查看
很多人用live555都是为了做一个rtsp的客户端。Live555提供了一个功能丰富的rtsp客户端:openRTSP。很多初学者都是通过它来学习live及rtsp的。这个程序修改做单路播放很容易,不过,一般客户端需要同时做多路播放或录像,这时再采用这个程序就比较麻烦了。而且,程序里也注明://NOTE:IfyouwanttodevelopyourownRTSPclientapplication(orembedRTSPclientfunctionalityintoyourownapplication),//thenwedon'trecommendusingthiscodeasamodel,becauseitistoocomplex(withmanyoptions).//Instead,werecommendusingthe"testRTSPClient"applicationcodeasamodel.建议用testRTSPClient,代码简洁,但也足够用了。testRTSPClient.cpp,本地接收流数据后,简单log一下,没做任何处理,这样正合适改造,而且它支持多路。下面简单以这个cpp为例,封装一个可重用的classdemo来。先简单分析一下流程:1 openURL,开始播放。2 在openURL里面,调用sendDescribeCommand,向服务器端发请求。然后,通过回调函数处理。3如果没有错误的话,env->taskScheduler().doEventLoop(&eventLoopWatchVariable);这里阻塞执行。4 DummySink,这个是数据的回调,DummySink::afterGettingFrame这里取到数据。在这个程序里,main里面调用:for(inti=1;i<=argc-1;++i){ openURL(*env,argv[0],argv[i]); }voidshutdownStream(RTSPClient*rtspClient,intexitCode=1);这里是结束某个流,rtspClient是由openURL创建的。这就实现了多路的同时播放。如果要简单地处理,其实只要把openURL和shutdownStream封装成起来就可以了。下面是简单接口的示例:
classCRTSPSession
{
public:
CRTSPSession();
virtual~CRTSPSession();
intstartRTSPClient(charconst*progName,charconst*rtspURL,intdebugLevel);
intstopRTSPClient();
intopenURL(UsageEnvironment&env,charconst*progName,charconst*rtspURL,intdebugLevel);
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(openURL(*env,m_progName.c_str(),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::openURL(UsageEnvironment&env,charconst*progName,charconst*rtspURL,intdebugLevel)
{
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.url()<<"\"]:";
}
//Afunctionthatoutputsastringthatidentifieseachsubsession(fordebuggingoutput).Modifythisifyouwish:
UsageEnvironment&operator<<(UsageEnvironment&env,constMediaSubsession&subsession){
returnenv<<subsession.mediumName()<<"/"<<subsession.codecName();
}
voidusage(UsageEnvironment&env,charconst*progName){
env<<"Usage:"<<progName<<"<rtsp-url-1>...<rtsp-url-N>\n";
env<<"\t(whereeach<rtsp-url-i>isa\"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;
staticintsws_flags=SWS_BICUBIC;CFfmpegDecode::CFfmpegDecode(){ m_bInit=false; img_convert_ctx=NULL;}CFfmpegDecode::~CFfmpegDecode(){ av_lockmgr_register(NULL);}intCFfmpegDecode::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; } return0;}intCFfmpegDecode::openDecoder(intwidth,intheight,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,"codecnotfound\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,"couldnotopencodec\n"); return-3; } m_bInit=true; return0;}intCFfmpegDecode::closeDecoder(){ if(decode_c) { avcodec_close(decode_c); av_free(decode_c); } if(decode_picture) av_free(decode_picture); m_bInit=false;}intCFfmpegDecode::decode_rtsp_frame(uint8_t*input,intnLen,boolbWaitIFrame/*=false*/){ if(!m_bInit) return-1; if(input==NULL||nLen<=0) return-2; try{ intgot_picture; intsize=nLen; AVPacketavpkt; av_init_packet(&avpkt); avpkt.size=size; avpkt.data=input; //while(avpkt.size>0) { intlen=avcodec_decode_video2(decode_c,decode_picture,&got_picture,&avpkt); if(len==-1) { return-3; } if(got_picture) { intw=decode_c->width; inth=decode_c->height; intnumBytes=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,"Cannotinitializetheconversioncontext\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); return0; if(avpkt.data) { avpkt.size-=len; avpkt.data+=len; } } else { return-5; } //return0; } //return0; } catch(...) { } return-6;}
代码参考ffplay.c,decode_encode.c。
如果多线程下有问题,记得
av_lockmgr_register。
相关文章推荐
- 基于live555的rtsp客户端接收及ffmpeg解码
- 基于live555的rtsp客户端接收及ffmpeg解码
- 基于live555的rtsp客户端接收及ffmpeg解码
- 一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——解码篇:(二)用ffmpeg解码音频
- 整理 live555 rtsp ffmpeg 客户端解码流程
- 一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——解码篇:(一)用ffmpeg解码视频
- ffmpeg用来进行编码解码,live555用来进行传输rtsp、rtp
- 基于live555的rtsp 客户端模块优化 标签: live555rtsp客户端 2014-11-14 09:24 980人阅读 评论(0) 收藏 举报 分类: 流媒体(16) 版权声明:本文
- 基于Live555,ffmpeg的RTSP播放器直播与点播
- EasyRTSPClient:基于live555封装的支持重连的RTSP客户端RTSPClient
- EasyRTSPClient:基于live555封装的支持重连的RTSP客户端RTSPClient
- 基于Live555,ffmpeg的RTSP播放器直播与点播
- 基于live555的rtsp封装rtspperf的客户端源码解析
- Live555 + h264 + ffmpeg 客户端解码 笔记
- 基于live555封装的rtsp客户端rtspperf
- RTSP H264播放器(基于live555、ffmpeg、d3d应用)
- live555实现ffmpeg解码H264的rtsp流
- 基于live555的rtsp 客户端模块优化
- 基于live555的rtsp客户端模块
- 基于live555的rtsp客户端接收及ffmpeg解码