1/**********
2This library is free software; you can redistribute it and/or modify it under
3the terms of the GNU Lesser General Public License as published by the
4Free Software Foundation; either version 3 of the License, or (at your
5option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7This library is distributed in the hope that it will be useful, but WITHOUT
8ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10more details.
11
12You should have received a copy of the GNU Lesser General Public License
13along with this library; if not, write to the Free Software Foundation, Inc.,
1451 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15**********/
16// "liveMedia"
17// Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved.
18// RTP sink for AMR audio (RFC 4867)
19// Implementation
20
21// NOTE: At present, this is just a limited implementation, supporting:
22// octet-alignment only; no interleaving; no frame CRC; no robust-sorting.
23
24#include "AMRAudioRTPSink.hh"
25#include "AMRAudioSource.hh"
26
27AMRAudioRTPSink*
28AMRAudioRTPSink::createNew(UsageEnvironment& env, Groupsock* RTPgs,
29 unsigned char rtpPayloadFormat,
30 Boolean sourceIsWideband,
31 unsigned numChannelsInSource) {
32 return new AMRAudioRTPSink(env, RTPgs, rtpPayloadFormat,
33 sourceIsWideband, numChannelsInSource);
34}
35
36AMRAudioRTPSink
37::AMRAudioRTPSink(UsageEnvironment& env, Groupsock* RTPgs,
38 unsigned char rtpPayloadFormat,
39 Boolean sourceIsWideband, unsigned numChannelsInSource)
40 : AudioRTPSink(env, RTPgs, rtpPayloadFormat,
41 sourceIsWideband ? 16000 : 8000,
42 sourceIsWideband ? "AMR-WB": "AMR",
43 numChannelsInSource),
44 fSourceIsWideband(sourceIsWideband), fFmtpSDPLine(NULL) {
45}
46
47AMRAudioRTPSink::~AMRAudioRTPSink() {
48 delete[] fFmtpSDPLine;
49}
50
51Boolean AMRAudioRTPSink::sourceIsCompatibleWithUs(MediaSource& source) {
52 // Our source must be an AMR audio source:
53 if (!source.isAMRAudioSource()) return False;
54
55 // Also, the source must be wideband iff we asked for this:
56 AMRAudioSource& amrSource = (AMRAudioSource&)source;
57 if ((amrSource.isWideband()^fSourceIsWideband) != 0) return False;
58
59 // Also, the source must have the same number of channels that we
60 // specified. (It could, in principle, have more, but we don't
61 // support that.)
62 if (amrSource.numChannels() != numChannels()) return False;
63
64 // Also, because in our current implementation we output only one
65 // frame in each RTP packet, this means that for multi-channel audio,
66 // each 'frame-block' will be split over multiple RTP packets, which
67 // may violate the spec. Warn about this:
68 if (amrSource.numChannels() > 1) {
69 envir() << "AMRAudioRTPSink: Warning: Input source has " << amrSource.numChannels()
70 << " audio channels. In the current implementation, the multi-frame frame-block will be split over multiple RTP packets\n";
71 }
72
73 return True;
74}
75
76void AMRAudioRTPSink::doSpecialFrameHandling(unsigned fragmentationOffset,
77 unsigned char* frameStart,
78 unsigned numBytesInFrame,
79 struct timeval framePresentationTime,
80 unsigned numRemainingBytes) {
81 // If this is the 1st frame in the 1st packet, set the RTP 'M' (marker)
82 // bit (because this is considered the start of a talk spurt):
83 if (isFirstPacket() && isFirstFrameInPacket()) {
84 setMarkerBit();
85 }
86
87 // If this is the first frame in the packet, set the 1-byte payload
88 // header (using CMR 15)
89 if (isFirstFrameInPacket()) {
90 u_int8_t payloadHeader = 0xF0;
91 setSpecialHeaderBytes(&payloadHeader, 1, 0);
92 }
93
94 // Set the TOC field for the current frame, based on the "FT" and "Q"
95 // values from our source:
96 AMRAudioSource* amrSource = (AMRAudioSource*)fSource;
97 if (amrSource == NULL) return; // sanity check
98
99 u_int8_t toc = amrSource->lastFrameHeader();
100 // Clear the "F" bit, because we're the last frame in this packet: #####
101 toc &=~ 0x80;
102 setSpecialHeaderBytes(&toc, 1, 1+numFramesUsedSoFar());
103
104 // Important: Also call our base class's doSpecialFrameHandling(),
105 // to set the packet's timestamp:
106 MultiFramedRTPSink::doSpecialFrameHandling(fragmentationOffset,
107 frameStart, numBytesInFrame,
108 framePresentationTime,
109 numRemainingBytes);
110}
111
112Boolean AMRAudioRTPSink
113::frameCanAppearAfterPacketStart(unsigned char const* /*frameStart*/,
114 unsigned /*numBytesInFrame*/) const {
115 // For now, pack only one AMR frame into each outgoing RTP packet: #####
116 return False;
117}
118
119unsigned AMRAudioRTPSink::specialHeaderSize() const {
120 // For now, because we're packing only one frame per packet,
121 // there's just a 1-byte payload header, plus a 1-byte TOC #####
122 return 2;
123}
124
125char const* AMRAudioRTPSink::auxSDPLine() {
126 if (fFmtpSDPLine == NULL) {
127 // Generate a "a=fmtp:" line with "octet-aligned=1"
128 // (That is the only non-default parameter.)
129 char buf[100];
130 sprintf(buf, "a=fmtp:%d octet-align=1\r\n", rtpPayloadType());
131 delete[] fFmtpSDPLine; fFmtpSDPLine = strDup(buf);
132 }
133 return fFmtpSDPLine;
134}
135