From 4b77dc28097288cb062fce6bf5de0fb3394877a9 Mon Sep 17 00:00:00 2001 From: John Grossman Date: Sat, 18 Feb 2012 17:46:40 -0800 Subject: [PATCH] LibAAH_RTP: Add support for AAC in MP4. Cherry picked from Ie8298eb9d253fc6ede448da87660a60d23170987 and conflicts fixed by hand. Change-Id: I6f0a59357ba1a3d57caf67d2eb0ade7486723e7b Signed-off-by: John Grossman --- media/libaah_rtp/aah_decoder_pump.cpp | 8 +- media/libaah_rtp/aah_rx_player.h | 11 +- media/libaah_rtp/aah_rx_player_core.cpp | 10 +- media/libaah_rtp/aah_rx_player_ring_buffer.cpp | 4 +- media/libaah_rtp/aah_rx_player_substream.cpp | 381 ++++++++++++++++++------- media/libaah_rtp/aah_tx_packet.cpp | 33 ++- media/libaah_rtp/aah_tx_packet.h | 32 ++- media/libaah_rtp/aah_tx_player.cpp | 91 +++++- media/libaah_rtp/aah_tx_player.h | 5 + media/libaah_rtp/aah_tx_sender.cpp | 3 +- 10 files changed, 446 insertions(+), 132 deletions(-) diff --git a/media/libaah_rtp/aah_decoder_pump.cpp b/media/libaah_rtp/aah_decoder_pump.cpp index 72fe43baf5..28b8c7bb2a 100644 --- a/media/libaah_rtp/aah_decoder_pump.cpp +++ b/media/libaah_rtp/aah_decoder_pump.cpp @@ -159,8 +159,8 @@ void AAH_DecoderPump::queueToRenderer(MediaBuffer* decoded_sample) { res = renderer_->queueTimedBuffer(pcm_payload, ts); if (res != OK) { - ALOGE("Failed to queue %d byte audio track buffer with media" - " PTS %lld. (res = %d)", decoded_amt, ts, res); + ALOGE("Failed to queue %d byte audio track buffer with" + " media PTS %lld. (res = %d)", decoded_amt, ts, res); } else { last_queued_pts_valid_ = true; last_queued_pts_ = ts; @@ -291,8 +291,8 @@ void* AAH_DecoderPump::workThread() { // thread_status_. thread_status_ = decoder_->start(format_.get()); if (OK != thread_status_) { - ALOGE("AAH_DecoderPump's work thread failed to start decoder (res = %d)", - thread_status_); + ALOGE("AAH_DecoderPump's work thread failed to start decoder" + " (res = %d)", thread_status_); return NULL; } diff --git a/media/libaah_rtp/aah_rx_player.h b/media/libaah_rtp/aah_rx_player.h index 7a1b6e3a7e..ba5617eb7d 100644 --- a/media/libaah_rtp/aah_rx_player.h +++ b/media/libaah_rtp/aah_rx_player.h @@ -217,14 +217,15 @@ class AAH_RXPlayer : public MediaPlayerInterface { status_t getStatus() const { return status_; } protected: - virtual ~Substream() { - shutdown(); - } + virtual ~Substream(); private: void cleanupDecoder(); bool shouldAbort(const char* log_tag); void processCompletedBuffer(); + bool setupSubstreamMeta(); + bool setupMP3SubstreamMeta(); + bool setupAACSubstreamMeta(); bool setupSubstreamType(uint8_t substream_type, uint8_t codec_type); @@ -235,12 +236,16 @@ class AAH_RXPlayer : public MediaPlayerInterface { bool substream_details_known_; uint8_t substream_type_; uint8_t codec_type_; + const char* codec_mime_type_; sp substream_meta_; MediaBuffer* buffer_in_progress_; uint32_t expected_buffer_size_; uint32_t buffer_filled_; + Vector aux_data_in_progress_; + uint32_t aux_data_expected_size_; + sp decoder_; static int64_t kAboutToUnderflowThreshold; diff --git a/media/libaah_rtp/aah_rx_player_core.cpp b/media/libaah_rtp/aah_rx_player_core.cpp index d2b33866cd..d6b31fd4a4 100644 --- a/media/libaah_rtp/aah_rx_player_core.cpp +++ b/media/libaah_rtp/aah_rx_player_core.cpp @@ -431,8 +431,8 @@ bool AAH_RXPlayer::processRX(PacketBuffer* pb) { // Looks like a NAK packet; make sure its long enough. if (amt < static_cast(sizeof(RetransRequest))) { - ALOGV("Dropping packet, too short to contain NAK payload (%u bytes)", - static_cast(amt)); + ALOGV("Dropping packet, too short to contain NAK payload" + " (%u bytes)", static_cast(amt)); goto drop_packet; } @@ -441,7 +441,8 @@ bool AAH_RXPlayer::processRX(PacketBuffer* pb) { gap.start_seq_ = ntohs(rtr->start_seq_); gap.end_seq_ = ntohs(rtr->end_seq_); - ALOGV("Process NAK for gap at [%hu, %hu]", gap.start_seq_, gap.end_seq_); + ALOGV("Process NAK for gap at [%hu, %hu]", + gap.start_seq_, gap.end_seq_); ring_buffer_.processNAK(&gap); return true; @@ -770,7 +771,8 @@ bool AAH_RXPlayer::processGaps() { ALOGE("Error when sending retransmit request (%d)", errno); } else { ALOGV("%s request for range [%hu, %hu] sent", - (kGS_FastStartGap == gap_status) ? "Fast Start" : "Retransmit", + (kGS_FastStartGap == gap_status) ? "Fast Start" + : "Retransmit", gap.start_seq_, gap.end_seq_); } diff --git a/media/libaah_rtp/aah_rx_player_ring_buffer.cpp b/media/libaah_rtp/aah_rx_player_ring_buffer.cpp index 0d8b31f544..779405e02d 100644 --- a/media/libaah_rtp/aah_rx_player_ring_buffer.cpp +++ b/media/libaah_rtp/aah_rx_player_ring_buffer.cpp @@ -116,8 +116,8 @@ bool AAH_RXPlayer::RXRingBuffer::pushBuffer(PacketBuffer* buf, // Check for overflow first. if ((!(norm_seq & 0x8000)) && (norm_seq >= (capacity_ - 1))) { - ALOGW("Ring buffer overflow; cap = %u, [rd, wr] = [%hu, %hu], seq = %hu", - capacity_, rd_seq_, norm_wr_seq + rd_seq_, seq); + ALOGW("Ring buffer overflow; cap = %u, [rd, wr] = [%hu, %hu]," + " seq = %hu", capacity_, rd_seq_, norm_wr_seq + rd_seq_, seq); PacketBuffer::destroy(buf); return false; } diff --git a/media/libaah_rtp/aah_rx_player_substream.cpp b/media/libaah_rtp/aah_rx_player_substream.cpp index 1e4c784629..18b0e2b290 100644 --- a/media/libaah_rtp/aah_rx_player_substream.cpp +++ b/media/libaah_rtp/aah_rx_player_substream.cpp @@ -27,6 +27,11 @@ #include #include "aah_rx_player.h" +#include "aah_tx_packet.h" + +inline uint32_t min(uint32_t a, uint32_t b) { + return (a < b ? a : b); +} namespace android { @@ -38,6 +43,7 @@ AAH_RXPlayer::Substream::Substream(uint32_t ssrc, OMXClient& omx) { substream_details_known_ = false; buffer_in_progress_ = NULL; status_ = OK; + codec_mime_type_ = ""; decoder_ = new AAH_DecoderPump(omx); if (decoder_ == NULL) { @@ -52,6 +58,9 @@ AAH_RXPlayer::Substream::Substream(uint32_t ssrc, OMXClient& omx) { cleanupBufferInProgress(); } +AAH_RXPlayer::Substream::~Substream() { + shutdown(); +} void AAH_RXPlayer::Substream::shutdown() { substream_meta_ = NULL; @@ -69,6 +78,9 @@ void AAH_RXPlayer::Substream::cleanupBufferInProgress() { expected_buffer_size_ = 0; buffer_filled_ = 0; waiting_for_rap_ = true; + + aux_data_in_progress_.clear(); + aux_data_expected_size_ = 0; } void AAH_RXPlayer::Substream::cleanupDecoder() { @@ -129,16 +141,16 @@ void AAH_RXPlayer::Substream::processPayloadStart(uint8_t* buf, // one that does not conflict with any previously received substream type. uint8_t header_type = (buf[1] >> 4) & 0xF; switch (header_type) { - case 0x01: + case TRTPPacket::kHeaderTypeAudio: // Audio, yay! Just break. We understand audio payloads. break; - case 0x02: + case TRTPPacket::kHeaderTypeVideo: ALOGV("RXed packet with unhandled TRTP header type (Video)."); return; - case 0x03: + case TRTPPacket::kHeaderTypeSubpicture: ALOGV("RXed packet with unhandled TRTP header type (Subpicture)."); return; - case 0x04: + case TRTPPacket::kHeaderTypeControl: ALOGV("RXed packet with unhandled TRTP header type (Control)."); return; default: @@ -148,15 +160,15 @@ void AAH_RXPlayer::Substream::processPayloadStart(uint8_t* buf, } if (substream_details_known_ && (header_type != substream_type_)) { - ALOGV("RXed TRTP Payload for SSRC=0x%08x where header type (%u) does not" - " match previously received header type (%u)", + ALOGV("RXed TRTP Payload for SSRC=0x%08x where header type (%u) does" + " not match previously received header type (%u)", ssrc_, header_type, substream_type_); return; } // Check the flags to see if there is another 32 bits of timestamp present. uint32_t trtp_header_len = 6; - bool ts_valid = buf[1] & 0x1; + bool ts_valid = buf[1] & TRTPPacket::kFlag_TSValid; if (ts_valid) { min_length += 4; trtp_header_len += 4; @@ -168,11 +180,7 @@ void AAH_RXPlayer::Substream::processPayloadStart(uint8_t* buf, } // Extract the TRTP length field and sanity check it. - uint32_t trtp_len; - trtp_len = (static_cast(buf[2]) << 24) | - (static_cast(buf[3]) << 16) | - (static_cast(buf[4]) << 8) | - static_cast(buf[5]); + uint32_t trtp_len = U32_AT(buf + 2); if (trtp_len < min_length) { ALOGV("TRTP length (%u) is too short to be valid. Must be at least %u" " bytes.", trtp_len, min_length); @@ -183,17 +191,14 @@ void AAH_RXPlayer::Substream::processPayloadStart(uint8_t* buf, int64_t ts = 0; uint32_t parse_offset = 6; if (ts_valid) { - ts = (static_cast(buf[parse_offset ]) << 56) | - (static_cast(buf[parse_offset + 1]) << 48) | - (static_cast(buf[parse_offset + 2]) << 40) | - (static_cast(buf[parse_offset + 3]) << 32); - ts |= ts_lower; + uint32_t ts_upper = U32_AT(buf + parse_offset); parse_offset += 4; + ts = (static_cast(ts_upper) << 32) | ts_lower; } // Check the flags to see if there is another 24 bytes of timestamp // transformation present. - if (buf[1] & 0x2) { + if (buf[1] & TRTPPacket::kFlag_TSTransformPresent) { min_length += 24; parse_offset += 24; trtp_header_len += 24; @@ -219,8 +224,8 @@ void AAH_RXPlayer::Substream::processPayloadStart(uint8_t* buf, if (amt < min_length) { ALOGV("TRTP porttion of RTP payload (%u bytes) too small to contain" - " entire TRTP header. TRTP does not currently support fragmenting" - " TRTP headers across RTP payloads", amt); + " entire TRTP header. TRTP does not currently support" + " fragmenting TRTP headers across RTP payloads", amt); return; } @@ -238,16 +243,42 @@ void AAH_RXPlayer::Substream::processPayloadStart(uint8_t* buf, decoder_->setRenderVolume(volume); } - // TODO : move all of the constant flag and offset definitions for TRTP up - // into some sort of common header file. - if (waiting_for_rap_ && !(flags & 0x08)) { + if (waiting_for_rap_ && !(flags & TRTPAudioPacket::kFlag_RandomAccessPoint)) { ALOGV("Dropping non-RAP TRTP Audio Payload while waiting for RAP."); return; } - if (flags & 0x10) { - ALOGV("Dropping TRTP Audio Payload with aux codec data present (only" - " handle MP3 right now, and it has no aux data)"); + // Check for the presence of codec aux data. + if (flags & TRTPAudioPacket::kFlag_AuxLengthPresent) { + min_length += 4; + trtp_header_len += 4; + + if (trtp_len < min_length) { + ALOGV("TRTP length (%u) is too short to be a valid audio payload. " + "Must be at least %u bytes.", trtp_len, min_length); + return; + } + + if (amt < min_length) { + ALOGV("TRTP porttion of RTP payload (%u bytes) too small to contain" + " entire TRTP header. TRTP does not currently support" + " fragmenting TRTP headers across RTP payloads", amt); + return; + } + + aux_data_expected_size_ = U32_AT(buf + parse_offset); + aux_data_in_progress_.clear(); + if (aux_data_in_progress_.capacity() < aux_data_expected_size_) { + aux_data_in_progress_.setCapacity(aux_data_expected_size_); + } + } else { + aux_data_expected_size_ = 0; + } + + if ((aux_data_expected_size_ + trtp_header_len) > trtp_len) { + ALOGV("Expected codec aux data length (%u) and TRTP header overhead" + " (%u) too large for total TRTP payload length (%u).", + aux_data_expected_size_, trtp_header_len, trtp_len); return; } @@ -255,7 +286,9 @@ void AAH_RXPlayer::Substream::processPayloadStart(uint8_t* buf, // the buffer in progress and pack as much payload as we can into it. If // the payload is finished once we are done, go ahead and send the payload // to the decoder. - expected_buffer_size_ = trtp_len - trtp_header_len; + expected_buffer_size_ = trtp_len + - trtp_header_len + - aux_data_expected_size_; if (!expected_buffer_size_) { ALOGV("Dropping TRTP Audio Payload with 0 Access Unit length"); return; @@ -263,9 +296,10 @@ void AAH_RXPlayer::Substream::processPayloadStart(uint8_t* buf, CHECK(amt >= trtp_header_len); uint32_t todo = amt - trtp_header_len; - if (expected_buffer_size_ < todo) { + if ((expected_buffer_size_ + aux_data_expected_size_) < todo) { ALOGV("Extra data (%u > %u) present in initial TRTP Audio Payload;" - " dropping payload.", todo, expected_buffer_size_); + " dropping payload.", todo, + expected_buffer_size_ + aux_data_expected_size_); return; } @@ -287,18 +321,32 @@ void AAH_RXPlayer::Substream::processPayloadStart(uint8_t* buf, return; } - // TODO : set this based on the codec type indicated in the TRTP stream. - // Right now, we only support MP3, so the choice is obvious. - meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); + meta->setCString(kKeyMIMEType, codec_mime_type_); if (ts_valid) { meta->setInt64(kKeyTime, ts); } - if (amt > 0) { + // Skip over the header we have already extracted. + amt -= trtp_header_len; + buf += trtp_header_len; + + // Extract as much of the expected aux data as we can. + todo = min(aux_data_expected_size_, amt); + if (todo) { + aux_data_in_progress_.appendArray(buf, todo); + buf += todo; + amt -= todo; + } + + // Extract as much of the expected payload as we can. + todo = min(expected_buffer_size_, amt); + if (todo > 0) { uint8_t* tgt = reinterpret_cast(buffer_in_progress_->data()); - memcpy(tgt + buffer_filled_, buf + trtp_header_len, todo); - buffer_filled_ += amt; + memcpy(tgt, buf, todo); + buffer_filled_ = amt; + buf += todo; + amt -= todo; } if (buffer_filled_ >= expected_buffer_size_) { @@ -318,6 +366,18 @@ void AAH_RXPlayer::Substream::processPayloadCont(uint8_t* buf, return; } + CHECK(aux_data_in_progress_.size() <= aux_data_expected_size_); + uint32_t aux_left = aux_data_expected_size_ - aux_data_in_progress_.size(); + if (aux_left) { + uint32_t todo = min(aux_left, amt); + aux_data_in_progress_.appendArray(buf, todo); + amt -= todo; + buf += todo; + + if (!amt) + return; + } + CHECK(buffer_filled_ < expected_buffer_size_); uint32_t buffer_left = expected_buffer_size_ - buffer_filled_; if (amt > buffer_left) { @@ -340,10 +400,6 @@ void AAH_RXPlayer::Substream::processPayloadCont(uint8_t* buf, } void AAH_RXPlayer::Substream::processCompletedBuffer() { - const uint8_t* buffer_data = NULL; - int sample_rate; - int channel_count; - size_t frame_size; status_t res; CHECK(NULL != buffer_in_progress_); @@ -353,10 +409,91 @@ void AAH_RXPlayer::Substream::processCompletedBuffer() { goto bailout; } + // Make sure our metadata used to initialize the decoder has been properly + // set up. + if (!setupSubstreamMeta()) + goto bailout; + + // If our decoder has not be set up, do so now. + res = decoder_->init(substream_meta_); + if (OK != res) { + ALOGE("Failed to init decoder (res = %d)", res); + cleanupDecoder(); + substream_meta_ = NULL; + goto bailout; + } + + // Queue the payload for decode. + res = decoder_->queueForDecode(buffer_in_progress_); + + if (res != OK) { + ALOGD("Failed to queue payload for decode, resetting decoder pump!" + " (res = %d)", res); + status_ = res; + cleanupDecoder(); + cleanupBufferInProgress(); + } + + // NULL out buffer_in_progress before calling the cleanup helper. + // + // MediaBuffers use something of a hybrid ref-counting pattern which prevent + // the AAH_DecoderPump's input queue from adding their own reference to the + // MediaBuffer. MediaBuffers start life with a reference count of 0, as + // well as an observer which starts as NULL. Before being given an + // observer, the ref count cannot be allowed to become non-zero as it will + // cause calls to release() to assert. Basically, before a MediaBuffer has + // an observer, they behave like non-ref counted obects where release() + // serves the roll of delete. After a MediaBuffer has an observer, they + // become more like ref counted objects where add ref and release can be + // used, and when the ref count hits zero, the MediaBuffer is handed off to + // the observer. + // + // Given all of this, when we give the buffer to the decoder pump to wait in + // the to-be-processed queue, the decoder cannot add a ref to the buffer as + // it would in a traditional ref counting system. Instead it needs to + // "steal" the non-existent ref. In the case of queue failure, we need to + // make certain to release this non-existent reference so that the buffer is + // cleaned up during the cleanupBufferInProgress helper. In the case of a + // successful queue operation, we need to make certain that the + // cleanupBufferInProgress helper does not release the buffer since it needs + // to remain alive in the queue. We acomplish this by NULLing out the + // buffer pointer before calling the cleanup helper. + buffer_in_progress_ = NULL; + +bailout: + cleanupBufferInProgress(); +} + +bool AAH_RXPlayer::Substream::setupSubstreamMeta() { + switch (codec_type_) { + case TRTPAudioPacket::kCodecMPEG1Audio: + codec_mime_type_ = MEDIA_MIMETYPE_AUDIO_MPEG; + return setupMP3SubstreamMeta(); + + case TRTPAudioPacket::kCodecAACAudio: + codec_mime_type_ = MEDIA_MIMETYPE_AUDIO_AAC; + return setupAACSubstreamMeta(); + + default: + ALOGV("Failed to setup substream metadata for unsupported codec" + " type (%u)", codec_type_); + break; + } + + return false; +} + +bool AAH_RXPlayer::Substream::setupMP3SubstreamMeta() { + const uint8_t* buffer_data = NULL; + int sample_rate; + int channel_count; + size_t frame_size; + status_t res; + buffer_data = reinterpret_cast(buffer_in_progress_->data()); if (buffer_in_progress_->size() < 4) { ALOGV("MP3 payload too short to contain header, dropping payload."); - goto bailout; + return false; } // Extract the channel count and the sample rate from the MP3 header. The @@ -369,7 +506,7 @@ void AAH_RXPlayer::Substream::processCompletedBuffer() { NULL, NULL)) { ALOGV("Failed to parse MP3 header in payload, droping payload."); - goto bailout; + return false; } @@ -381,8 +518,8 @@ void AAH_RXPlayer::Substream::processCompletedBuffer() { substream_meta_ = new MetaData(); if (substream_meta_ == NULL) { - ALOGE("Failed to allocate MetaData structure for substream"); - goto bailout; + ALOGE("Failed to allocate MetaData structure for MP3 substream"); + return false; } substream_meta_->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); @@ -396,7 +533,7 @@ void AAH_RXPlayer::Substream::processCompletedBuffer() { if ((prev_channel_count != channel_count) || (prev_sample_rate != sample_rate)) { - ALOGW("Format change detected, forcing decoder reset."); + ALOGW("MP3 format change detected, forcing decoder reset."); cleanupDecoder(); substream_meta_->setInt32(kKeyChannelCount, channel_count); @@ -404,56 +541,90 @@ void AAH_RXPlayer::Substream::processCompletedBuffer() { } } - // If our decoder has not be set up, do so now. - res = decoder_->init(substream_meta_); - if (OK != res) { - ALOGE("Failed to init decoder (res = %d)", res); + return true; +} + +bool AAH_RXPlayer::Substream::setupAACSubstreamMeta() { + int32_t sample_rate, channel_cnt; + static const size_t overhead = sizeof(sample_rate) + + sizeof(channel_cnt); + + if (aux_data_in_progress_.size() < overhead) { + ALOGE("Not enough aux data (%u) to initialize AAC substream decoder", + aux_data_in_progress_.size()); + return false; + } + + const uint8_t* aux_data = aux_data_in_progress_.array(); + size_t aux_data_size = aux_data_in_progress_.size(); + sample_rate = U32_AT(aux_data); + channel_cnt = U32_AT(aux_data + sizeof(sample_rate)); + + const uint8_t* esds_data = NULL; + size_t esds_data_size = 0; + if (aux_data_size > overhead) { + esds_data = aux_data + overhead; + esds_data_size = aux_data_size - overhead; + } + + // Do we already have metadata? If so, has it changed at all? If not, then + // there should be nothing else to do. Otherwise, release our old stream + // metadata and make new metadata. + if (substream_meta_ != NULL) { + uint32_t type; + const void* data; + size_t size; + int32_t prev_sample_rate; + int32_t prev_channel_count; + bool res; + + res = substream_meta_->findInt32(kKeySampleRate, &prev_sample_rate); + CHECK(res); + res = substream_meta_->findInt32(kKeyChannelCount, &prev_channel_count); + CHECK(res); + + // If nothing has changed about the codec aux data (esds, sample rate, + // channel count), then we can just do nothing and get out. Otherwise, + // we will need to reset the decoder and make a new metadata object to + // deal with the format change. + bool hasData = (esds_data != NULL); + bool hadData = substream_meta_->findData(kKeyESDS, &type, &data, &size); + bool esds_change = (hadData != hasData); + + if (!esds_change && hasData) + esds_change = ((size != esds_data_size) || + memcmp(data, esds_data, size)); + + if (!esds_change && + (prev_sample_rate == sample_rate) && + (prev_channel_count == channel_cnt)) { + return true; // no change, just get out. + } + + ALOGW("AAC format change detected, forcing decoder reset."); cleanupDecoder(); substream_meta_ = NULL; - goto bailout; } - // Queue the payload for decode. - res = decoder_->queueForDecode(buffer_in_progress_); + CHECK(substream_meta_ == NULL); - if (res != OK) { - ALOGD("Failed to queue payload for decode, resetting decoder pump!" - " (res = %d)", res); - status_ = res; - cleanupDecoder(); - cleanupBufferInProgress(); + substream_meta_ = new MetaData(); + if (substream_meta_ == NULL) { + ALOGE("Failed to allocate MetaData structure for AAC substream"); + return false; } - // NULL out buffer_in_progress before calling the cleanup helper. - // - // MediaBuffers use something of a hybrid ref-counting pattern which prevent - // the AAH_DecoderPump's input queue from adding their own reference to the - // MediaBuffer. MediaBuffers start life with a reference count of 0, as - // well as an observer which starts as NULL. Before being given an - // observer, the ref count cannot be allowed to become non-zero as it will - // cause calls to release() to assert. Basically, before a MediaBuffer has - // an observer, they behave like non-ref counted obects where release() - // serves the roll of delete. After a MediaBuffer has an observer, they - // become more like ref counted objects where add ref and release can be - // used, and when the ref count hits zero, the MediaBuffer is handed off to - // the observer. - // - // Given all of this, when we give the buffer to the decoder pump to wait in - // the to-be-processed queue, the decoder cannot add a ref to the buffer as - // it would in a traditional ref counting system. Instead it needs to - // "steal" the non-existent ref. In the case of queue failure, we need to - // make certain to release this non-existent reference so that the buffer is - // cleaned up during the cleanupBufferInProgress helper. In the case of a - // successful queue operation, we need to make certain that the - // cleanupBufferInProgress helper does not release the buffer since it needs - // to remain alive in the queue. We acomplish this by NULLing out the - // buffer pointer before calling the cleanup helper. - buffer_in_progress_ = NULL; + substream_meta_->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); + substream_meta_->setInt32 (kKeySampleRate, sample_rate); + substream_meta_->setInt32 (kKeyChannelCount, channel_cnt); -bailout: - cleanupBufferInProgress(); -} + if (esds_data) { + substream_meta_->setData(kKeyESDS, kTypeESDS, + esds_data, esds_data_size); + } + return true; +} void AAH_RXPlayer::Substream::processTSTransform(const LinearTransform& trans) { if (decoder_ != NULL) { @@ -471,27 +642,35 @@ bool AAH_RXPlayer::Substream::isAboutToUnderflow() { bool AAH_RXPlayer::Substream::setupSubstreamType(uint8_t substream_type, uint8_t codec_type) { - // Sanity check the codec type. Right now we only support MP3. Also check - // for conflicts with previously delivered codec types. - if (substream_details_known_ && (codec_type != codec_type_)) { - ALOGV("RXed TRTP Payload for SSRC=0x%08x where codec type (%u) does not" - " match previously received codec type (%u)", - ssrc_, codec_type, codec_type_); - return false; - } + // Sanity check the codec type. Right now we only support MP3 and AAC. + // Also check for conflicts with previously delivered codec types. + if (substream_details_known_) { + if (codec_type != codec_type_) { + ALOGV("RXed TRTP Payload for SSRC=0x%08x where codec type (%u) does" + " not match previously received codec type (%u)", + ssrc_, codec_type, codec_type_); + return false; + } - if (codec_type != 0x03) { - ALOGV("RXed TRTP Audio Payload for SSRC=0x%08x with unsupported codec" - " type (%u)", ssrc_, codec_type); - return false; + return true; } - if (!substream_details_known_) { - substream_type_ = substream_type; - codec_type_ = codec_type; - substream_details_known_ = true; + switch (codec_type) { + // MP3 and AAC are all we support right now. + case TRTPAudioPacket::kCodecMPEG1Audio: + case TRTPAudioPacket::kCodecAACAudio: + break; + + default: + ALOGV("RXed TRTP Audio Payload for SSRC=0x%08x with unsupported" + " codec type (%u)", ssrc_, codec_type); + return false; } + substream_type_ = substream_type; + codec_type_ = codec_type; + substream_details_known_ = true; + return true; } diff --git a/media/libaah_rtp/aah_tx_packet.cpp b/media/libaah_rtp/aah_tx_packet.cpp index 3f6e0e9520..4cd6e4715b 100644 --- a/media/libaah_rtp/aah_tx_packet.cpp +++ b/media/libaah_rtp/aah_tx_packet.cpp @@ -142,12 +142,18 @@ void TRTPAudioPacket::setVolume(uint8_t val) { mVolume = val; } -void TRTPAudioPacket::setAccessUnitData(void* data, int len) { +void TRTPAudioPacket::setAccessUnitData(const void* data, size_t len) { CHECK(!mIsPacked); mAccessUnitData = data; mAccessUnitLen = len; } +void TRTPAudioPacket::setAuxData(const void* data, size_t len) { + CHECK(!mIsPacked); + mAuxData = data; + mAuxDataLen = len; +} + /*** TRTP control packet properties ***/ void TRTPControlPacket::setCommandID(TRTPCommandID val) { @@ -232,6 +238,7 @@ bool TRTPAudioPacket::pack() { } int packetLen = kRTPHeaderLen + + mAuxDataLen + mAccessUnitLen + TRTPHeaderLen(); @@ -249,16 +256,24 @@ bool TRTPAudioPacket::pack() { mPacketLen = packetLen; uint8_t* cur = mPacket; + bool hasAux = mAuxData && mAuxDataLen; + uint8_t flags = (static_cast(hasAux) << 4) | + (static_cast(mRandomAccessPoint) << 3) | + (static_cast(mDropable) << 2) | + (static_cast(mDiscontinuity) << 1) | + (static_cast(mEndOfStream)); writeTRTPHeader(cur, true, packetLen); writeU8(cur, mCodecType); - writeU8(cur, - (static_cast(mRandomAccessPoint) << 3) | - (static_cast(mDropable) << 2) | - (static_cast(mDiscontinuity) << 1) | - (static_cast(mEndOfStream))); + writeU8(cur, flags); writeU8(cur, mVolume); + if (hasAux) { + writeU32(cur, mAuxDataLen); + memcpy(cur, mAuxData, mAuxDataLen); + cur += mAuxDataLen; + } + memcpy(cur, mAccessUnitData, mAccessUnitLen); mIsPacked = true; @@ -293,12 +308,10 @@ int TRTPAudioPacket::TRTPHeaderLen() const { } - // TODO : properly compute aux data length. Currently, nothing - // uses aux data, so its length is always 0. - int auxDataLength = 0; + int auxDataLenField = (NULL != mAuxData) ? sizeof(uint32_t) : 0; return TRTPPacket::TRTPHeaderLen() + 3 + - auxDataLength + + auxDataLenField + pcmParamLength; } diff --git a/media/libaah_rtp/aah_tx_packet.h b/media/libaah_rtp/aah_tx_packet.h index 833803ea1b..7f78ea0b11 100644 --- a/media/libaah_rtp/aah_tx_packet.h +++ b/media/libaah_rtp/aah_tx_packet.h @@ -25,7 +25,7 @@ namespace android { class TRTPPacket : public RefBase { - protected: + public: enum TRTPHeaderType { kHeaderTypeAudio = 1, kHeaderTypeVideo = 2, @@ -33,6 +33,12 @@ class TRTPPacket : public RefBase { kHeaderTypeControl = 4, }; + enum TRTPPayloadFlags { + kFlag_TSTransformPresent = 0x02, + kFlag_TSValid = 0x01, + }; + + protected: TRTPPacket(TRTPHeaderType headerType) : mIsPacked(false) , mVersion(2) @@ -121,6 +127,14 @@ class TRTPPacket : public RefBase { class TRTPAudioPacket : public TRTPPacket { public: + enum AudioPayloadFlags { + kFlag_AuxLengthPresent = 0x10, + kFlag_RandomAccessPoint = 0x08, + kFlag_Dropable = 0x04, + kFlag_Discontinuity = 0x02, + kFlag_EndOfStream = 0x01, + }; + TRTPAudioPacket() : TRTPPacket(kHeaderTypeAudio) , mCodecType(kCodecInvalid) @@ -129,13 +143,17 @@ class TRTPAudioPacket : public TRTPPacket { , mDiscontinuity(false) , mEndOfStream(false) , mVolume(0) - , mAccessUnitData(NULL) { } + , mAccessUnitData(NULL) + , mAccessUnitLen(0) + , mAuxData(NULL) + , mAuxDataLen(0) { } enum TRTPAudioCodecType { kCodecInvalid = 0, kCodecPCMBigEndian = 1, kCodecPCMLittleEndian = 2, kCodecMPEG1Audio = 3, + kCodecAACAudio = 4, }; void setCodecType(TRTPAudioCodecType val); @@ -144,7 +162,8 @@ class TRTPAudioPacket : public TRTPPacket { void setDiscontinuity(bool val); void setEndOfStream(bool val); void setVolume(uint8_t val); - void setAccessUnitData(void* data, int len); + void setAccessUnitData(const void* data, size_t len); + void setAuxData(const void* data, size_t len); virtual bool pack(); @@ -158,8 +177,11 @@ class TRTPAudioPacket : public TRTPPacket { bool mDiscontinuity; bool mEndOfStream; uint8_t mVolume; - void* mAccessUnitData; - int mAccessUnitLen; + + const void* mAccessUnitData; + size_t mAccessUnitLen; + const void* mAuxData; + size_t mAuxDataLen; DISALLOW_EVIL_CONSTRUCTORS(TRTPAudioPacket); }; diff --git a/media/libaah_rtp/aah_tx_player.cpp b/media/libaah_rtp/aah_tx_player.cpp index a79a9891ef..90f78945bd 100644 --- a/media/libaah_rtp/aah_tx_player.cpp +++ b/media/libaah_rtp/aah_tx_player.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -98,6 +99,8 @@ AAH_TXPlayer::AAH_TXPlayer() mPumpAudioEvent = new AAH_TXEvent(this, &AAH_TXPlayer::onPumpAudio); mPumpAudioEventPending = false; + mAudioCodecData = NULL; + reset_l(); } @@ -398,7 +401,76 @@ void AAH_TXPlayer::onPrepareAsyncEvent() { } } - mAudioSource->getFormat()->findInt64(kKeyDuration, &mDurationUs); + mAudioFormat = mAudioSource->getFormat(); + if (!mAudioFormat->findInt64(kKeyDuration, &mDurationUs)) + mDurationUs = 1; + + const char* mime_type = NULL; + if (!mAudioFormat->findCString(kKeyMIMEType, &mime_type)) { + ALOGE("Failed to find audio substream MIME type during prepare."); + abortPrepare(BAD_VALUE); + return; + } + + if (!strcmp(mime_type, MEDIA_MIMETYPE_AUDIO_MPEG)) { + mAudioCodec = TRTPAudioPacket::kCodecMPEG1Audio; + } else + if (!strcmp(mime_type, MEDIA_MIMETYPE_AUDIO_AAC)) { + mAudioCodec = TRTPAudioPacket::kCodecAACAudio; + + uint32_t type; + int32_t sample_rate; + int32_t channel_count; + const void* esds_data; + size_t esds_len; + + if (!mAudioFormat->findInt32(kKeySampleRate, &sample_rate)) { + ALOGE("Failed to find sample rate for AAC substream."); + abortPrepare(BAD_VALUE); + return; + } + + if (!mAudioFormat->findInt32(kKeyChannelCount, &channel_count)) { + ALOGE("Failed to find channel count for AAC substream."); + abortPrepare(BAD_VALUE); + return; + } + + if (!mAudioFormat->findData(kKeyESDS, &type, &esds_data, &esds_len)) { + ALOGE("Failed to find codec init data for AAC substream."); + abortPrepare(BAD_VALUE); + return; + } + + CHECK(NULL == mAudioCodecData); + mAudioCodecDataSize = esds_len + + sizeof(sample_rate) + + sizeof(channel_count); + mAudioCodecData = new uint8_t[mAudioCodecDataSize]; + if (NULL == mAudioCodecData) { + ALOGE("Failed to allocate %u bytes for AAC substream codec aux" + " data.", mAudioCodecDataSize); + mAudioCodecDataSize = 0; + abortPrepare(BAD_VALUE); + return; + } + + uint8_t* tmp = mAudioCodecData; + tmp[0] = static_cast((sample_rate >> 24) & 0xFF); + tmp[1] = static_cast((sample_rate >> 16) & 0xFF); + tmp[2] = static_cast((sample_rate >> 8) & 0xFF); + tmp[3] = static_cast((sample_rate ) & 0xFF); + tmp[4] = static_cast((channel_count >> 24) & 0xFF); + tmp[5] = static_cast((channel_count >> 16) & 0xFF); + tmp[6] = static_cast((channel_count >> 8) & 0xFF); + tmp[7] = static_cast((channel_count ) & 0xFF); + + memcpy(tmp + 8, esds_data, esds_len); + } else { + ALOGE("Unsupported MIME type \"%s\" in audio substream", mime_type); + abortPrepare(BAD_VALUE); + return; + } status_t err = mAudioSource->start(); if (err != OK) { @@ -666,6 +738,11 @@ void AAH_TXPlayer::reset_l() { mAudioSource->stop(); } mAudioSource.clear(); + mAudioCodec = TRTPAudioPacket::kCodecInvalid; + mAudioFormat = NULL; + delete[] mAudioCodecData; + mAudioCodecData = NULL; + mAudioCodecDataSize = 0; mFlags = 0; mExtractorFlags = 0; @@ -1078,14 +1155,24 @@ void AAH_TXPlayer::onPumpAudio() { packet->setPTS(mediaTimeUs); packet->setSubstreamID(1); - packet->setCodecType(TRTPAudioPacket::kCodecMPEG1Audio); + packet->setCodecType(mAudioCodec); packet->setVolume(mTRTPVolume); // TODO : introduce a throttle for this so we can control the // frequency with which transforms get sent. packet->setClockTransform(mCurrentClockTransform); packet->setAccessUnitData(data, mediaBuffer->range_length()); + + // TODO : while its pretty much universally true that audio ES payloads + // are all RAPs across all codecs, it might be a good idea to throttle + // the frequency with which we send codec out of band data to the RXers. + // If/when we do, we need to flag only those payloads which have + // required out of band data attached to them as RAPs. packet->setRandomAccessPoint(true); + if (mAudioCodecData && mAudioCodecDataSize) { + packet->setAuxData(mAudioCodecData, mAudioCodecDataSize); + } + queuePacketToSender_l(packet); mediaBuffer->release(); diff --git a/media/libaah_rtp/aah_tx_player.h b/media/libaah_rtp/aah_tx_player.h index 64cf5dc11a..094c6f2b14 100644 --- a/media/libaah_rtp/aah_tx_player.h +++ b/media/libaah_rtp/aah_tx_player.h @@ -153,6 +153,11 @@ class AAH_TXPlayer : public MediaPlayerHWInterface { sp mCachedSource; sp mAudioSource; + TRTPAudioPacket::TRTPAudioCodecType mAudioCodec; + sp mAudioFormat; + uint8_t* mAudioCodecData; + size_t mAudioCodecDataSize; + int64_t mDurationUs; int64_t mBitrate; diff --git a/media/libaah_rtp/aah_tx_sender.cpp b/media/libaah_rtp/aah_tx_sender.cpp index d991ea76af..499068c17d 100644 --- a/media/libaah_rtp/aah_tx_sender.cpp +++ b/media/libaah_rtp/aah_tx_sender.cpp @@ -283,7 +283,8 @@ void AAH_TXSender::trimRetryBuffers() { // remove the state for any endpoints that are no longer in use for (size_t i = 0; i < endpointsToRemove.size(); i++) { Endpoint& e = endpointsToRemove.editItemAt(i); - ALOGD("*** %s removing endpoint addr=%08x", __PRETTY_FUNCTION__, e.addr); + ALOGD("*** %s removing endpoint addr=%08x", + __PRETTY_FUNCTION__, e.addr); size_t index = mEndpointMap.indexOfKey(e); delete mEndpointMap.valueAt(index); mEndpointMap.removeItemsAt(index); -- 2.11.0