/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "LibAAH_RTP" //#define LOG_NDEBUG 0 #include #include #include #include #include #include #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 { int64_t AAH_RXPlayer::Substream::kAboutToUnderflowThreshold = 50ull * 1000; AAH_RXPlayer::Substream::Substream(uint32_t ssrc, OMXClient& omx) { ssrc_ = ssrc; substream_details_known_ = false; buffer_in_progress_ = NULL; status_ = OK; codec_mime_type_ = ""; decoder_ = new AAH_DecoderPump(omx); if (decoder_ == NULL) { ALOGE("%s failed to allocate decoder pump!", __PRETTY_FUNCTION__); } if (OK != decoder_->initCheck()) { ALOGE("%s failed to initialize decoder pump!", __PRETTY_FUNCTION__); } // cleanupBufferInProgress will reset most of the internal state variables. // Just need to make sure that buffer_in_progress_ is NULL before calling. cleanupBufferInProgress(); } AAH_RXPlayer::Substream::~Substream() { shutdown(); } void AAH_RXPlayer::Substream::shutdown() { substream_meta_ = NULL; status_ = OK; cleanupBufferInProgress(); cleanupDecoder(); } void AAH_RXPlayer::Substream::cleanupBufferInProgress() { if (NULL != buffer_in_progress_) { buffer_in_progress_->release(); buffer_in_progress_ = 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() { if (decoder_ != NULL) { decoder_->shutdown(); } } bool AAH_RXPlayer::Substream::shouldAbort(const char* log_tag) { // If we have already encountered a fatal error, do nothing. We are just // waiting for our owner to shut us down now. if (OK != status_) { ALOGV("Skipping %s, substream has encountered fatal error (%d).", log_tag, status_); return true; } return false; } void AAH_RXPlayer::Substream::processPayloadStart(uint8_t* buf, uint32_t amt, int32_t ts_lower) { uint32_t min_length = 6; if (shouldAbort(__PRETTY_FUNCTION__)) { return; } // Do we have a buffer in progress already? If so, abort the buffer. In // theory, this should never happen. If there were a discontinutity in the // stream, the discon in the seq_nos at the RTP level should have already // triggered a cleanup of the buffer in progress. To see a problem at this // level is an indication either of a bug in the transmitter, or some form // of terrible corruption/tampering on the wire. if (NULL != buffer_in_progress_) { ALOGE("processPayloadStart is aborting payload already in progress."); cleanupBufferInProgress(); } // Parse enough of the header to know where we stand. Since this is a // payload start, it should begin with a TRTP header which has to be at // least 6 bytes long. if (amt < min_length) { ALOGV("Discarding payload too short to contain TRTP header (len = %u)", amt); return; } // Check the TRTP version number. if (0x01 != buf[0]) { ALOGV("Unexpected TRTP version (%u) in header. Expected %u.", buf[0], 1); return; } // Extract the substream type field and make sure its one we understand (and // one that does not conflict with any previously received substream type. uint8_t header_type = (buf[1] >> 4) & 0xF; switch (header_type) { case TRTPPacket::kHeaderTypeAudio: // Audio, yay! Just break. We understand audio payloads. break; case TRTPPacket::kHeaderTypeVideo: ALOGV("RXed packet with unhandled TRTP header type (Video)."); return; case TRTPPacket::kHeaderTypeSubpicture: ALOGV("RXed packet with unhandled TRTP header type (Subpicture)."); return; case TRTPPacket::kHeaderTypeControl: ALOGV("RXed packet with unhandled TRTP header type (Control)."); return; default: ALOGV("RXed packet with unhandled TRTP header type (%u).", header_type); return; } 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)", 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] & TRTPPacket::kFlag_TSValid; if (ts_valid) { min_length += 4; trtp_header_len += 4; if (amt < min_length) { ALOGV("Discarding payload too short to contain TRTP timestamp" " (len = %u)", amt); return; } } // Extract the TRTP length field and sanity check it. 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); return; } // Extract the rest of the timestamp field if valid. int64_t ts = 0; uint32_t parse_offset = 6; if (ts_valid) { 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] & TRTPPacket::kFlag_TSTransformPresent) { min_length += 24; parse_offset += 24; trtp_header_len += 24; if (amt < min_length) { ALOGV("Discarding payload too short to contain TRTP timestamp" " transformation (len = %u)", amt); return; } } // TODO : break the parsing into individual parsers for the different // payload types (audio, video, etc). // // At this point in time, we know that this is audio. Go ahead and parse // the basic header, check the codec type, and find the payload portion of // the packet. min_length += 3; 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; } uint8_t codec_type = buf[parse_offset ]; uint8_t flags = buf[parse_offset + 1]; uint8_t volume = buf[parse_offset + 2]; parse_offset += 3; trtp_header_len += 3; if (!setupSubstreamType(header_type, codec_type)) { return; } if (decoder_ != NULL) { decoder_->setRenderVolume(volume); } if (waiting_for_rap_ && !(flags & TRTPAudioPacket::kFlag_RandomAccessPoint)) { ALOGV("Dropping non-RAP TRTP Audio Payload while waiting for RAP."); return; } // 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; } // OK - everything left is just payload. Compute the payload size, start // 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 - aux_data_expected_size_; if (!expected_buffer_size_) { ALOGV("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_ + aux_data_expected_size_) < todo) { ALOGV("Extra data (%u > %u) present in initial TRTP Audio Payload;" " dropping payload.", todo, expected_buffer_size_ + aux_data_expected_size_); return; } buffer_filled_ = 0; buffer_in_progress_ = new MediaBuffer(expected_buffer_size_); if ((NULL == buffer_in_progress_) || (NULL == buffer_in_progress_->data())) { ALOGV("Failed to allocate MediaBuffer of length %u", expected_buffer_size_); cleanupBufferInProgress(); return; } sp meta = buffer_in_progress_->meta_data(); if (meta == NULL) { ALOGV("Missing metadata structure in allocated MediaBuffer; dropping" " payload"); cleanupBufferInProgress(); return; } meta->setCString(kKeyMIMEType, codec_mime_type_); if (ts_valid) { meta->setInt64(kKeyTime, ts); } // 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, buf, todo); buffer_filled_ = amt; buf += todo; amt -= todo; } if (buffer_filled_ >= expected_buffer_size_) { processCompletedBuffer(); } } void AAH_RXPlayer::Substream::processPayloadCont(uint8_t* buf, uint32_t amt) { if (shouldAbort(__PRETTY_FUNCTION__)) { return; } if (NULL == buffer_in_progress_) { ALOGV("TRTP Receiver skipping payload continuation; no buffer currently" " in progress."); 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) { ALOGV("Extra data (%u > %u) present in continued TRTP Audio Payload;" " dropping payload.", amt, buffer_left); cleanupBufferInProgress(); return; } if (amt > 0) { uint8_t* tgt = reinterpret_cast(buffer_in_progress_->data()); memcpy(tgt + buffer_filled_, buf, amt); buffer_filled_ += amt; } if (buffer_filled_ >= expected_buffer_size_) { processCompletedBuffer(); } } void AAH_RXPlayer::Substream::processCompletedBuffer() { status_t res; CHECK(NULL != buffer_in_progress_); if (decoder_ == NULL) { ALOGV("Dropping complete buffer, no decoder pump allocated"); 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."); 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) { decoder_->setRenderTSTransform(trans); } } bool AAH_RXPlayer::Substream::isAboutToUnderflow() { if (decoder_ == NULL) { return false; } return decoder_->isAboutToUnderflow(kAboutToUnderflowThreshold); } bool AAH_RXPlayer::Substream::setupSubstreamType(uint8_t substream_type, uint8_t codec_type) { // 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; } 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; } } // namespace android