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>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,56 +409,10 @@ void AAH_RXPlayer::Substream::processCompletedBuffer() {
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
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.");
|
||||
// Make sure our metadata used to initialize the decoder has been properly
|
||||
// set up.
|
||||
if (!setupSubstreamMeta())
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
// Extract the channel count and the sample rate from the MP3 header. The
|
||||
// stagefright MP3 requires that these be delivered before decoing can
|
||||
// begin.
|
||||
if (!GetMPEGAudioFrameSize(U32_AT(buffer_data),
|
||||
&frame_size,
|
||||
&sample_rate,
|
||||
&channel_count,
|
||||
NULL,
|
||||
NULL)) {
|
||||
ALOGV("Failed to parse MP3 header in payload, droping payload.");
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
|
||||
// Make sure that our substream metadata is set up properly. If there has
|
||||
// been a format change, be sure to reset the underlying decoder. In
|
||||
// stagefright, it seems like the only way to do this is to destroy and
|
||||
// recreate the decoder.
|
||||
if (substream_meta_ == NULL) {
|
||||
substream_meta_ = new MetaData();
|
||||
|
||||
if (substream_meta_ == NULL) {
|
||||
ALOGE("Failed to allocate MetaData structure for substream");
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
substream_meta_->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
|
||||
substream_meta_->setInt32 (kKeyChannelCount, channel_count);
|
||||
substream_meta_->setInt32 (kKeySampleRate, sample_rate);
|
||||
} else {
|
||||
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 ((prev_channel_count != channel_count) ||
|
||||
(prev_sample_rate != sample_rate)) {
|
||||
ALOGW("Format change detected, forcing decoder reset.");
|
||||
cleanupDecoder();
|
||||
|
||||
substream_meta_->setInt32(kKeyChannelCount, channel_count);
|
||||
substream_meta_->setInt32(kKeySampleRate, sample_rate);
|
||||
}
|
||||
}
|
||||
|
||||
// If our decoder has not be set up, do so now.
|
||||
res = decoder_->init(substream_meta_);
|
||||
@@ -418,7 +428,7 @@ void AAH_RXPlayer::Substream::processCompletedBuffer() {
|
||||
|
||||
if (res != OK) {
|
||||
ALOGD("Failed to queue payload for decode, resetting decoder pump!"
|
||||
" (res = %d)", res);
|
||||
" (res = %d)", res);
|
||||
status_ = res;
|
||||
cleanupDecoder();
|
||||
cleanupBufferInProgress();
|
||||
@@ -454,6 +464,167 @@ 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.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract the channel count and the sample rate from the MP3 header. The
|
||||
// stagefright MP3 requires that these be delivered before decoing can
|
||||
// begin.
|
||||
if (!GetMPEGAudioFrameSize(U32_AT(buffer_data),
|
||||
&frame_size,
|
||||
&sample_rate,
|
||||
&channel_count,
|
||||
NULL,
|
||||
NULL)) {
|
||||
ALOGV("Failed to parse MP3 header in payload, droping payload.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Make sure that our substream metadata is set up properly. If there has
|
||||
// been a format change, be sure to reset the underlying decoder. In
|
||||
// stagefright, it seems like the only way to do this is to destroy and
|
||||
// recreate the decoder.
|
||||
if (substream_meta_ == NULL) {
|
||||
substream_meta_ = new MetaData();
|
||||
|
||||
if (substream_meta_ == NULL) {
|
||||
ALOGE("Failed to allocate MetaData structure for MP3 substream");
|
||||
return false;
|
||||
}
|
||||
|
||||
substream_meta_->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
|
||||
substream_meta_->setInt32 (kKeyChannelCount, channel_count);
|
||||
substream_meta_->setInt32 (kKeySampleRate, sample_rate);
|
||||
} else {
|
||||
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 ((prev_channel_count != channel_count) ||
|
||||
(prev_sample_rate != sample_rate)) {
|
||||
ALOGW("MP3 format change detected, forcing decoder reset.");
|
||||
cleanupDecoder();
|
||||
|
||||
substream_meta_->setInt32(kKeyChannelCount, channel_count);
|
||||
substream_meta_->setInt32(kKeySampleRate, sample_rate);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
CHECK(substream_meta_ == NULL);
|
||||
|
||||
substream_meta_ = new MetaData();
|
||||
if (substream_meta_ == NULL) {
|
||||
ALOGE("Failed to allocate MetaData structure for AAC substream");
|
||||
return false;
|
||||
}
|
||||
|
||||
substream_meta_->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
|
||||
substream_meta_->setInt32 (kKeySampleRate, sample_rate);
|
||||
substream_meta_->setInt32 (kKeyChannelCount, channel_cnt);
|
||||
|
||||
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,26 +642,34 @@ 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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (codec_type != 0x03) {
|
||||
ALOGV("RXed TRTP Audio Payload for SSRC=0x%08x with unsupported codec"
|
||||
" type (%u)", ssrc_, codec_type);
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
|
||||
if (!substream_details_known_) {
|
||||
substream_type_ = substream_type;
|
||||
codec_type_ = codec_type;
|
||||
substream_details_known_ = true;
|
||||
}
|
||||
substream_type_ = substream_type;
|
||||
codec_type_ = codec_type;
|
||||
substream_details_known_ = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user