rtsp录制成mp4

来源:互联网 发布:apache tiles 编辑:程序博客网 时间:2024/05/17 22:26

将RTSP流录制为mp4文件


录制法度要持续添加新功能:模仿电视,板卡发送出来的是rtsp流(h264视频+alaw(pcma)音频)。


因为之前做过将rtp流(h264视频+aac音频)录制合成mp4文件(拜见http://www.cnblogs.com/chutianyao/archive/2012/04/13/2446140.html),很天然的就决意将其合成为mp4文件。


 


然则有些不合:


(1)须要解析RTSP和谈。研究了一下RFC2326,发明也不是很错杂。


  rtsp分把握流和数据流:把握流就是客户端向办事端发送把握号令,包含查看节目信息、播放、停止节目等,一般是经由过程TCP和谈通信的;数据流就是办事端将音视频数据发送到指定的地址、端口上,我们的音频和视频零丁发送到两个不合的端口上,采取的是UDP和谈。采取TCP或UDP,在RTSP和谈中并没有明白规定,可以按照实际景象断定。


  把握流采取的是HTTP文本和谈,斗劲简单、便利调试,这个RTSP和谈中也没有规定必须应用HTTP,不过一般都是采取HTTP来实现的。


 


  大致步调:


  1. 客户端连接rtsp办事器,发送option办法;办事器返回可用的办法,凡是有DESCRIBE,SETUP,PLAY,TEARDOWN等,因为板卡端的rtsp办事法度也是我们本身实现的,可以确保已经实现了这些办法,是以客户端就没有进行搜检了;


  2. 客户端发送DESCRIBE办法,办事器返回RTSP流的相干信息,包含video stream,audio stream的个数、码率、辨别率等参数信息;


  3. 按照返回的参数信息,客户端决意要播放哪些video stream,audio stream,发送SETUP办法;


   我们的RTSP流为:一个alaw audio 和一个h264 video,须要指定音视频数据分别发送到哪个端口上,经由过程下面的代码来机关发送消息: 



 1 int RTSP::Set_Setup() 2 { 3     int nRet = -1; 4     int m_nIndex = 0; 5  6     if (m_pBuf != NULL) 7     { 8 //        if (m_pContentBase == NULL) 9 //        {10 //            sprintf(m_pBuf, "SETUP %s/%s %s\r\n", m_strUrl.c_str(), m_pMedia->p_control, RTSP_VERSSION);11 //        }12 //        else13 //        {14 //            sprintf(m_pBuf, "SETUP %s%s %s\r\n", m_pContentBase, m_pMedia->p_control, RTSP_VERSSION);15 //        }16 //        printf("m_pContentBase:%s\n", m_pContentBase);17 //        printf("m_strUrl:%s\n", m_strUrl.c_str());18 //        printf("m_pMedia->p_control:%s\n", m_pMedia->p_control);19 //        printf("m_pBuf:%s\n", m_pBuf);20         sprintf(m_pBuf, "SETUP %s %s\r\n", m_pMedia->p_control, RTSP_VERSSION);21 22         m_nIndex = strlen(m_pBuf);23         sprintf(m_pBuf + m_nIndex, "CSeq: %d\r\n", m_nSeqNum);24         m_nIndex = strlen(m_pBuf);25 26         if (m_pMedia->i_media_type == VIDEO)27         {28             GetVideoPort();29             sprintf(m_pBuf + m_nIndex, "Transport: %s;%s;client_port=%d-%d\r\n""RTP/AVP""unicast", m_nVideoPort, m_nVideoPort + 1);30             m_nIndex = strlen(m_pBuf);31         }32         else if (m_pMedia->i_media_type == AUDIO)33         {34             GetAudioPort();35             sprintf(m_pBuf + m_nIndex, "Transport: %s;%s;client_port=%d-%d\r\n""RTP/AVP""unicast", m_nAudioPort, m_nAudioPort + 1);36             m_nIndex = strlen(m_pBuf);37         }38 39         if (m_pSession[0] != 040         {41             sprintf(m_pBuf + m_nIndex, "Session: %s\r\n", m_pSession);42             m_nIndex = strlen(m_pBuf);43         }44 45         sprintf(m_pBuf + m_nIndex, "User-Agent: %s\r\n", USER_AGENT_STR);46         m_nIndex = strlen(m_pBuf);47         sprintf(m_pBuf + m_nIndex, "\r\n");48         m_nIndex = strlen(m_pBuf);49         m_nBufSize = m_nIndex;50 51         nRet = 0;52     }53 54     return nRet;55 }


 


  4. SETUP成功之后,经由过程PLAY号令就可以进行播放了:



 1 int RTSP::Set_Play() 2 { 3     int nRet = -1; 4     int m_nIndex = 0; 5  6     if (m_pBuf != NULL) 7     { 8         sprintf(m_pBuf, "PLAY %s %s\r\n", m_strUrl.c_str(), RTSP_VERSSION); 9         m_nIndex = strlen(m_pBuf);10         sprintf(m_pBuf + m_nIndex, "CSeq: %d\r\n", m_nSeqNum);11         m_nIndex = strlen(m_pBuf);12         sprintf(m_pBuf + m_nIndex, "Session: %s\r\n", m_pSession);13         m_nIndex = strlen(m_pBuf);14         sprintf(m_pBuf + m_nIndex, "Range: npt=0.000-\r\n");15         m_nIndex = strlen(m_pBuf);16         sprintf(m_pBuf + m_nIndex, "User-Agent: %s\r\n", USER_AGENT_STR);17         m_nIndex = strlen(m_pBuf);18         sprintf(m_pBuf + m_nIndex, "\r\n");19         m_nIndex = strlen(m_pBuf);20         m_nBufSize = m_nIndex;21 22         nRet = 0;23     }24 25     return nRet;26 }


  如许我们就可以在刚才指定的端口上接管UDP的音视频数据了。


 更具体的可以参考rtsp和谈的实现。


 


(2)合成MP4.


我们已经知道音视频格局分别为:alaw(pcma), h264;查看文档发明,mp4v2正好支撑这两种格局,剩下就很简单了:



 1 bool COutputATV::CreateMp4File(string filename) 2 { 3     m_Mp4File = MP4CreateEx(filename.c_str()); 4     if (m_Mp4File == MP4_INVALID_FILE_HANDLE) 5     { 6         return false; 7     } 8  9     MP4SetTimeScale(m_Mp4File, 90000);10     m_nVideoTrack = MP4AddH264VideoTrack(m_Mp4File,11                                          90000//timescale12                                          3214//sample duration:/*(90000 / 25)*/13                                                                                           /*  NOTICE:14                                                                                            *  why 3214? read the commets below.15                                                                                            */16                                          320//width:17                                          240//height:18                                          0 x64//sps[1] AVCProfileIndication19                                          0 x00//sps[2] profile_compat20                                          0 x1f//sps[3] AVCLevelIndication21                                          3); // 4 bytes length before each NAL unit22     if (m_nVideoTrack == MP4_INVALID_TRACK_ID)23     {24         LOG(LOG_TYPE_ERROR, "CreateMp4File():MP4AddH264VideoTrack() failed.");25         return false;26     }27     MP4SetVideoProfileLevel(m_Mp4File, 0 x7F);28 29     m_nAudioTrack = MP4AddALawAudioTrack(m_Mp4File,30                                          8000//timescale31                                          500);  //sampleDuration.32     /* NOTICE:33      * in standard release of mp4v2 library(v1.9.1, and trunk-r479),the function MP4AddALawAudioTrack() does not specify the 3rd param:34      * ""sampleDuration"", it calculate a fixed duration value with the following formula:35      *                      uint32_t fixedSampleDuration = (timeScale * 20)/1000; // 20mSec/Sample36      * please read the source code of MP4AddALawAudioTrack().37      * they can do it in this way because RFC3551 defines PCMA(a-law) as 20msec per sample, so the duration is a fixed value, please read RFC38      * 3551:http://www.ietf.org/rfc/rfc3551.txt39      * but, the souce boards"" we used does not follow the RFC specifition, we found the sample duration value is 500.40      * (why the param is 500? every rtp packet contains  a timestamp, the duration is the difference of two samples(not rtp packets), the same as41      * h264 tracks in rtp). SO:42      * I modified the declarion of MP4AddALawAudioTrack(), add the 3rd param:""sampleDuration"", to pass the actual duration value,I also modified43      * the implmention of MP4AddALawAudioTrack().44      *45      * as a result:46      * ***************************               IMPORTANT                ***************************47      * when distribute the Record software, you MUST use the mp4v2 library distribute with it,48      * please DO NOT use the standard release download  network!49      * ***********************************************************************************50      *51      * we use the default value of duration when creating mp4 file, we will modify it later when begin to write the first two samples with its52      * actual value.53      *54      * Added by:Zhengfeng Rao.55      * 2012-05-0856      */57 58     MP4SetTrackIntegerProperty(m_Mp4File,59                                m_nAudioTrack,60                                "mdia.minf.stbl.stsd.alaw.channels"61                                 1);62 63     if (m_nAudioTrack == MP4_INVALID_TRACK_ID)64     {65         LOG(LOG_TYPE_ERROR, "CreateMp4File():MP4AddAudioTrack() failed.");66         return false;67     }68     MP4SetAudioProfileLevel(m_Mp4File, 0 x02);69 70     return true;71 }


 


写音视频数据:



  1 void COutputATV::DecodeRtp(unsigned char *pbuf, int datalength)  2 {  3     if((pbuf == NULL) || (datalength <= 0))  4     {  5         return;  6     }  7   8     rtp_header_t rtp_header;  9     char cType = pbuf[0]; 10  11     //the 1st byte indicate the node is audio/video, it""s added by the input thread, so we need to remove it. 12     pbuf += 1; 13     datalength -= 1; 14     int i_header_size = GetRtpHeader(&rtp_header, pbuf, datalength); 15  16     if(i_header_size <=0 17     { 18         LOG(LOG_TYPE_ERROR, "COutputATV::DecodeRtp() Invalid header size:%d", i_header_size); 19         return; 20     } 21  22     if(cType == ""A"" 23     { 24         if (rtp_header.i_pt == 0 x8//AUDIO 25         { 26             int i_size = datalength - i_header_size; 27             if (m_nAudioTimeStamp == 0 28             { 29                 m_nAudioTimeStamp = rtp_header.i_timestamp; 30             } 31  32             if (m_nAudioTimeStamp != rtp_header.i_timestamp)//got a frame 33             { 34                 MP4WriteSample(m_Mp4File, m_nAudioTrack, m_pAudioFrame, m_nAudioFrameIndex); 35                 m_nAudioFrameIndex = 0; 36  37                 m_nAudioTimeStamp = rtp_header.i_timestamp; 38                 memcpy(m_pAudioFrame + m_nAudioFrameIndex, pbuf + i_header_size, i_size); 39                 m_nAudioFrameIndex += i_size; 40             } 41             else 42             { 43                 memcpy(m_pAudioFrame + m_nAudioFrameIndex, pbuf + i_header_size, i_size); 44                 m_nAudioFrameIndex += i_size; 45             } 46         } 47         else 48         { 49             //INVALID packet. 50         } 51     } 52     else if(cType == ""V"" 53     { 54         if (rtp_header.i_pt == 0 x60// VIDEO 55         { 56             char p_save_buf[4096] = {0}; 57             int i_size = RtpToH264(pbuf, datalength, p_save_buf, &m_nNaluOkFlag, &m_nLastPktNum); 58             if(i_size <= 0 59             { 60                 DumpFrame(pbuf, datalength); 61                 LOG_PERIOD(LOG_TYPE_WARN, "RtpToH264() Illegal packet, igonred. datalength = %d, i_size = %d", datalength-1, i_size); 62                 return; 63             } 64  65             if (m_nVideoTimeStamp == 0 66             { 67                 m_nVideoTimeStamp = rtp_header.i_timestamp; 68  69                 m_nVideoFrameIndex = 0; 70                 memcpy(m_pVideoFrame + m_nVideoFrameIndex, p_save_buf, i_size); 71                 m_nVideoFrameIndex += i_size; 72             } 73  74             if (m_nVideoTimeStamp != rtp_header.i_timestamp || p_save_buf[12] == 0 x78 75             { 76                 if (m_nVideoFrameIndex >= 4 77                 { 78                     unsigned int* p = (unsigned int*) (&m_pVideoFrame[0]); 79                     *p = htonl(m_nVideoFrameIndex - 4); 80  81                    MP4WriteSample(m_Mp4File, m_nVideoTrack, m_pVideoFrame, m_nVideoFrameIndex, MP4_INVALID_DURATION, 01); 82                     //DumpFrame(m_pVideoFrame, m_nVideoFrameIndex); 83                 } 84  85                 m_nVideoFrameIndex = 0; 86                 m_nVideoTimeStamp = rtp_header.i_timestamp; 87                 memcpy(m_pVideoFrame + m_nVideoFrameIndex, p_save_buf, i_size); 88                 m_nVideoFrameIndex += i_size; 89             } 90             else 91             { 92                 //printf("2.3.3*************i_size:%d, m_nVideoFrameIndex:%d\n", i_size, m_nVideoFrameIndex); 93                 memcpy(m_pVideoFrame + m_nVideoFrameIndex, p_save_buf, i_size); 94                 m_nVideoFrameIndex += i_size; 95             } 96         } 97         else 98         { 99             //INVALID packet.100         }101     }102     else103     {104         //INVALID packet.105     }106 }


 


须要申明的是:


libmp4v2经由过程MP4AddALawAudioTrack(mp4file, timescale,sampleDuration)添加alaw音频时,第三个参数sampleDuration是我本身批改libmp4v2库添加的。


因为libmp4v2中 MP4AddALawAudioTrack接口为:MP4AddALawAudioTrack(mp4file, timescale),sampleDuration是经由过程如下公式策画获得的:


uint32_t fixedSampleDuration = (timeScale * 20)/1000; // 20mSec/Sample


而这策画出来的值,并不合适我们的实际景象,所以我添加了这第三个参数,可以本身指定sample duration。


原创粉丝点击