首页 > 试题

info什么意思

更新时间:2023-02-04 19:53:50 阅读: 评论:0

上海流浪大师为什么流浪-祝贺你


2023年2月4日发(作者:清纯美女桌面)

海康sip服务器是什么意思_海康摄像头⼊门

海康RTSP取流URL格式

⼀、预览取流

设备预览取流的RTSPURL有新⽼版本,2012年之前的设备(⽐如V2.0版本的Netra设备)⽀持⽼的取流格式,之后的设备新⽼取流格式都

⽀持。

⽼版本

URL规定:

rtsp://urname:password@//ch/

注:VLC可以⽀持解析URL⾥的⽤户名密码,实际发给设备的RTSP请求不⽀持带⽤户名密码。

举例说明:

DS-9016HF-ST的IP通道01主码流:rtsp://admin:12345@172.6.22.106:554/h264/ch33/main/av_stream

DS-9016HF-ST的模拟通道01⼦码流:rtsp://admin:12345@172.6.22.106:554/h264/ch1/sub/av_stream

DS-9016HF-ST的零通道主码流(零通道⽆⼦码流):rtsp://admin:12345@172.6.22.106:554/h264/ch0/main/av_stream

DS-2DF7274-A的第三码流:rtsp://admin:12345@172.6.10.11:554/h264/ch1/stream3/av_stream

URL规定:

rtsp://urname:password@

:/Streaming/Channels/(?parm1=value1&parm2-=value2…)

注:VLC可以⽀持解析URL⾥的⽤户名密码,实际发给设备的RTSP请求不⽀持带⽤户名密码。

详细描述:

举例说明:

DS-9632N-ST的IP通道01主码流:rtsp://admin:12345@172.6.22.234:554/Streaming/Channels/101?

transportmode=unicast

DS-9016HF-ST的IP通道01主码流:rtsp://admin:12345@172.6.22.106:554/Streaming/Channels/1701?

transportmode=unicast

DS-9016HF-ST的模拟通道01⼦码流:rtsp://admin:12345@172.6.22.106:554/Streaming/Channels/102?

transportmode=unicast

(单播):rtsp://admin:12345@172.6.22.106:554/Streaming/Channels/102?transportmode=multicast

(多播):rtsp://admin:12345@172.6.22.106:554/Streaming/Channels/102(?后⾯可省略,默认单播)

DS-9016HF-ST的零通道主码流(零通道⽆⼦码流):rtsp://admin:12345@172.6.22.106:554/Streaming/Channels/001

DS-2DF7274-A的第三码流:rtsp://admin:12345@172.6.10.11:554/Streaming/Channels/103

注:前⾯⽼URL,NVR(>=64路的除外)的IP通道从33开始;新URL,通道号全部按顺序从1开始。

URL规定:

rtsp://urname:password@

:/Streaming/tracks/(?parm1=value1&parm2-=value2…)

注:VLC可以⽀持解析URL⾥的⽤户名密码,实际发给设备的RTSP请求不⽀持带⽤户名密码。

举例说明:

DS-9016HF-ST的模拟通道01:

rtsp://admin:12345@172.6.22.106:554/Streaming/tracks/101?starttime=20120802t063812z&endtime=20120802t064816z

DS-9016HF-ST的IP通道01:rtsp://admin:12345@172.6.22.106:554/Streaming/tracks/1701?

starttime=20131013t093812z&endtime=20131013t104816z

表⽰以单播形式回放指定设备的通道中的录像⽂件,时间范围是starttime到endtime,

其中starttime和endtime的格式要符合ISO8601。具体格式是:

YYYYMMDD”T”on”Z”,Y是年,M是⽉,D是⽇,T是时间分格符,H是⼩时,M是分,S是秒,Z是可选的、表⽰

Zulu(GMT)时间。

VLC播放⽰例:

媒体--》打开⽹络串流--》⽹络:

rtsp://urname:password@192.168.1.17:554/MPEG-4/ch1/main/av_stream

Linux下编译eXosip2库以及测试

原⽂作者:这个名字不知道有没有⼈⽤啊

环境:

Ubuntu18.04+libosip2-5.1.0+libexosip2-5.1.0+c-ares-1.15.0

下载

依次解压编译(注意顺序,exosip要在最后编译)

tarxvf对应压缩包名

cd解压出来的⽂件夹

./configuremake

sudomakeinstall

测试

#include#include#include#include

usingnamespacestd;intmain()

{

eXosip_t*sip=eXosip_malloc();if(eXosip_init(sip)==OSIP_SUCCESS)

{

cout<<"eXosipinitok"<

}el{

cout<<"exosipinitfail"<

}intret=eXosip_listen_addr(sip,IPPROTO_UDP,NULL,0,AF_INET,0);if(ret==OSIP_SUCCESS)

{

cout<<"exosioplistenaddrsuccess"<

}elcout<<"listenaddrfail,ret:"<

eXosip_quit(sip);

cout<<"test"<

}

编译运⾏

g++-losip2-leXosip2

./

#include#include#include#include#include#include#include

intmain(intargc,char*argv[])

