zoukankan      html  css  js  c++  java
  • 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 !
     
  • 相关阅读:
    Vsftpd 3.0.2 正式版发布
    Putdb WebBuilder 6.5 正式版本发布
    SoaBox 1.1.6 GA 发布,SOA 模拟环境
    pynag 0.4.6 发布,Nagios配置和插件管理
    Percona Playback 0.4,MySQL 负荷回放工具
    xombrero 1.3.1 发布,微型 Web 浏览器
    Hypertable 0.9.6.4 发布,分布式数据库
    libmemcached 1.0.11 发布
    CryptoHeaven 3.7 发布,安全邮件解决方案
    Android Activity生命周期
  • 原文地址:https://www.cnblogs.com/baldermurphy/p/6763994.html
Copyright © 2011-2022 走看看