最近离职,找了个工作,要求做一个c++的流媒体服务器,研究流媒体服务器,对于我这样,对服务器都是小白的程序员来说,是个极大的挑战,话不多说,成败在此一举,不成,过段时间卷铺盖滚蛋
首先去官网下载了一堆源码
我首先想到的是试下效果,
于是编译了EasyPusher,得到EasyPusher_File.exe
编译EasyDarwin 得到EasyDarwin.exe
EasyDarwin.exe支持程序调试的方式启动,也支持程序以服务的形式运行,由于调试所以执行EasyDarwin.exe -c ./easydarwin.xml -d
然后向服务器推送EasyPusher_FILE.exe -d 192.168.1.146 -p 554 -n easypusher_file.sdp 其中的端口是在easydarwin.xml里面配置的
然后查看通过http://localhost:10008/api/v1/getrtsplivesessions (我这里是本机所以localhost)查看流列表,
{ "EasyDarwin" : { "Body" : { "SessionCount" : "0" }, "Header" : { "CSeq" : "1", "MessageType" : "MSG_SC_RTSP_LIVE_SESSIONS_ACK", "Version" : "1.0" } } }
有流的时候
{ "EasyDarwin" : { "Body" : { "SessionCount" : "1", "Sessions" : [ { "Channel" : 1, "NumOutputs" : 0, "index" : 0, "name" : "easypusher_file.sdp", "url" : "rtsp://192.168.1.146:554/easypusher_file.sdp" } ] }, "Header" : { "CSeq" : "1", "MessageType" : "MSG_SC_RTSP_LIVE_SESSIONS_ACK", "Version" : "1.0" } } }
于是我便把rtsp://192.168.1.146:554/easypusher_file.sdp放入到VLC中播放,结果居然播放不了
问题就来了
为何运行不了呢?
使用EasyPusher-master里面的EasyPusher_File推送文件给服务器
在EasyPusher_File的main.cpp里面 printf("send video %d size data ", sample_size);
unsigned int _stdcall VideoThread(void* lParam) { int nTrackId = (int)lParam; while (g_bThreadLiving[nTrackId]) { g_bVideoStart = true; int chunk_offset_amount = g_root.co[nTrackId].chunk_offset_amount; unsigned long lTimeStamp = 0; int nSampleId = 0; for(int chunk_index = 0 ; chunk_index < chunk_offset_amount; ++chunk_index) { if (!g_bThreadLiving[nTrackId]) { return 0; } //copy_sample_data(g_fin, chunk_index, name,nID,root,nSampleId); _fseeki64(g_fin, g_root.co[nTrackId].chunk_offset_from_file_begin[chunk_index], SEEK_SET); //获取当前chunk中有多少个sample uint32_t sample_num_in_cur_chunk_ = get_sample_num_in_cur_chunk(g_root.sc[nTrackId], chunk_index+1); //@a mark获取chunk中sample的数目 uint32_t sample_index_ = get_sample_index(g_root.sc[nTrackId], chunk_index+1);//chunk中第一个sample的序号 unsigned int cur=_ftelli64(g_fin); for(int i = 0; i < sample_num_in_cur_chunk_; i++) { if (!g_bThreadLiving[nTrackId]) { return 0; } // #ifdef _WIN32 // DWORD dwStart = ::GetTickCount(); // #endif uint32_t sample_size = get_sample_size(g_root.sz[nTrackId], sample_index_+i);//获取当前sample的大小 uint32_t sample_time = get_sample_time(g_root.ts[nTrackId], nSampleId ); //double dbSampleTime = (double)sample_time/g_root.trk[nTrackId].mdia.mdhd.timescale ; //uint32_t uSampleTime = dbSampleTime*1000000; EnterCriticalSection(&m_cs); uint32_t uSampleTime = Sync_clock(g_root.trk[nTrackId].mdia.mdhd.timescale, sample_time,VEDIO_PUSH, &lTimeStamp); LeaveCriticalSection(&m_cs); _fseeki64(g_fin,cur,SEEK_SET); unsigned char *ptr=new unsigned char [sample_size]; fread(ptr, sample_size, 1, g_fin); //写一帧数据 --- 可以直接进行网络推送 //fwrite(ptr, sample_size, 1, fout); EASY_AV_Frame avFrame; memset(&avFrame, 0x00, sizeof(EASY_AV_Frame)); ptr[0] = 0x00; ptr[1] = 0x00; ptr[2] = 0x00; ptr[3] = 0x01; unsigned char naltype = ( (unsigned char)ptr[4] & 0x1F); avFrame.pBuffer = (unsigned char*)ptr; avFrame.u32AVFrameLen = sample_size; avFrame.u32VFrameType = (naltype==0x05)?EASY_SDK_VIDEO_FRAME_I:EASY_SDK_VIDEO_FRAME_P; avFrame.u32AVFrameFlag = EASY_SDK_VIDEO_FRAME_FLAG; avFrame.u32TimestampSec = lTimeStamp/1000000; avFrame.u32TimestampUsec = (lTimeStamp%1000000); //EnterCriticalSection(&m_cs); EasyPusher_PushFrame(g_fPusherHandle, &avFrame); printf("send video %d size data ", sample_size); //LeaveCriticalSection(&m_cs); //lTimeStamp += uSampleTime; // #ifdef _WIN32 // // DWORD dwStop = ::GetTickCount(); // #endif //printf("Sleep=%d ", uSampleTime/1000-(dwStop-dwStart)); if(uSampleTime!=0) { #ifndef _WIN32 usleep(uSampleTime); #else SleepEx(uSampleTime/1000, FALSE); #endif } delete [] ptr; cur+=sample_size; nSampleId++; } } } return 0; }
我在接受收数据包的地方
QTSSReflectorModule.cpp--》ProcessRTPData
char* packetData = inParams->inPacketData; UInt8 packetChannel; packetChannel = (UInt8)packetData[1]; UInt16 packetDataLen; memcpy(&packetDataLen, &packetData[2], 2); packetDataLen = ntohs(packetDataLen); char* rtpPacket = &packetData[4]; printf("receve %d size data ,packetChannel is %d ", packetDataLen, packetChannel);
看到发送和接受数据量是一样的,所以服务器接受了数据,只是没有做转发处理
查阅了资料
http://www.easydarwin.org/article/EasyDarwin/38.html
他其中说的一个模块叫QTSSOnDemandRelayModule,这个是负责转发的模块,而http://www.easydarwin.org/里面的源代码不含有这个模块
最后考万能的百度搜索QTSSOnDemandRelayModule,找到了https://github.com/parsons-smith/DarwinServer
老规矩,编译,修改,直到编译通过,运行,不过发现一个问题,就是运行一会儿直接就崩溃了
找到这个地方
void *ListeningThread(void *arg){ if ((sock_fd = socket ( AF_INET , SOCK_STREAM , 0)) == - 1) { perror ("ERROR:Socket error "); pthread_exit((void *) -1); } memset ( &server_addr, 0, sizeof(struct sockaddr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons (CMS_SERVER_PORT); server_addr.sin_addr.s_addr = inet_addr(CMS_SERVER_IP); //默认连接本地的8000端口 if ( connect ( sock_fd, ( struct sockaddr * ) & server_addr, sizeof( struct sockaddr ) ) == -1) { perror ("ERROR:Cannot connect to a CMSServer... "); printf("ERROR:Cannot connect to a CMSServer... "); pthread_exit((void *) -1); } if ( send ( sock_fd, registermsg , strlen(registermsg), 0) == - 1) { perror ( "ERROR:Send error " ); printf("ERROR:Send error "); } fd_set fdsr; int ret; struct timeval tv; while(1){ tv.tv_sec = 30; tv.tv_usec = 0; FD_ZERO(&fdsr); FD_SET(sock_fd, &fdsr); ret = select(sock_fd + 1, &fdsr, NULL, NULL, &tv); if (ret < 0) { perror("ERROR:Select... "); printf("ERROR:Select... "); continue; } if (ret == 0) { Sleep(0.1); continue; } if (FD_ISSET(sock_fd, &fdsr)) { printf("ERROR:recv(sock_fd) %d ",sock_fd); ret = recv(sock_fd, buf, MAXDATASIZE, 0); if (ret <= 0) { if(ret==0){ printf("Connection closed "); } if(ret==SOCKET_ERROR) { int err=WSAGetLastError(); if(err==WSAEWOULDBLOCK) { continue; } else if(err==WSAETIMEDOUT){ printf("ERROR:time out "); } else if(err==WSAENETDOWN){ printf("ERROR:连接断开 "); } } } printf("ERROR:Socket closed...ret %d ",ret); // FD_CLR(sock_fd, &fdsr); // break; } else { // receive data buf[ret] = '