{

structeXosip_t*excontext;

eXosip_event_t*je;

osip_message_t*reg=NULL;

osip_message_t*invite=NULL;

osip_message_t*ack=NULL;

osip_message_t*info=NULL;

osip_message_t*message=NULL;intcall_id,dialog_id;inti,flag;intflag1=1;intiReturnCode;char

identity[30]="sip:140@127.0.0.1";//UAC1,端⼝是15060

charregistar[30]="sip:133@127.0.0.1:15061";//UAS,端⼝是15061

charsource_call[30]="sip:140@127.0.0.1";chardest_call[30]="sip:133@127.0.0.1:15061";//identify和register这⼀组地址是

和source和destination地址相同的//在这个例⼦中,uac和uas通信,则source就是⾃⼰的地址,⽽⽬的地址就是uac1的地址

charcommand;chartmp[4096];

std::cout<<"r向服务器注册"<<:endl>

std::cout<<"c取消注册"<<:endl>

std::cout<<"i发起呼叫请求"<<:endl>

std::cout<<"h挂断"<<:endl>

std::cout<<"q推出程序"<<:endl>

std::cout<<"s执⾏⽅法INFO"<<:endl>

std::cout<<"m执⾏⽅法MESSAGE"<<:endl>

excontext=eXosip_malloc();

iReturnCode=eXosip_init(excontext);if(iReturnCode!=0)

{

printf("Can'tinitializeeXosip!n");

return-1;

}el{

printf("eXosip_initsuccessfully!n");

}//绑定uac⾃⼰的端⼝15060,并进⾏端⼝监听

iReturnCode=eXosip_listen_addr(excontext,IPPROTO_UDP,NULL,15060,AF_INET,0);if(iReturnCode!=0)

{

eXosip_quit(excontext);

fprintf(stderr,"Couldn'tinitializetransportlayer!n");

return-1;

}while(true)

{//输⼊命令

std::cout<<"Pleainputthecommand:"<<:endl>

std::cin>>command;

switch(command)

{ca'r':

std::cout<<"Thismodalisnotcompleted!"<<:endl>

break;ca'i'://INVITE,发起呼叫请求

i=eXosip_call_build_initial_invite(excontext,&invite,dest_call,source_call,NULL,"Thisisacallforconversation");if(i!=0)

{

std::cout<<"InitialINVITEfailed!"<<:endl>

break;

}//符合SDP格式,其中属性a是⾃定义格式,也就是说可以存放⾃⼰的信息,//但是只能有两列,⽐如帐户信息//但是经过测试,格式vot

必不可少,原因未知,估计是协议栈在传输时需要检查的

snprintf(tmp,4096,"v=0rn"

"o=anonymous00INIP40.0.0.0rn"

"t=110rn"

"a=urname:rainfishrn"

"a=password:123rn");

osip_message_t_body(invite,tmp,strlen(tmp));

osip_message_t_content_type(invite,"application/sdp");

eXosip_lock(excontext);

i=eXosip_call_nd_initial_invite(excontext,invite);//inviteSIPINVITEmessagetond

eXosip_unlock(excontext);//发送了INVITE消息,等待应答

flag1=1;while(flag1)

{

je=eXosip_event_wait(excontext,0,200);//WaitforaneXosipevent//(超时时间秒,超时时间毫秒)

if(je==NULL)

{

printf("Noresponorthetimeisover!n");

break;

}

switch(je->type)//可能会到来的事件类型

{caEXOSIP_CALL_INVITE://收到⼀个INVITE请求

printf("anewinvitereceived!n");

break;caEXOSIP_CALL_PROCEEDING://收到100trying消息,表⽰请求正在处理中

printf("proceeding!n");

break;caEXOSIP_CALL_RINGING://收到180Ringing应答,表⽰接收到INVITE请求的UA

printf("ringing!n");

printf("call_idis%d,dialog_idis%dn",je->cid,je->did);

break;caEXOSIP_CALL_ANSWERED://收到200OK,表⽰请求已经被成功接受,⽤户应答

printf("ok!connected!n");

call_id=je->cid;

dialog_id=je->did;

printf("call_idis%d,dialog_idis%dn",je->cid,je->did);//回送ack应答消息

eXosip_call_build_ack(excontext,je->did,&ack);

eXosip_call_nd_ack(excontext,je->did,ack);

flag1=0;//推出While循环

break;caEXOSIP_CALL_CLOSED://aBYEwasreceivedforthiscall

printf("theothersidclod!n");

break;caEXOSIP_CALL_ACK://ACKreceivedfor200oktoINVITE

printf("ACKreceived!n");

break;

default://收到其他应答

printf("otherrespon!n");

break;

}

eXosip_event_free(je);//FreeressourceinaneXosipevent

}

break;ca'h'://挂断

printf("Holded!n");

eXosip_lock(excontext);

eXosip_call_terminate(excontext,call_id,dialog_id);

eXosip_unlock(excontext);

break;ca'c':

printf("Thismodalisnotcommpleted!n");

break;ca's'://传输INFO⽅法

eXosip_call_build_info(excontext,dialog_id,&info);

snprintf(tmp,4096,"nThisisasipmessage(Method:INFO)");

osip_message_t_body(info,tmp,strlen(tmp));//格式可以任意设定,text/plain代表⽂本信息;

osip_message_t_content_type(info,"text/plain");

eXosip_call_nd_request(excontext,dialog_id,info);

break;ca'm'://传输MESSAGE⽅法,也就是即时消息,和INFO⽅法相⽐,我认为主要区别是://MESSAGE不⽤建⽴连接,直接传输

信息,⽽INFO消息必须在建⽴INVITE的基础上传输

printf("themethod:MESSAGEn");

eXosip_message_build_request(excontext,&message,"MESSAGE",dest_call,source_call,NULL);//内容,⽅法,to,from

,route

snprintf(tmp,4096,"Thisisasipmessage(Method:MESSAGE)");

osip_message_t_body(message,tmp,strlen(tmp));//假设格式是xml

osip_message_t_content_type(message,"text/xml");

eXosip_message_nd_request(excontext,message);

break;ca'q':

eXosip_quit(excontext);

printf("Exitthetup!n");

flag=0;

break;

}

}

return(0);

}

ViewCode

#include#include#include#include#include#include#include

//#include

intmain(intargc,char*argv[])

