📄 rtp_transmitter.cpp
字号:
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is MPEG4IP.
*
* The Initial Developer of the Original Code is Cisco Systems Inc.
* Portions created by Cisco Systems Inc. are
* Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
*
* Contributor(s):
* Dave Mackie dmackie@cisco.com
* Bill May wmay@cisco.com
*/
#include "mp4live.h"
#include "rtp_transmitter.h"
// #define RTP_DEBUG 1
int CRtpTransmitter::ThreadMain(void)
{
while (SDL_SemWait(m_myMsgQueueSemaphore) == 0) {
CMsg* pMsg = m_myMsgQueue.get_message();
if (pMsg != NULL) {
switch (pMsg->get_value()) {
case MSG_NODE_STOP_THREAD:
DoStopTransmit();
delete pMsg;
return 0;
case MSG_NODE_START:
DoStartTransmit();
break;
case MSG_NODE_STOP:
DoStopTransmit();
break;
case MSG_SINK_FRAME:
size_t dontcare;
DoSendFrame((CMediaFrame*)pMsg->get_message(dontcare));
break;
}
delete pMsg;
}
}
return -1;
}
void CRtpTransmitter::DoStartTransmit()
{
if (m_sink) {
return;
}
m_startTimestamp = GetTimestamp();
if (m_pConfig->GetBoolValue(CONFIG_AUDIO_ENABLE)) {
m_audioTimeScale =
m_pConfig->GetIntegerValue(CONFIG_AUDIO_SAMPLE_RATE);
m_audioDestAddress =
m_pConfig->GetStringValue(CONFIG_RTP_DEST_ADDRESS);
if (m_pConfig->GetBoolValue(CONFIG_RTP_DISABLE_TS_OFFSET)) {
m_audioRtpTimestampOffset = 0;
} else {
m_audioRtpTimestampOffset = random();
}
m_audioSrcPort = GetRandomPortBlock();
if (m_pConfig->GetIntegerValue(CONFIG_RTP_AUDIO_DEST_PORT) == 0) {
m_pConfig->SetIntegerValue(CONFIG_RTP_AUDIO_DEST_PORT,
m_audioSrcPort + 2);
}
m_audioRtpSession = rtp_init(m_audioDestAddress,
m_audioSrcPort,
m_pConfig->GetIntegerValue(CONFIG_RTP_AUDIO_DEST_PORT),
m_pConfig->GetIntegerValue(CONFIG_RTP_MCAST_TTL),
m_rtcpBandwidth,
RtpCallback, (uint8_t *)this);
m_audioQueueCount = 0;
m_audioQueueSize = 0;
if (!strcasecmp(m_pConfig->GetStringValue(CONFIG_AUDIO_ENCODING),
AUDIO_ENCODING_MP3)) {
m_audioFrameType = CMediaFrame::Mp3AudioFrame;
m_audioPayloadBytesPerPacket = 4;
m_audioPayloadBytesPerFrame = 0;
} else { // AAC
m_audioFrameType = CMediaFrame::AacAudioFrame;
m_audioPayloadBytesPerPacket = 2;
m_audioPayloadBytesPerFrame = 2;
}
}
if (m_pConfig->GetBoolValue(CONFIG_VIDEO_ENABLE)) {
m_videoDestAddress =
m_pConfig->GetStringValue(CONFIG_RTP_DEST_ADDRESS);
if (m_pConfig->GetBoolValue(CONFIG_RTP_DISABLE_TS_OFFSET)) {
m_videoRtpTimestampOffset = 0;
} else {
m_videoRtpTimestampOffset = random();
}
m_videoSrcPort = GetRandomPortBlock();
if (m_pConfig->GetIntegerValue(CONFIG_RTP_VIDEO_DEST_PORT) == 0) {
m_pConfig->SetIntegerValue(CONFIG_RTP_VIDEO_DEST_PORT,
m_videoSrcPort + 2);
}
m_videoRtpSession = rtp_init(m_videoDestAddress,
m_videoSrcPort,
m_pConfig->GetIntegerValue(CONFIG_RTP_VIDEO_DEST_PORT),
m_pConfig->GetIntegerValue(CONFIG_RTP_MCAST_TTL),
m_rtcpBandwidth,
RtpCallback, (uint8_t *)this);
}
if (m_audioRtpSession || m_videoRtpSession) {
// automatic sdp file generation
GenerateSdpFile(m_pConfig);
m_sink = true;
}
}
void CRtpTransmitter::DoStopTransmit()
{
if (!m_sink) {
return;
}
if (m_audioRtpSession) {
// send any pending frames
SendQueuedAudioFrames();
rtp_done(m_audioRtpSession);
m_audioRtpSession = NULL;
}
if (m_videoRtpSession) {
rtp_done(m_videoRtpSession);
m_videoRtpSession = NULL;
}
m_sink = false;
}
void CRtpTransmitter::DoSendFrame(CMediaFrame* pFrame)
{
if (pFrame == NULL) {
return;
}
if (!m_sink) {
delete pFrame;
return;
}
if (pFrame->GetType() == m_audioFrameType && m_audioRtpSession) {
SendAudioFrame(pFrame);
} else if (pFrame->GetType() == CMediaFrame::Mpeg4VideoFrame
&& m_videoRtpSession) {
SendMpeg4VideoWith3016(pFrame);
} else {
delete pFrame;
}
}
void CRtpTransmitter::SendAudioFrame(CMediaFrame* pFrame)
{
u_int16_t newAudioQueueSize;
if (m_audioQueueCount > 0) {
newAudioQueueSize =
m_audioQueueSize
+ m_audioPayloadBytesPerFrame
+ pFrame->GetDataLength();
if (newAudioQueueSize <=
m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE)) {
m_audioQueue[m_audioQueueCount++] = pFrame;
m_audioQueueSize = newAudioQueueSize;
// if we fill the last slot in the queue
// send the packet
if (m_audioQueueCount == audioQueueMaxCount) {
SendQueuedAudioFrames();
}
return;
} else {
// send queued frames
SendQueuedAudioFrames();
// fall thru
}
}
// by here m_audioQueueCount == 0
newAudioQueueSize =
m_audioPayloadBytesPerPacket
+ m_audioPayloadBytesPerFrame
+ pFrame->GetDataLength();
if (newAudioQueueSize
<= m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE)) {
m_audioQueue[m_audioQueueCount++] = pFrame;
m_audioQueueSize = newAudioQueueSize;
} else {
// highly unusual case
// we need to fragment audio frame
// over multiple packets
SendAudioJumboFrame(pFrame);
}
}
void CRtpTransmitter::SendQueuedAudioFrames(void)
{
static u_int32_t zero32 = 0;
u_int8_t aacHeader[2 + (audioQueueMaxCount * 2)];
struct iovec iov[audioQueueMaxCount + 1];
if (m_audioQueueCount == 0) {
return;
}
u_int32_t rtpTimestamp =
AudioTimestampToRtp(m_audioQueue[0]->GetTimestamp());
u_int64_t ntpTimestamp =
TimestampToNtp(m_audioQueue[0]->GetTimestamp());
#if RTP_DEBUG
debug_message("A ts %llu rtp %u ntp %u.%u",
m_audioQueue[0]->GetTimestamp(),
rtpTimestamp,
(u_int32_t)(ntpTimestamp >> 32),
(u_int32_t)ntpTimestamp);
#endif
// check if RTCP SR needs to be sent
rtp_send_ctrl_2(m_audioRtpSession, rtpTimestamp, ntpTimestamp, NULL);
rtp_update(m_audioRtpSession);
// create the iovec list
u_int8_t i;
// audio payload headers
if (m_audioQueue[0]->GetType() == CMediaFrame::Mp3AudioFrame) {
iov[0].iov_base = &zero32;
iov[0].iov_len = 4;
} else { // AAC
u_int16_t numHdrBits = 16 * m_audioQueueCount;
aacHeader[0] = numHdrBits >> 8;
aacHeader[1] = numHdrBits & 0xFF;
for (i = 0; i < m_audioQueueCount; i++) {
aacHeader[2 + (i * 2)] =
m_audioQueue[i]->GetDataLength() >> 5;
aacHeader[2 + (i * 2) + 1] =
(m_audioQueue[i]->GetDataLength() & 0x1F) << 3;
}
iov[0].iov_base = aacHeader;
iov[0].iov_len = 2 + (m_audioQueueCount * 2);
}
// audio payload data
for (i = 0; i < m_audioQueueCount; i++) {
iov[i+1].iov_base = m_audioQueue[i]->GetData();
iov[i+1].iov_len = m_audioQueue[i]->GetDataLength();
}
// send packet
rtp_send_data_iov(
m_audioRtpSession,
rtpTimestamp,
m_audioPayloadNumber, 1, 0, NULL,
iov, m_audioQueueCount + 1,
NULL, 0, 0);
// delete all the pending media frames
for (int i = 0; i < m_audioQueueCount; i++) {
delete m_audioQueue[i];
m_audioQueue[i] = NULL;
}
m_audioQueueCount = 0;
m_audioQueueSize = 0;
}
void CRtpTransmitter::SendAudioJumboFrame(CMediaFrame* pFrame)
{
u_int32_t rtpTimestamp =
AudioTimestampToRtp(pFrame->GetTimestamp());
u_int64_t ntpTimestamp =
TimestampToNtp(pFrame->GetTimestamp());
#if RTP_DEBUG
debug_message("A ts %llu rtp %u ntp %u.%u",
pFrame->GetTimestamp(),
rtpTimestamp,
(u_int32_t)(ntpTimestamp >> 32),
(u_int32_t)ntpTimestamp);
#endif
// check if RTCP SR needs to be sent
rtp_send_ctrl_2(m_audioRtpSession, rtpTimestamp, ntpTimestamp, NULL);
rtp_update(m_audioRtpSession);
u_int32_t dataOffset = 0;
u_int32_t spaceAvailable =
m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE)
- (m_audioPayloadBytesPerPacket + m_audioPayloadBytesPerFrame);
bool firstPacket = true;
u_int8_t payloadHeader[4];
struct iovec iov[2];
iov[0].iov_base = &payloadHeader;
iov[0].iov_len = 4;
if (pFrame->GetType() == CMediaFrame::Mp3AudioFrame) {
payloadHeader[0] = 0;
payloadHeader[1] = 0;
do {
// update MP3 payload header
payloadHeader[2] = (dataOffset >> 8);
payloadHeader[3] = (dataOffset & 0xFF);
iov[1].iov_base = (u_int8_t*)pFrame->GetData() + dataOffset;
// figure out how much data we're sending
u_int32_t dataPending = pFrame->GetDataLength() - dataOffset;
iov[1].iov_len = MIN(dataPending, spaceAvailable);
// send packet
rtp_send_data_iov(
m_audioRtpSession,
rtpTimestamp,
m_audioPayloadNumber, firstPacket, 0, NULL,
iov, 2,
NULL, 0, 0);
dataOffset += iov[1].iov_len;
firstPacket = false;
} while (dataOffset < pFrame->GetDataLength());
} else { // AAC
bool lastPacket = false;
payloadHeader[0] = 0;
payloadHeader[1] = 16;
payloadHeader[2] = pFrame->GetDataLength() >> 5;
payloadHeader[3] = (pFrame->GetDataLength() & 0x1F) << 3;
do {
iov[1].iov_base = (u_int8_t*)pFrame->GetData() + dataOffset;
// figure out how much data we're sending
u_int32_t dataPending = pFrame->GetDataLength() - dataOffset;
if (dataPending <= spaceAvailable) {
iov[1].iov_len = dataPending;
lastPacket = true;
} else {
iov[1].iov_len = spaceAvailable;
}
// send packet
if (firstPacket) {
rtp_send_data_iov(
m_audioRtpSession,
rtpTimestamp,
m_audioPayloadNumber, false, 0, NULL,
&iov[0], 2,
NULL, 0, 0);
firstPacket = false;
spaceAvailable +=
m_audioPayloadBytesPerPacket
+ m_audioPayloadBytesPerFrame;
} else {
rtp_send_data_iov(
m_audioRtpSession,
rtpTimestamp,
m_audioPayloadNumber, lastPacket, 0, NULL,
&iov[1], 1,
NULL, 0, 0);
}
dataOffset += iov[1].iov_len;
} while (dataOffset < pFrame->GetDataLength());
}
delete pFrame;
}
void CRtpTransmitter::SendMpeg4VideoWith3016(CMediaFrame* pFrame)
{
u_int32_t rtpTimestamp =
VideoTimestampToRtp(pFrame->GetTimestamp());
u_int64_t ntpTimestamp =
TimestampToNtp(pFrame->GetTimestamp());
#if RTP_DEBUG
debug_message("V ts %llu rtp %u ntp %u.%u",
pFrame->GetTimestamp(),
rtpTimestamp,
(u_int32_t)(ntpTimestamp >> 32),
(u_int32_t)ntpTimestamp);
#endif
// check if RTCP SR needs to be sent
rtp_send_ctrl_2(m_videoRtpSession, rtpTimestamp, ntpTimestamp, NULL);
rtp_update(m_videoRtpSession);
u_int8_t* pData = (u_int8_t*)pFrame->GetData();
u_int32_t bytesToSend = pFrame->GetDataLength();
struct iovec iov;
while (bytesToSend) {
u_int32_t payloadLength;
bool lastPacket;
if (bytesToSend
<= m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE)) {
payloadLength = bytesToSend;
lastPacket = true;
} else {
payloadLength =
m_pConfig->GetIntegerValue(CONFIG_RTP_PAYLOAD_SIZE);
lastPacket = false;
}
iov.iov_base = pData;
iov.iov_len = payloadLength;
rtp_send_data_iov(
m_videoRtpSession,
rtpTimestamp,
m_videoPayloadNumber, lastPacket, 0, NULL,
&iov, 1,
NULL, 0, 0);
pData += payloadLength;
bytesToSend -= payloadLength;
}
delete pFrame;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -