• 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 ", 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] = '';
                                    if(parseDevice->DecodeXml(buf, sock_fd) < 0 ){
                                        printf("ERROR:Decode xml message error...
    ");
                                    }
                            }
                    
            }
            printf("ERROR:CMSServer is down, please restart it ...
    ");
            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
    ");
            return QTSS_Unimplemented;
        }
        if (success != parseDevice->LoadDeviceXml(sDevicePrefs))
        {
            qtss_printf("ERROR:parseDevice LoadDeviceXml %s fail
    ", sDevicePrefs);
        }
        //int lthread = pthread_create(&lpt, NULL, ListeningThread, NULL);
        //if(lthread < 0){
        //    qtss_printf("ERROR:Listen thread create failed!
    ");
        //}
        return QTSS_NoErr;
    }

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

     这里是一次拉流的过程的日志
    其中rtsp://114.55.107.180:10554/683776_s.sdp不存在流,所以试一次失败的拉流的日志
    前面的一段是正常启动服务器日志,之后,就是一次请求拉流,拉流转接,转接的地址失败,关掉各种流的过程
    F:easydarwinother_versionDarwinServer-masterDarwinServer-masterEasyDarwinWinNTSupportRelease>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 !
     
  • 相关阅读:
    LeetCode
    LeetCode
    LeetCode
    LeetCode
    codevs 2977 二叉堆练习1x
    codevs 2010 求后序遍历x
    二叉树的序遍历x(内含结构体与非结构体版x)
    医院设置x
    求后序遍历x
    [LightOJ1017]Brush (III)(dp)
  • 原文地址:https://www.cnblogs.com/baldermurphy/p/6763994.html
Copyright © 2020-2023  润新知