{

structeXosip_t*excontext;

eXosip_event_t*je=NULL;

osip_message_t*ack=NULL;

osip_message_t*invite=NULL;

osip_message_t*answer=NULL;

sdp_message_t*remote_sdp=NULL;intcall_id,dialog_id;inti,j,iReturnCode;intid;charsour_call[30]=

"sip:140@127.0.0.1";chardest_call[30]="sip:133@127.0.0.1:15060";//clientip

charcommand;chartmp[4096];charlocalip[128];intpos=0;//初始化sip

excontext=eXosip_malloc();

iReturnCode=eXosip_init(excontext);if(iReturnCode!=0)

{

printf("Can'tinitializeeXosip!n");

return-1;

}el{

printf("eXosip_initsuccessfully!n");

}

iReturnCode=eXosip_listen_addr(excontext,IPPROTO_UDP,NULL,15061,AF_INET,0);if(iReturnCode!=0)

{

eXosip_quit(excontext);

fprintf(stderr,"eXosip_listen_addrerror!nCouldn'tinitializetransportlayer!n");

}for(;;)

{//侦听是否有消息到来

je=eXosip_event_wait(excontext,0,50);//协议栈带有此语句,具体作⽤未知

eXosip_lock(excontext);

eXosip_default_action(excontext,je);//eXosip_automatic_refresh(excontext);

eXosip_unlock(excontext);if(je==NULL)//没有接收到消息

continue;//printf("thecidis%s,didis%s/n",je->did,je->cid);

switch(je->type)

{caEXOSIP_MESSAGE_NEW://新的消息到来

printf("EXOSIP_MESSAGE_NEW!n");if(MSG_IS_MESSAGE(je->request))//如果接受到的消息类型是MESSAGE

{

{

osip_body_t*body;

osip_message_get_body(je->request,0,&body);

printf("Igetthemsgis:%sn",body->body);//printf("thecidis%s,didis%s/n",je->did,je->cid);

}//按照规则,需要回复OK信息

eXosip_message_build_answer(excontext,je->tid,200,&answer);

eXosip_message_nd_answer(excontext,je->tid,200,answer);

}

break;caEXOSIP_CALL_INVITE://得到接收到消息的具体信息

printf("ReceivedaINVITEmsgfrom%s:%s,UrNameis%s,passwordis%sn",je->request->req_uri->host,

je->request->req_uri->port,je->request->req_uri->urname,je->request->req_uri->password);//得到消息体,认为该消息就是SDP

格式.

remote_sdp=eXosip_get_remote_sdp(excontext,je->did);

call_id=je->cid;

dialog_id=je->did;

eXosip_lock(excontext);

eXosip_call_nd_answer(excontext,je->tid,180,NULL);

i=eXosip_call_build_answer(excontext,je->tid,200,&answer);if(i!=0)

{

printf("Thisrequestmsgisinvalid!Cann'trespon!n");

eXosip_call_nd_answer(excontext,je->tid,400,NULL);

}el{

snprintf(tmp,4096,"v=0rn"

"o=anonymous00INIP40.0.0.0rn"

"t=110rn"

"a=urname:rainfishrn"

"a=password:123rn");//设置回复的SDP消息体,下⼀步计划分析消息体//没有分析消息体,直接回复原来的消息,这⼀块做的不好。

osip_message_t_body(answer,tmp,strlen(tmp));

osip_message_t_content_type(answer,"application/sdp");

eXosip_call_nd_answer(excontext,je->tid,200,answer);

printf("nd200over!n");

}

eXosip_unlock(excontext);//显⽰出在sdp消息体中的attribute的内容,⾥⾯计划存放我们的信息

printf("theINFOis:n");while(!osip_list_eol(&(remote_sdp->a_attributes),pos))

{

sdp_attribute_t*at;

at=(sdp_attribute_t*)osip_list_get(&remote_sdp->a_attributes,pos);

printf("%s:%sn",at->a_att_field,at->a_att_value);//这⾥解释了为什么在SDP消息体中属性a⾥⾯存放必须是两列

pos++;

}

break;caEXOSIP_CALL_ACK:

printf("ACKrecieved!n");//printf("thecidis%s,didis%s/n",je->did,je->cid);

break;caEXOSIP_CALL_CLOSED:

printf("theremoteholdthession!n");//eXosip_call_build_ack(dialog_id,&ack);//eXosip_call_nd_ack(dialog_id,ack);

i=eXosip_call_build_answer(excontext,je->tid,200,&answer);if(i!=0)

{

printf("Thisrequestmsgisinvalid!Cann'trespon!n");

eXosip_call_nd_answer(excontext,je->tid,400,NULL);

}el{

eXosip_call_nd_answer(excontext,je->tid,200,answer);

printf("byend200over!n");

}

break;caEXOSIP_CALL_MESSAGE_NEW://⾄于该类型和EXOSIP_MESSAGE_NEW的区别,源代码这么解释的

/*//requestrelatedeventswithincalls(exceptINVITE)

EXOSIP_CALL_MESSAGE_NEW,

//responreceivedforrequestoutsidecalls

EXOSIP_MESSAGE_NEW,

我也不是很明⽩,理解是:EXOSIP_CALL_MESSAGE_NEW是⼀个呼叫中的新的消息到来,⽐如ringtrying都算,所以在接受到后必须

判断

该消息类型,EXOSIP_MESSAGE_NEW⽽是表⽰不是呼叫内的消息到来。

该解释有不妥地⽅,仅供参考。*/printf("EXOSIP_CALL_MESSAGE_NEWn");if(MSG_IS_INFO(je->request))//如果传输的是INFO

⽅法

{

eXosip_lock(excontext);

i=eXosip_call_build_answer(excontext,je->tid,200,&answer);if(i==0)

{

eXosip_call_nd_answer(excontext,je->tid,200,answer);

}

eXosip_unlock(excontext);

{

osip_body_t*body;

osip_message_get_body(je->request,0,&body);

printf("thebodyis%sn",body->body);

}

}

break;

default:

printf("Couldnotparthemsg!n");

}

}

}

ViewCode

编译并运⾏

g++-ouac-losip2-leXosip2-lpthread-losipparr2

