EasyDarwin的框架理解

最近离职,找了个工作,要求做一个c++的流媒体服务器,研究流媒体服务器,对于我这样,对服务器都是小白的程序员来说,是个极大的挑战,话不多说,成败在此一举,不成,过段时间卷铺盖滚蛋

首先去官网下载了一堆源码

http://www.easydarwin.org/

我首先想到的是试下效果,

于是编译了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 \r\n", 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  \r\n", sample_size);
                //LeaveCriticalSection(&m_cs);
                
                //lTimeStamp += uSampleTime;
            
// #ifdef _WIN32
// 
//                 DWORD dwStop = ::GetTickCount();
// #endif
                //printf("Sleep=%d\r\n", 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 \n", 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\n"); 
                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...\n"); 
                printf("ERROR:Cannot connect to a CMSServer... \n");
                pthread_exit((void *) -1);
        }
        if ( send ( sock_fd, registermsg , strlen(registermsg), 0) == - 1) { 
                perror ( "ERROR:Send error\n" ); 
                printf("ERROR:Send error\n");
        }

        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...\n");
                    printf("ERROR:Select...\n");
                    continue;
                } 
                if (ret == 0) {
                        Sleep(0.1);
                        continue;
                }
                if (FD_ISSET(sock_fd, &fdsr)) {
                        printf("ERROR:recv(sock_fd) %d \n",sock_fd);
                        ret = recv(sock_fd, buf, MAXDATASIZE, 0);
                        if (ret <= 0) { 
                            if(ret==0){
                                 printf("Connection closed\n");
                            }
                            if(ret==SOCKET_ERROR)  { 
                            int err=WSAGetLastError();  

                            if(err==WSAEWOULDBLOCK)  {  
                                continue;  
                            }  
                            else if(err==WSAETIMEDOUT){
                                printf("ERROR:time out  \n");
                            } 
                            else if(err==WSAENETDOWN){
                                printf("ERROR:连接断开  \n");
                            }
                            }
                            }
                                printf("ERROR:Socket closed...ret %d\n",ret);
                               // FD_CLR(sock_fd, &fdsr);
                              //  break;
                        } else {        // receive data
                                buf[ret] = '\0';
                                if(parseDevice->DecodeXml(buf, sock_fd) < 0 ){
                                    printf("ERROR:Decode xml message error...\n");
                                }
                        }
                
        }
        printf("ERROR:CMSServer is down, please restart it ...\n");
        close ( sock_fd); 
        return NULL;

}

分析这段代码的调用

 

 QTSSErrorLogModuleDispatch (QTSS_StateChange_Role) ----------------------------------------->QTSSErrorLogModule.StateChange()
RTSPSession里面的 kFilteringRequest里面 QTSSAdminModuleDispatch(QTSS_RTSPFilter_Role)------->或者 QTSSAdminModule.FilterRequest()
                                                                                                                                                          或者 RereadPrefsTask.Run() 
 ||||
 ||||
 ||||                                                                
 QTSServer::RereadPrefsService(更新服务器上面的配置)-----》case QTSS_RereadPrefs_Role:  return RereadPrefs();--->pthread_create(&lpt, NULL, ListeningThread, NULL);--> 绑定本地的端口 8000端口,并且发送了一个消息,等待回应
 
这个地方开启了一个线程,然后向cms(本地8000端口)发送了消息,请求更新配置参数,然而我当前的项目实际中是不需要cms的,所以我把开启线程禁掉
QTSS_Error RereadPrefs()
{
    delete [] sDevicePrefs;
    sDevicePrefs = QTSSModuleUtils::GetStringAttribute(sPrefs, "device_prefs_file", sDefaultDevicePrefs);
    parseDevice = NEW CParseDevice();
    if (success != parseDevice->Init())
    {
        qtss_printf("ERROR:parseDevice Init fail\n");
        return QTSS_Unimplemented;
    }
    if (success != parseDevice->LoadDeviceXml(sDevicePrefs))
    {
        qtss_printf("ERROR:parseDevice LoadDeviceXml %s fail\n", sDevicePrefs);
    }
    //int lthread = pthread_create(&lpt, NULL, ListeningThread, NULL);
    //if(lthread < 0){
    //    qtss_printf("ERROR:Listen thread create failed!\n");
    //}
    return QTSS_NoErr;
}

所以关掉这个线程就正常运行

 这里是一次拉流的过程的日志
