blob: 7d0cc33b6096c06a1795e98b34d95d93e607a41d [file] [log] [blame]
/**********
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 3 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-2020 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;
}