g++-ouas-losip2-leXosip2-lpthread-losipparr2

./uas

./uac

exosip对接海康摄像头

#include#include#include#include#include#include#include

staticvoidRegisterSuccess(structeXosip_t*peCtx,eXosip_event_t*je)

{intiReturnCode=0;

osip_message_t*pSRegister=NULL;

iReturnCode=eXosip_message_build_answer(peCtx,je->tid,200,&pSRegister);if(iReturnCode==0&&pSRegister!=NULL

)

{

eXosip_lock(peCtx);

eXosip_message_nd_answer(peCtx,je->tid,200,pSRegister);

eXosip_unlock(peCtx);//osip_message_free(pSRegister);

}

}voidRegisterFailed(structeXosip_t*peCtx,eXosip_event_t*je){intiReturnCode=0;

osip_message_t*pSRegister=NULL;

iReturnCode=eXosip_message_build_answer(peCtx,je->tid,401,&pSRegister);if(iReturnCode==0&&pSRegister!=NULL

)

{

eXosip_lock(peCtx);

eXosip_message_nd_answer(peCtx,je->tid,401,pSRegister);

eXosip_unlock(peCtx);

}

}intmain(intargc,char*argv[])

{structeXosip_t*excontext;

eXosip_event_t*je;

osip_message_t*reg=NULL;

osip_message_t*invite=NULL;

osip_message_t*ack=NULL;

osip_message_t*info=NULL;

osip_message_t*message=NULL;intcall_id,dialog_id;inti,flag;intflag1=1;intiReturnCode;intregisterOk;char*p;char

identity[30]="sip:140@127.0.0.1";//UAC1,端⼝是15060

charregistar[30]="sip:133@127.0.0.1:15061";//UAS,端⼝是15061

charsource_call[30]="sip:140@127.0.0.1";chardest_call[30]="sip:133@127.0.0.1:15061";//identify和register这⼀组地址是

和source和destination地址相同的//在这个例⼦中,uac和uas通信,则source就是⾃⼰的地址,⽽⽬的地址就是uac1的地址

charcommand;chartmp[4096];

std::cout<<"r向服务器注册"<<:endl>

std::cout<<"c取消注册"<<:endl>

std::cout<<"i发起呼叫请求"<<:endl>

std::cout<<"h挂断"<<:endl>

std::cout<<"q推出程序"<<:endl>

std::cout<<"s执⾏⽅法INFO"<<:endl>

std::cout<<"m执⾏⽅法MESSAGE"<<:endl>

excontext=eXosip_malloc();

iReturnCode=eXosip_init(excontext);if(iReturnCode!=0)

{

printf("Can'tinitializeeXosip!n");return-1;

}el{

printf("eXosip_initsuccessfully!n");

}//绑定uac⾃⼰的端⼝15060,并进⾏端⼝监听

iReturnCode=eXosip_listen_addr(excontext,IPPROTO_UDP,NULL,5060,AF_INET,0);if(iReturnCode!=0)

{

eXosip_quit(excontext);

fprintf(stderr,"Couldn'tinitializetransportlayer!n");return-1;

}while(true)

{

eXosip_event_t*je=NULL;

je=eXosip_event_wait(excontext,0,4);if(je==NULL){

std::cout<<"eventisnull"<<:endl>

osip_usleep(100000*50);continue;

}switch(je->type){caEXOSIP_MESSAGE_NEW:

{//printf("newmsgmethod:%sn",je->request->sip_method);

if(MSG_IS_REGISTER(je->request)){

std::cout<<"msgbody:"<<:endl>

registerOk=1;

}elif(MSG_IS_MESSAGE(je->request)){

osip_body_t*body=NULL;

osip_message_get_body(je->request,0,&body);if(body!=NULL){

p=strstr(body->body,"Keepalive");if(p!=NULL){

registerOk=1;

std::cout<<"msgbody:"<<:endl>

std::cout<body<<:endl>

}el{

std::cout<<"msgbody:"<<:endl>

std::cout<body<<:endl>

}

}el{

std::cout<<"getbodyfailed"<<:endl>

}

}elif(strncmp(je->request->sip_method,"BYE",4)!=0){

std::cout<<"unsupportnewmsgmethod:"<request->sip_method<<:endl>

}

RegisterSuccess(excontext,je);

}break;caEXOSIP_MESSAGE_ANSWERED:

{

printf("answeredmethod:%sn",je->request->sip_method);

RegisterSuccess(excontext,je);

}break;caEXOSIP_CALL_ANSWERED:

{

osip_message_t*ack=NULL;

call_id=je->cid;

dialog_id=je->did;

printf("callansweredmethod:%s,call_id:%d,dialog_id:%dn",je->request->sip_method,call_id,dialog_id);

eXosip_call_build_ack(excontext,je->did,&ack);

eXosip_lock(excontext);

eXosip_call_nd_ack(excontext,je->did,ack);

eXosip_unlock(excontext);

}break;caEXOSIP_CALL_PROCEEDING:

{

printf("recvEXOSIP_CALL_PROCEEDINGn");

RegisterSuccess(excontext,je);

}break;caEXOSIP_CALL_REQUESTFAILURE:

{

printf("recvEXOSIP_CALL_REQUESTFAILUREn");

RegisterSuccess(excontext,je);

}break;caEXOSIP_CALL_MESSAGE_ANSWERED:

{

printf("recvEXOSIP_CALL_MESSAGE_ANSWEREDn");

RegisterSuccess(excontext,je);

}break;caEXOSIP_CALL_RELEASED:

{

printf("recvEXOSIP_CALL_RELEASEDn");

RegisterSuccess(excontext,je);

}break;caEXOSIP_CALL_CLOSED:

{

printf("recvEXOSIP_CALL_CLOSEDn");

RegisterSuccess(excontext,je);

}break;caEXOSIP_CALL_MESSAGE_NEW:

{

printf("recvEXOSIP_CALL_MESSAGE_NEWn");

RegisterSuccess(excontext,je);

}break;default:

{

printf("##test,%s:%d,unsupporttype:%dn",__FILE__,__LINE__,je->type);

RegisterSuccess(excontext,je);

}break;

}

eXosip_event_free(je);

}return(0);

}

