| /********** |
| 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-2015 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; |
| } |