其中rtsp://114.55.107.180:10554/683776_s.sdp不存在流,所以试一次失败的拉流的日志
前面的一段是正常启动服务器日志,之后,就是一次请求拉流,拉流转接,转接的地址失败,关掉各种流的过程
F:\easydarwin\other_version\DarwinServer-master\DarwinServer-master\EasyDarwin\WinNTSupport\Release>EasyDarwin.exe -c ./easydarwin.xml -d
WARNING: No module folder exists.
INFO: Module Loaded...QTSSFileModule [static]
QTSSFileModule QTSS_Register_Role!
INFO: Module Loaded...QTSSReflectorModule [static]
QTSSReflectorModule QTSS_Register_Role!
INFO: Module Loaded...QTSSOnDemandRelayModule [static]
QTSSOnDemandRelayModule QTSS_Register_Role!
INFO: Module Loaded...QTSSAccessLogModule [static]
QTSSAccessLogModule QTSS_Register_Role!
INFO: Module Loaded...QTSSFlowControlModule [static]
QTSSFlowControlModule QTSS_Register_Role!
INFO: Module Loaded...QTSSPosixFileSysModule [static]
QTSSPosixFileSysModule QTSS_Register_Role!
INFO: Module Loaded...QTSSAdminModule [static]
QTSSAdminModule QTSS_Register_Role!
INFO: Module Loaded...QTSSMP3StreamingModule [static]
QTSSMP3StreamingModule QTSS_Register_Role!
INFO: Module Loaded...QTSSAccessModule [static]
QTSSFileModule QTSS_Initialize_Role!
QTSSReflectorModule QTSS_Initialize_Role!
QTSSOnDemandRelayModule QTSS_Initialize_Role!
ServerAddr is :114.55.107.180
QTSSAccessLogModule QTSS_Initialize_Role !
QTSSFlowControlModule QTSS_Initialize_Role!
QTSSPosixFileSysModule QTSS_Initialize_Role!
QTSSAdminModule QTSS_Initialize_Role!
QTSSMP3StreamingModule QTSS_Initialize_Role!
mongoose listen on port:80 document path:./
QTSSPosixFileSysModule QTSS_OpenFile_Role!
QTSSPosixFileSysModule QTSS_ReadFile_Role!
QTSSPosixFileSysModule QTSS_CloseFile_Role!
QTSSPosixFileSysModule QTSS_OpenFile_Role!
QTSSPosixFileSysModule QTSS_ReadFile_Role!
QTSSPosixFileSysModule QTSS_CloseFile_Role!
Streaming Server done starting up


event Context 有socket数据过来! TCPListenerSocket::ProcessEvent TCPListenerSocket::ProcessEvent accept fFileDesc 352 osSocket is 1092 TCPListenerSocket::ProcessEvent GetSessionTask theTask->fTaskName is live_RTSPSession TCPListenerSocket::ProcessEvent RequestEvent event Context 有socket数据过来! EventContext::ProcessEvent fTask->Signal(Task::kReadEvent) RTSPSession kReadingFirstRequest RTSPSession kHTTPFilteringRequest RTSPSession kHaveNonTunnelMessage RTSPSession kFilteringRequest QTSSAdminModule QTSS_RTSPFilter_Role! QTSSMP3StreamingModule QTSS_RTSPFilter_Role! RTSPSession kPostProcessingRequest RTSPSession kSendingResponse RTSPSession kCleaningUp RTSPSession kReadingRequest RTSPSession kHaveNonTunnelMessage RTSPSession kFilteringRequest QTSSAdminModule QTSS_RTSPFilter_Role! QTSSMP3StreamingModule QTSS_RTSPFilter_Role! RTSPSession kRoutingRequest QTSSReflectorModule QTSS_RTSPRoute_Role! RTSPSession kPreprocessingRequest QTSSReflectorModule QTSS_RTSPPreProcessor_Role! QTSSPosixFileSysModule QTSS_OpenFile_Role! QTSSOnDemandRelayModule QTSS_RTSPPreProcessor_Role! QTSSOnDemandRelayModule ProcessRTSPRequest QTSSOnDemandRelayModule DoDescribe() QTSSOnDemandRelayModule DoDescribe() theUriStr is ipC1 New Connection ipC1:rtsp://114.55.107.180:10554/683776_s.sdp QTSSOnDemandRelayModule DoDescribe() NEW RTSPRelaySession m_szSourceUrl is rtsp://114.55.107.180:10554/683776_s.sdp RTSPRelaySession::SendDescribe() RTSPClient::sendDescribeCommand() RTSPClient::sendRequest() RTSPClient::openConnection() RTSPClient::connectToServer() event Context 有socket数据过来! TCPListenerSocket::ProcessEvent TCPListenerSocket::ProcessEvent accept fFileDesc 352 osSocket is 1124 TCPListenerSocket::ProcessEvent GetSessionTask theTask->fTaskName is live_RTSPSession TCPListenerSocket::ProcessEvent RequestEvent event Context 有socket数据过来! EventContext::ProcessEvent fTask->Signal(Task::kReadEvent) RTSPClient::connectionHandler() RTSPClient connectionHandler1() [URL:"rtsp://114.55.107.180:10554/683776_s.sdp"]: Failed to get a SDP description: Connection to server failed: Unknown error QTSSOnDemandRelayModule DoDescribe() SendErrorResponse Disconnect ipC1:rtsp://114.55.107.180:10554/683776_s.sdp RTSPSession kPostProcessingRequest QTSSAccessLogModule QTSS_RTSPPostProcessor_Role ! RTSPSession kSendingResponse RTSPSession kCleaningUp QTSSMP3StreamingModule QTSS_RTSPSessionClosing_Role! RTSPSession kReadingFirstRequest RTSPSession kHTTPFilteringRequest RTSPSession kHaveNonTunnelMessage RTSPSession kFilteringRequest QTSSAdminModule QTSS_RTSPFilter_Role! QTSSMP3StreamingModule QTSS_RTSPFilter_Role! [URL:"RTSPSession kPostProcessingRequest rtsp://114.55.107.180:10554/683776_s.sdpRTSPSession kSendingResponse "]: RTSPSession kCleaningUp Closing the stream. RTSPSession kReadingRequest Disconnect complete QTSSMP3StreamingModule QTSS_RTSPSessionClosing_Role!
QTSSFileModule QTSS_ClientSessionClosing_Role!
QTSSReflectorModule QTSS_ClientSessionClosing_Role!
QTSSOnDemandRelayModule QTSS_ClientSessionClosing_Role
QTSSAccessLogModule QTSS_ClientSessionClosing_Role !

 

 
posted @ 2017-04-25 20:47  balder_m  阅读(12685)  评论(7编辑  收藏  举报