ViewCode

海康--》sip服务器发送注册包

sip服务器--》海康发送200包

sip服务器--》海康摄像头发送message保活包

问题汇总:

./:errorwhileloadingsharedlibraries:.12:cannotopensharedobjectfile:Nosuchfileordirectory

解决办法:

sudovim/etc/

末尾添加/usr/local/lib

保存退出,执⾏sudoldconfig

参考⽂档:

从海康7816的ps流⾥获取数据h264数据

github:作为上级域,可以对接海康、⼤华、宇视等gb28181平台,获取ps流,转换为标准h.264裸流

python-librtmp

librtmp使⽤的是0.3.0,使⽤树莓派noir官⽅摄像头适配的。

⽬的是能使⽤Python进⾏rtmp推流,⽅便在h264帧⾥加⼊弹幕等操作。通过wireshark抓ffmpeg的包⼀点点改动,最终可以在red5和⽃

鱼上推流了。

importpicameraimporttimeimporttracebackimportctypesfromlibrtmpimport*

globalmeta_packetglobalstart_timeclassWriter():#camera可以通过⼀个类⽂件的对象来输出,实现write⽅法即可

conn=None#rtmp连接

sps=None#记录sps帧,发过以后就不需要再发了(抓包看到ffmpeg是这样的)

pps=None#同上

sps_len=0#同上

pps_len=0#同上

time_stamp=0def__init__(lf,conn):

=conndefwrite(lf,data):try:#寻找h264帧间隔符

indexs=[]

index=0

data_len=len(data)whileindex

data[index+2])==0x00andord(data[index+3])==0x01:

(index)

index=index+3index=index+1

#寻找h264帧间隔符完成

#通过间隔符个数确定类型,树莓派摄像头的第⼀帧是sps+pps同时发的

iflen(indexs)==1:#⾮spspps帧

buf=data[4:len(data)]#裁掉原来的头(00000001),把帧内容拿出来

buf_len=len(buf)

type=ord(buf[0])&0x1f

iftype==0x05:#关键帧,根据wireshark抓包结果,需要拼装spspps帧内容三部分,长度都⽤4个字节表⽰

body0=0x17data_body_array=[bytes(bytearray(

[body0,0x01,0x00,0x00,0x00,(_len>>24)&0xff,(_len>>16)&0xff,

(_len>>8)&0xff,

_len&0xff])),,

bytes(bytearray(

[(_len>>24)&0xff,(_len>>16)&0xff,(_len>>8)&0xff,

_len&0xff])),

,

bytes(bytearray(

[(buf_len>>24)&0xff,(buf_len>>16)&0xff,(buf_len>>8)&0xff,(buf_len)&0xff])),

buf

]

mbody=''.join(data_body_array)

time_stamp=0#第⼀次发出的时候,发时间戳0,此后发真时间戳

_stamp!=0:

time_stamp=int((()-start_time)*1000)

packet_body=RTMPPacket(type=PACKET_TYPE_VIDEO,format=PACKET_SIZE_LARGE,channel=0x06,

timestamp=time_stamp,body=mbody)

packet_.m_nInfoField2=1

el:#⾮关键帧

body0=0x27data_body_array=[bytes(bytearray(

[body0,0x01,0x00,0x00,0x00,(buf_len>>24)&0xff,(buf_len>>16)&0xff,

(buf_len>>8)&0xff,

(buf_len)&0xff])),buf]

mbody=''.join(data_body_array)#if(_stamp==0):

_stamp=int((()-start_time)*1000)

packet_body=RTMPPacket(type=PACKET_TYPE_VIDEO,format=PACKET_SIZE_MEDIUM,channel=0x06,

timestamp=_stamp,body=mbody)

_packet(packet_body)eliflen(indexs)==2:#spspps帧

otNone:returndata_body_array=[bytes(bytearray([0x17,0x00,0x00,0x00,0x00,0x01]))]

sps=data[indexs[0]+4:indexs[1]]

sps_len=len(sps)

pps=data[indexs[1]+4:len(data)]

pps_len=len(pps)

=sps

_len=sps_len

=pps

_len=pps_len

data_body_(sps[1:4])

data_body_(bytes(bytearray([0xff,0xe1,(sps_len>>8)&0xff,sps_len&0xff])))

data_body_(sps)

data_body_(bytes(bytearray([0x01,(pps_len>>8)&0xff,pps_len&0xff])))

data_body_(pps)

data_body=''.join(data_body_array)

body_packet=RTMPPacket(type=PACKET_TYPE_VIDEO,format=PACKET_SIZE_LARGE,channel=0x06,

timestamp=0,body=data_body)

body_.m_nInfoField2=_packet(meta_packet,queue=True)

_packet(body_packet,queue=True)exceptException,e:

_exc()defflush(lf):pass

defget_property_string(string):#返回两字节string长度及string

length=len(string)return''.join([chr((length>>8)&0xff),chr(length&0xff),string])defget_meta_string(string):#按照meta

packet要求格式返回bytes,带02前缀

return''.join([chr(0x02),get_property_string(string)])defget_meta_double(db):

nums=[0x00]

fp=r(ctypes.c_double(db))

cp=(fp,R(ctypes.c_longlong))foriinrange(7,-1,-1):

((>>(i*8))&0xff)return''.join(bytes(bytearray(nums)))defget_meta_boolean(isTrue):

nums=[0x01]if(isTrue):

(0x01)el:

(0x00)return''.join(bytes(bytearray(nums)))

conn=RTMP('rtmp://192.168.199.154/oflaDemo/test',#推流地址

live=True)

