135 lines
5.1 KiB
C++
Executable File
135 lines
5.1 KiB
C++
Executable File
/**********
|
|
This library is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU Lesser General Public License as published by the
|
|
Free Software Foundation; either version 2.1 of the License, or (at your
|
|
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
|
|
|
|
This library is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
|
more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this library; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
**********/
|
|
// "liveMedia"
|
|
// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
|
|
// RTP sink for AMR audio (RFC 4867)
|
|
// Implementation
|
|
|
|
// NOTE: At present, this is just a limited implementation, supporting:
|
|
// octet-alignment only; no interleaving; no frame CRC; no robust-sorting.
|
|
|
|
#include "AMRAudioRTPSink.hh"
|
|
#include "AMRAudioSource.hh"
|
|
|
|
AMRAudioRTPSink*
|
|
AMRAudioRTPSink::createNew(UsageEnvironment& env, Groupsock* RTPgs,
|
|
unsigned char rtpPayloadFormat,
|
|
Boolean sourceIsWideband,
|
|
unsigned numChannelsInSource) {
|
|
return new AMRAudioRTPSink(env, RTPgs, rtpPayloadFormat,
|
|
sourceIsWideband, numChannelsInSource);
|
|
}
|
|
|
|
AMRAudioRTPSink
|
|
::AMRAudioRTPSink(UsageEnvironment& env, Groupsock* RTPgs,
|
|
unsigned char rtpPayloadFormat,
|
|
Boolean sourceIsWideband, unsigned numChannelsInSource)
|
|
: AudioRTPSink(env, RTPgs, rtpPayloadFormat,
|
|
sourceIsWideband ? 16000 : 8000,
|
|
sourceIsWideband ? "AMR-WB": "AMR",
|
|
numChannelsInSource),
|
|
fSourceIsWideband(sourceIsWideband), fFmtpSDPLine(NULL) {
|
|
}
|
|
|
|
AMRAudioRTPSink::~AMRAudioRTPSink() {
|
|
delete[] fFmtpSDPLine;
|
|
}
|
|
|
|
Boolean AMRAudioRTPSink::sourceIsCompatibleWithUs(MediaSource& source) {
|
|
// Our source must be an AMR audio source:
|
|
if (!source.isAMRAudioSource()) return False;
|
|
|
|
// Also, the source must be wideband iff we asked for this:
|
|
AMRAudioSource& amrSource = (AMRAudioSource&)source;
|
|
if ((amrSource.isWideband()^fSourceIsWideband) != 0) return False;
|
|
|
|
// Also, the source must have the same number of channels that we
|
|
// specified. (It could, in principle, have more, but we don't
|
|
// support that.)
|
|
if (amrSource.numChannels() != numChannels()) return False;
|
|
|
|
// Also, because in our current implementation we output only one
|
|
// frame in each RTP packet, this means that for multi-channel audio,
|
|
// each 'frame-block' will be split over multiple RTP packets, which
|
|
// may violate the spec. Warn about this:
|
|
if (amrSource.numChannels() > 1) {
|
|
envir() << "AMRAudioRTPSink: Warning: Input source has " << amrSource.numChannels()
|
|
<< " audio channels. In the current implementation, the multi-frame frame-block will be split over multiple RTP packets\n";
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
void AMRAudioRTPSink::doSpecialFrameHandling(unsigned fragmentationOffset,
|
|
unsigned char* frameStart,
|
|
unsigned numBytesInFrame,
|
|
struct timeval framePresentationTime,
|
|
unsigned numRemainingBytes) {
|
|
// If this is the 1st frame in the 1st packet, set the RTP 'M' (marker)
|
|
// bit (because this is considered the start of a talk spurt):
|
|
if (isFirstPacket() && isFirstFrameInPacket()) {
|
|
setMarkerBit();
|
|
}
|
|
|
|
// If this is the first frame in the packet, set the 1-byte payload
|
|
// header (using CMR 15)
|
|
if (isFirstFrameInPacket()) {
|
|
u_int8_t payloadHeader = 0xF0;
|
|
setSpecialHeaderBytes(&payloadHeader, 1, 0);
|
|
}
|
|
|
|
// Set the TOC field for the current frame, based on the "FT" and "Q"
|
|
// values from our source:
|
|
AMRAudioSource* amrSource = (AMRAudioSource*)fSource;
|
|
if (amrSource == NULL) return; // sanity check
|
|
|
|
u_int8_t toc = amrSource->lastFrameHeader();
|
|
// Clear the "F" bit, because we're the last frame in this packet: #####
|
|
toc &=~ 0x80;
|
|
setSpecialHeaderBytes(&toc, 1, 1+numFramesUsedSoFar());
|
|
|
|
// Important: Also call our base class's doSpecialFrameHandling(),
|
|
// to set the packet's timestamp:
|
|
MultiFramedRTPSink::doSpecialFrameHandling(fragmentationOffset,
|
|
frameStart, numBytesInFrame,
|
|
framePresentationTime,
|
|
numRemainingBytes);
|
|
}
|
|
|
|
Boolean AMRAudioRTPSink
|
|
::frameCanAppearAfterPacketStart(unsigned char const* /*frameStart*/,
|
|
unsigned /*numBytesInFrame*/) const {
|
|
// For now, pack only one AMR frame into each outgoing RTP packet: #####
|
|
return False;
|
|
}
|
|
|
|
unsigned AMRAudioRTPSink::specialHeaderSize() const {
|
|
// For now, because we're packing only one frame per packet,
|
|
// there's just a 1-byte payload header, plus a 1-byte TOC #####
|
|
return 2;
|
|
}
|
|
|
|
char const* AMRAudioRTPSink::auxSDPLine() {
|
|
if (fFmtpSDPLine == NULL) {
|
|
// Generate a "a=fmtp:" line with "octet-aligned=1"
|
|
// (That is the only non-default parameter.)
|
|
char buf[100];
|
|
sprintf(buf, "a=fmtp:%d octet-align=1\r\n", rtpPayloadType());
|
|
delete[] fFmtpSDPLine; fFmtpSDPLine = strDup(buf);
|
|
}
|
|
return fFmtpSDPLine;
|
|
}
|