#include <media/stagefright/Utils.h>
#include "aah_rx_player.h"
+#include "aah_tx_packet.h"
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
namespace android {
substream_details_known_ = false;
buffer_in_progress_ = NULL;
status_ = OK;
+ codec_mime_type_ = "";
decoder_ = new AAH_DecoderPump(omx);
if (decoder_ == NULL) {
cleanupBufferInProgress();
}
+AAH_RXPlayer::Substream::~Substream() {
+ shutdown();
+}
void AAH_RXPlayer::Substream::shutdown() {
substream_meta_ = NULL;
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() {
}
// 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) {
LOGV("TRTP length (%u) is too short to be valid. Must be at least %u"
" bytes.", trtp_len, min_length);
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
return;
}
+ // Check for the presence of codec aux data.
if (flags & 0x10) {
- LOGV("Dropping TRTP Audio Payload with aux codec data present (only"
- " handle MP3 right now, and it has no aux data)");
+ min_length += 4;
+ trtp_header_len += 4;
+
+ if (trtp_len < min_length) {
+ LOGV("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) {
+ LOGV("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) {
+ LOGV("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;
}
// 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_) {
LOGV("Dropping TRTP Audio Payload with 0 Access Unit length");
return;
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) {
LOGV("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;
}
// 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_) {
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) {
}
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_);
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) {
+ LOGE("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) {
+ LOGD("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:
+ LOGV("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) {
LOGV("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
NULL,
NULL)) {
LOGV("Failed to parse MP3 header in payload, droping payload.");
- goto bailout;
+ return false;
}
substream_meta_ = new MetaData();
if (substream_meta_ == NULL) {
- LOGE("Failed to allocate MetaData structure for substream");
- goto bailout;
+ LOGE("Failed to allocate MetaData structure for MP3 substream");
+ return false;
}
substream_meta_->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
if ((prev_channel_count != channel_count) ||
(prev_sample_rate != sample_rate)) {
- LOGW("Format change detected, forcing decoder reset.");
+ LOGW("MP3 format change detected, forcing decoder reset.");
cleanupDecoder();
substream_meta_->setInt32(kKeyChannelCount, channel_count);
}
}
- // If our decoder has not be set up, do so now.
- res = decoder_->init(substream_meta_);
- if (OK != res) {
- LOGE("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) {
+ LOGE("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;
+
+ substream_meta_->findInt32(kKeySampleRate, &prev_sample_rate);
+ substream_meta_->findInt32(kKeyChannelCount, &prev_channel_count);
+
+ // 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.
+ }
+
+ LOGW("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) {
- LOGD("Failed to queue payload for decode, resetting decoder pump!"
- " (res = %d)", res);
- status_ = res;
- cleanupDecoder();
- cleanupBufferInProgress();
+ substream_meta_ = new MetaData();
+ if (substream_meta_ == NULL) {
+ LOGE("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) {
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_)) {
- LOGV("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_) {
+ LOGV("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) {
- LOGV("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:
+ LOGV("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;
}
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <utils/Timers.h>
mPumpAudioEvent = new AAH_TXEvent(this, &AAH_TXPlayer::onPumpAudio);
mPumpAudioEventPending = false;
+ mAudioCodecData = NULL;
+
reset();
}
}
}
- 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)) {
+ LOGE("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)) {
+ LOGE("Failed to find sample rate for AAC substream.");
+ abortPrepare(BAD_VALUE);
+ return;
+ }
+
+ if (!mAudioFormat->findInt32(kKeyChannelCount, &channel_count)) {
+ LOGE("Failed to find channel count for AAC substream.");
+ abortPrepare(BAD_VALUE);
+ return;
+ }
+
+ if (!mAudioFormat->findData(kKeyESDS, &type, &esds_data, &esds_len)) {
+ LOGE("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) {
+ LOGE("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 {
+ LOGE("Unsupported MIME type \"%s\" in audio substream", mime_type);
+ abortPrepare(BAD_VALUE);
+ return;
+ }
status_t err = mAudioSource->start();
if (err != OK) {
mAudioSource->stop();
}
mAudioSource.clear();
+ mAudioCodec = TRTPAudioPacket::kCodecInvalid;
+ mAudioFormat = NULL;
+ delete[] mAudioCodecData;
+ mAudioCodecData = NULL;
+ mAudioCodecDataSize = 0;
mFlags = 0;
mExtractorFlags = 0;
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();