_EnableWrite()

t()

start_time=()#拼装视频格式的数据包

meta_body_array=[get_meta_string('@tDataFrame'),get_meta_string('onMetaData'),

bytes(bytearray([0x08,0x00,0x00,0x00,0x06])),#两个字符串和ECMAarray头,共计6个元素,注释掉了⾳频相关数据

get_property_string('width'),get_meta_double(640.0),

get_property_string('height'),get_meta_double(480.0),

get_property_string('videodatarate'),get_meta_double(0.0),

get_property_string('framerate'),get_meta_double(25.0),

get_property_string('videocodecid'),get_meta_double(7.0),#get_property_string('audiodatarate'),get_meta_double(125.0),

#get_property_string('audiosamplerate'),get_meta_double(44100.0),

#get_property_string('audiosamplesize'),get_meta_double(16.0),

#get_property_string('stereo'),get_meta_boolean(True),

#get_property_string('audiocodecid'),get_meta_double(10.0),

get_property_string('encoder'),get_meta_string('Lavf57.56.101'),

bytes(bytearray([0x00,0x00,0x09]))

]

meta_body=''.join(meta_body_array)printmeta_('hex')

meta_packet=RTMPPacket(type=PACKET_TYPE_INFO,format=PACKET_SIZE_LARGE,channel=0x04,

timestamp=0,body=meta_body)

meta_.m_nInfoField2=1#修改streamid

stream=_stream(writeable=True)

ra()ascamera:

_preview()

(2)

_recording(Writer(conn),format='h264',resize=(640,480),intra_period=25,

quality=25)#开始录制,数据输出到Writer的对象⾥

whileTrue:#永远不停⽌

(60)

_recording()

_preview()

srs-librtmp

SRS提供的librtmp

应⽤场景

librtmp的主要应⽤场景包括:

播放RTMP流:譬如rtmpdump,将服务器的流读取后保存为flv⽂件。

推流:提供推流到RTMP服务器。

基于同步阻塞socket,客户端⽤可以了。

arm:编译出来给arm-linux⽤,譬如某些设备上,采集后推送到RTMP服务器。

不⽀持直接发布h.264裸码流,⽽srs-librtmp⽀持,参考:publish-h264-raw-data

备注:关于链接ssl,握⼿协议,简单握⼿和复杂握⼿,参考RTMP握⼿协议

备注:ARM上使⽤srs-librtmp需要交叉编译,参考srs-arm,即使⽤交叉编译环境编译srs-librtmp(可以不依赖于其他库,ssl/st都不需

要)

librtmp做Server

群⾥有很多⼈问,librtmp如何做rver,实在不胜其骚扰,所以单列⼀章。

rver的特点是会有多个客户端连接,⾄少有两个:⼀个推流连接,⼀个播放连接。所以rver有两种策略:

每个连接⼀个线程或进程:像apache。这样可以⽤同步socket来收发数据(同步简单)。坏处就是没法⽀持很⾼并发,1000个已经到顶了,

得开1000个线程/进程啊。

使⽤单进程,但是⽤异步socket:像nginx这样。好处就是能⽀持很⾼并发。坏处就是异步socket⿇烦。

rtmpdump提供的librtmp,当然是基于同步socket的。所以使⽤librtmp做rver,只能采取第⼀种⽅法,即⽤多线程处理多个连接。多

线程多⿇烦啊!要锁,同步,⽽且还⽀持不了多少个。

librtmp的定位就是客户端程序,偏偏要超越它的定位去使⽤,这种⼤约只有中国⼈才能这样“⽆所畏惧”。

嵌⼊式设备上做rtmprver,当然可以⽤srs/crtmpd/nginx-rtmp,轮也轮不到librtmp。

SRS为何提供librtmp

srs提供的客户端srs-librtmp的定位和librtmp不⼀样,主要是:

librtmp的代码确实很烂,⽏庸置疑,典型的代码堆积。

librtmp接⼝定义不良好,这个对⽐srs就可以看出,使⽤起来得看实现代码。

没有实例:接⼝的使⽤最好提供实例,srs提供了publish/play/rtmpdump实例。

最⼩依赖关系:srs调整了模块化,只取出了core/kernel/rtmp三个模块,其他代码没有编译到srs-librtmp中,避免了冗余。

最少依赖库:srs-librtmp只依赖c/c++标准库(若需要复杂握⼿需要依赖openssl,srs也编译出来了,只需要加⼊链接即可)。

不依赖st:srs-librtmp使⽤同步阻塞socket,没有使⽤st(st主要是服务器处理并发需要)。

SRS提供了测速函数,直接调⽤srs-librtmp就可以完成到服务器的测速。参考:BandwidthTest

SRS提供了⽇志接⼝,可以获取服务器端的信息,譬如版本,对应的ssionid。参考:Tracablelog

SRS可以直接导出⼀个srs-librtmp的project,编译成.h和.a使⽤。或者导出为.h和.cpp,⼀个⼤⽂件。参考:exportsrslibrtmp

⼀句话,srs为何提供客户端开发库?因为rtmp客户端开发不⽅便,不直观,不简洁。

ExportSrsLibrtmp

SRS在2.0提供了导出srs-librtmp的编译选项,可以将srs-librtmp单独导出为project,单独编译⽣成.h和.a,⽅便在linux和windows平台

编译。

使⽤⽅法,导出为project,可以make成.h和.a:

dir=/home/winlin/srs-librtmp&&

rm-rf$dir&&

./configure--export-librtmp-project=$dir&&

cd$dir&&make&&

./objs/rearch/librtmp/srs_playrtmp:///live/livestream

SRS将srs-librtmp导出为独⽴可以make的项⽬,⽣成.a静态库和.h头⽂件,以及⽣成了srs-librtmp的所有实例。

还可以直接导出为⼀个⽂件,提供了简单的使⽤实例,其他实例参考rearch的其他例⼦:

