summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorJohn Grossman <johngro@google.com>2012-02-18 17:46:40 -0800
committerJohn Grossman <johngro@google.com>2012-03-01 14:41:35 -0800
commit4b77dc28097288cb062fce6bf5de0fb3394877a9 (patch)
tree8f5b8bfaf308065aef9999dcb1b2950ef8656166 /media
parentf08d7d0ee19c225d5b67fb49046290adfd9ab4bc (diff)
downloadframeworks_av-4b77dc28097288cb062fce6bf5de0fb3394877a9.zip
frameworks_av-4b77dc28097288cb062fce6bf5de0fb3394877a9.tar.gz
frameworks_av-4b77dc28097288cb062fce6bf5de0fb3394877a9.tar.bz2
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 <johngro@google.com>
Diffstat (limited to 'media')
-rw-r--r--media/libaah_rtp/aah_decoder_pump.cpp8
-rw-r--r--media/libaah_rtp/aah_rx_player.h11
-rw-r--r--media/libaah_rtp/aah_rx_player_core.cpp10
-rw-r--r--media/libaah_rtp/aah_rx_player_ring_buffer.cpp4
-rw-r--r--media/libaah_rtp/aah_rx_player_substream.cpp381
-rw-r--r--media/libaah_rtp/aah_tx_packet.cpp33
-rw-r--r--media/libaah_rtp/aah_tx_packet.h32
-rw-r--r--media/libaah_rtp/aah_tx_player.cpp91
-rw-r--r--media/libaah_rtp/aah_tx_player.h5
-rw-r--r--media/libaah_rtp/aah_tx_sender.cpp3
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 72fe43b..28b8c7b 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 7a1b6e3..ba5617e 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<MetaData> substream_meta_;
MediaBuffer* buffer_in_progress_;
uint32_t expected_buffer_size_;
uint32_t buffer_filled_;
+ Vector<uint8_t> aux_data_in_progress_;
+ uint32_t aux_data_expected_size_;
+
sp<AAH_DecoderPump> 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 d2b3386..d6b31fd 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<ssize_t>(sizeof(RetransRequest))) {
- ALOGV("Dropping packet, too short to contain NAK payload (%u bytes)",
- static_cast<uint32_t>(amt));
+ ALOGV("Dropping packet, too short to contain NAK payload"
+ " (%u bytes)", static_cast<uint32_t>(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 0d8b31f..779405e 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 1e4c784..18b0e2b 100644
--- a/media/libaah_rtp/aah_rx_player_substream.cpp
+++ b/media/libaah_rtp/aah_rx_player_substream.cpp
@@ -27,6 +27,11 @@
#include <media/stagefright/Utils.h>
#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<uint32_t>(buf[2]) << 24) |
- (static_cast<uint32_t>(buf[3]) << 16) |
- (static_cast<uint32_t>(buf[4]) << 8) |
- static_cast<uint32_t>(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<int64_t>(buf[parse_offset ]) << 56) |
- (static_cast<int64_t>(buf[parse_offset + 1]) << 48) |
- (static_cast<int64_t>(buf[parse_offset + 2]) << 40) |
- (static_cast<int64_t>(buf[parse_offset + 3]) << 32);
- ts |= ts_lower;
+ uint32_t ts_upper = U32_AT(buf + parse_offset);
parse_offset += 4;
+ ts = (static_cast<int64_t>(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<uint8_t*>(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<const uint8_t*>(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 3f6e0e9..4cd6e47 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<int>(hasAux) << 4) |
+ (static_cast<int>(mRandomAccessPoint) << 3) |
+ (static_cast<int>(mDropable) << 2) |
+ (static_cast<int>(mDiscontinuity) << 1) |
+ (static_cast<int>(mEndOfStream));
writeTRTPHeader(cur, true, packetLen);
writeU8(cur, mCodecType);
- writeU8(cur,
- (static_cast<int>(mRandomAccessPoint) << 3) |
- (static_cast<int>(mDropable) << 2) |
- (static_cast<int>(mDiscontinuity) << 1) |
- (static_cast<int>(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 833803e..7f78ea0 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 a79a989..90f7894 100644
--- a/media/libaah_rtp/aah_tx_player.cpp
+++ b/media/libaah_rtp/aah_tx_player.cpp
@@ -28,6 +28,7 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <utils/Timers.h>
@@ -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<uint8_t>((sample_rate >> 24) & 0xFF);
+ tmp[1] = static_cast<uint8_t>((sample_rate >> 16) & 0xFF);
+ tmp[2] = static_cast<uint8_t>((sample_rate >> 8) & 0xFF);
+ tmp[3] = static_cast<uint8_t>((sample_rate ) & 0xFF);
+ tmp[4] = static_cast<uint8_t>((channel_count >> 24) & 0xFF);
+ tmp[5] = static_cast<uint8_t>((channel_count >> 16) & 0xFF);
+ tmp[6] = static_cast<uint8_t>((channel_count >> 8) & 0xFF);
+ tmp[7] = static_cast<uint8_t>((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 64cf5dc..094c6f2 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<NuCachedSource2> mCachedSource;
sp<MediaSource> mAudioSource;
+ TRTPAudioPacket::TRTPAudioCodecType mAudioCodec;
+ sp<MetaData> 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 d991ea7..499068c 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);