dir=/home/winlin/srs-librtmp&&

rm-rf$dir&&

./configure--export-librtmp-single=$dir&&

cd$dir&&_-g-O0-lstdc++-oexample&&

stripexample&&./example

备注:导出⽬录⽀持相对⽬录和绝对⽬录。

编译srs-librtmp

编译SRS时,会⾃动编译srs-librtmp,譬如:

./configure--with-librtmp--without-ssl

编译会⽣成srs-librtmp和对应的实例。

备注:⽀持librtmp只需要打开--with-librtmp,但推荐打开--without-ssl,不依赖于ssl,对于⼀般客户端(不需要模拟flash)⾜够了。这样

srs-librtmp不依赖于任何其他库,在x86/x64/arm等平台都可以编译和运⾏

备注:就算打开了--with-ssl,srslibrtmp也只提供simple_handshake函数,不提供complex_handshake函数。所以推荐关闭ssl,不依

赖于ssl,没有实际的⽤处。

SRS编译成功后,⽤户就可以使⽤这些库开发

Windows下编译srs-librtmp

srs-librtmp可以只依赖于c++和socket,可以在windows下编译。

先使⽤SRS导出srs-librtmp,然后在vs中编译,参考:exportsrslibrtmp

使⽤了⼀些linux的头⽂件,需要做⼀些portal。

注意:srs-librtmp客户端推流和抓流,不需要ssl库。代码都是c++/stl,⽹络部分⽤的是同步socket。

数据格式

srs-librtmp提供了⼀系列接⼝函数,就数据按照⼀定格式发送到服务器,或者从服务器读取⾳视频数据。

数据接⼝包括:

读取数据包:intsrs_read_packet(int*type,u_int32_t*timestamp,char**data,int*size)

发送数据包:intsrs_write_packet(inttype,u_int32_ttimestamp,char*data,intsize)

接⼝接受的的数据(char*data),⾳视频数据,格式为flv的Video/Audio数据。参考srs的doc⽬录的规范⽂件

video_file_format_spec_v10_

⾳频数据格式参考:E.4.2.1AUDIODATA,p76,譬如,aac编码的⾳频数据。

视频数据格式参考:E.4.3.1VIDEODATA,p78,譬如,h.264编码的视频数据。

脚本数据格式参考:E.4.4.1SCRIPTDATA,p80,譬如,onMetadata,流的信息(宽⾼,码率,分辨率等)

数据类型(inttype)定义如下(E.4.1FLVTag,page75):

⾳频:8=audio,宏定义:SRS_RTMP_TYPE_AUDIO

视频:9=video,宏定义:SRS_RTMP_TYPE_VIDEO

脚本数据:18=scriptdata,宏定义:SRS_RTMP_TYPE_SCRIPT

其他的数据,譬如时间戳,都是通过参数接受和发送。

另外,⽂档其他重要信息:

flv⽂件头格式:E.2TheFLVheader,p74。

flv⽂件主体格式:E.3TheFLVFileBody,p74。

tag头格式:E.4.1FLVTag,p75。

使⽤flv格式的原因:

flv的格式⾜够简单。

ffmpeg也是⽤的这种格式

收到流后加上flvtagheader,就可以直接保存为flv⽂件

从flv⽂件解封装数据后,只要将tag的内容给接⼝就可以,flv的tag头很简单。

PublishH.264RawData

SRS-librtmp⽀持发布h.264裸码流,直接调⽤api即可将数据发送给SRS。

总结起来就是说,H264的裸码流(帧)转换RTMP时:

dts和pts是不在h264流中的,外部给出。

SPS和PPS在RTMP⼀个包⾥⾯发出去。

RTMP包=5字节RTMP包头+H264头+H264数据,具体参考:SrsAvcAacCodec::video_avc_demux

直接提供接⼝,发送h264数据,其中包含annexb的头:N[00]000001,whereN>=0.

加了⼀个直接发送h264裸码流的接⼝:

/**

*writeh.264rawframeoverRTMPtortmprver.

*@paramframestheinputh264rawdata,encodedh.264I/P/Bframesdata.

*framescanbeoneormorethanoneframe,

*eachframeprefixedh.264annexbheader,byN[00]000001,whereN>=0,

*forinstance,frame=header(00000001)+payload(6742802995A014016E40)

*aboutannexb,@eH.264-AVC-ISO_IEC_,page211.

*@paamframes_sizethesizeofh264rawdata.

*asrtframes_size>0,atleasthas1bytesheader.

*@paramdtsthedtsofh.264rawdata.

*@paramptstheptsofh.264rawdata.

*

*@remark,urshouldfreetheframes.

*@remark,thetbnofdts/ptsis1/1000forRTMP,thatis,inms.

*@remark,cts=pts-dts

*

*@return0,success;otherswi,failed.

*/

externintsrs_h264_write_raw_frames(srs_rtmp_trtmp,

char*frames,intframes_size,u_int32_tdts,u_int32_tpts

);

⾥⾯的数据是:

//SPS

802995A014016E40

//PPS

CE3880

//IFrame

B8041014C038008B0D0D3A071.....

//PFrame

E02041F8CDDC562BBDEFAD2F.....

调⽤时,可以SPS和PPS⼀起发,帧⼀次发⼀个:

//SPS+PPS

srs_h264_write_raw_frame('802995A014016E4CE3880',size,dts,pts)

//IFrame

srs_h264_write_raw_frame('B8041014C038008B0D0D3A071......',size,dts,pts)

//PFrame

srs_h264_write_raw_frame('E02041F8CDDC562BBDEFAD2F......',size,dts,pts)

调⽤时,可以⼀次发⼀次frame也⾏:

//SPS

srs_h264_write_raw_frame('802995A014016E4',size,dts,pts)

//PPS

srs_h264_write_raw_frame('CE3880',size,dts,pts)

//IFrame

srs_h264_write_raw_frame('B8041014C038008B0D0D3A071......',size,dts,pts)

//PFrame

srs_h264_write_raw_frame('E02041F8CDDC562BBDEFAD2F......',size,dts,pts)

PublishAudioRawStream

srs-librtmp提供了api可以将⾳频裸码流发布到SRS,⽀持AACADTS格式。

API定义如下:

/**

*writeanaudiorawframetosrs.

*notsimilartoh.264video,theaudioneveraggregated,always

*encodedoneframebyone,sothisapiisudtowriteaframe.

*

*@paramsound_lowingvaluesaredefined:

*0=LinearPCM,platformendian

*1=ADPCM

*2=MP3

*3=LinearPCM,littleendian

*4=Nellymor16kHzmono

*5=Nellymor8kHzmono

*6=Nellymor

*7=G.711A-lawlogarithmicPCM

*8=G.711mu-lawlogarithmicPCM

*9=rerved

*10=AAC

*11=Speex

*14=MP38kHz

*15=Device-specificsound

*Formats7,8,14,and15arererved.

*AACissupportedinFlashPlayer9,0,115,0andhigher.

*SpeexissupportedinFlashPlayer10andhigher.

*@paramsound_lowingvaluesaredefined:

*0=5.5kHz

*1=11kHz

*2=22kHz

*3=44kHz

*@paramsound_rameteronlypertainsto

*sdformatsalwaysdecode

*to16bitsinternally.

*0=8-bitsamples

*1=16-bitsamples

*@paramsound_typeMonoorstereosound

*0=Monosound

*1=Stereosound

*@paramtimestampThetimestampofaudio.

*

*@example/trunk/rearch/librtmp/srs_aac_raw_publish.c

*@example/trunk/rearch/librtmp/srs_audio_raw_publish.c

*

*@remarkforaac,theframemustbeinADTSformat.

*@eaac-mp4a-format-ISO_IEC_14496-3+,page75,1.A.2.2ADTS

*@remarkforaac,onlysupportprofile1-4,AACmain/LC/SSR/LTP,

*@eaac-mp4a-format-ISO_IEC_14496-3+,page23,1.5.1.1Audioobjecttype

*

*@eE.4.2.1AUDIODATAofvideo_file_format_spec_v10_

*

*@return0,success;otherswi,failed.

*/

externintsrs_audio_write_raw_frame(srs_rtmp_trtmp,

charsound_format,charsound_rate,charsound_size,charsound_type,

char*frame,intframe_size,u_int32_ttimestamp

);

/**

*whetheraacrawdataisinadtsformat,

*whichbytesquencematches'1'B,thatis0xFFF.

*@paramaac_raw_datatheinputaacrawdata,aencodedaacframedata.

*@paramac_raw_sizethesizeofaacrawdata.

*

*@reamrkudtocheckwhethercurrentframeisinadtsformat.

*@eaac-mp4a-format-ISO_IEC_14496-3+,page75,1.A.2.2ADTS

*@example/trunk/rearch/librtmp/srs_aac_raw_publish.c

*

*@return0fal;otherwi,true.

*/

externsrs_boolsrs_aac_is_adts(char*aac_raw_data,intac_raw_size);

/**

*partheadtsheadertogettheframesize,

*whichbytesquencematches'1'B,thatis0xFFF.

*@paramaac_raw_datatheinputaacrawdata,aencodedaacframedata.

*@paramac_raw_sizethesizeofaacrawdata.

*

*@returnfailedwhen<=0failed;otherwi,ok.

*/

externintsrs_aac_adts_frame_size(char*aac_raw_data,intac_raw_size);

调⽤实例参考#212,以及srs_audio_raw_publish.c和srs_aac_raw_publish.c,参考examples.

srs-librtmpExamples

SRS提供了实例sample,也会在编译srs-librtmp时⾃动编译:

rearch/librtmp/srs_play.c:播放RTMP流实例。

rearch/librtmp/srs_publish.c:推送RTMP流实例。

rearch/librtmp/srs_ingest_flv.c:读取本地FLV⽂件并推送RTMP流实例。

rearch/librtmp/srs_ingest_mp4.c:读取本地MP4⽂件并推送RTMP流实例。

rearch/librtmp/srs_ingest_rtmp.c:读取RTMP流并推送RTMP流实例。

rearch/librtmp/srs_bandwidth_check.c:带宽测试⼯具。

rearch/librtmp/srs_flv_injecter.c:点播FLV关键帧注⼊⽂件。

rearch/librtmp/srs_flv_parr.c:FLV⽂件查看⼯具。

rearch/librtmp/srs_detect_rtmp.c:RTMP流检测⼯具。

rearch/librtmp/srs_h264_raw_publish.c:H.264裸码流发布到SRS实例。

rearch/librtmp/srs_audio_raw_publish.c:Audio裸码流发布到SRS实例。

rearch/librtmp/srs_aac_raw_publish.c:AudioAACADTS裸码流发布到SRS实例。

rearch/librtmp/srs_rtmp_dump.c:将RTMP流录制成flv⽂件实例。

./objs/srs_ingest_hls:将HLS流采集成RTMP推送给SRS。

运⾏实例

启动SRS:

make&&./objs/

推流实例:

make&&./objs/rearch/librtmp/objs/srs_publishrtmp://127.0.0.1:1935/live/livestream

备注:推流实例发送的视频数据不是真正的视频数据,实际使⽤时,譬如从摄像头取出h.264裸码流,需要封装成接⼝要求的数据,然后调

⽤接⼝发送出去。或者直接发送h264裸码流。

播放实例:

make&&./objs/rearch/librtmp/objs/srs_playrtmp:///live/livestreamsuckrtmpstreamlikertmpdump

本文发布于:2023-02-04 19:53:50,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/88/188928.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

下一篇:天使也一样
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图