| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cobalt/media/formats/mp2t/es_parser_adts.h" |
| |
| #include <algorithm> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "cobalt/media/base/audio_timestamp_helper.h" |
| #include "cobalt/media/base/bit_reader.h" |
| #include "cobalt/media/base/channel_layout.h" |
| #include "cobalt/media/base/media_util.h" |
| #include "cobalt/media/base/stream_parser_buffer.h" |
| #include "cobalt/media/base/timestamp_constants.h" |
| #include "cobalt/media/formats/common/offset_byte_queue.h" |
| #include "cobalt/media/formats/mp2t/mp2t_common.h" |
| #include "cobalt/media/formats/mpeg/adts_constants.h" |
| #include "starboard/types.h" |
| |
| namespace cobalt { |
| namespace media { |
| |
| static int ExtractAdtsFrameSize(const uint8_t* adts_header) { |
| return ((static_cast<int>(adts_header[5]) >> 5) | |
| (static_cast<int>(adts_header[4]) << 3) | |
| ((static_cast<int>(adts_header[3]) & 0x3) << 11)); |
| } |
| |
| // Return true if buf corresponds to an ADTS syncword. |
| // |buf| size must be at least 2. |
| static bool isAdtsSyncWord(const uint8_t* buf) { |
| // The first 12 bits must be 1. |
| // The layer field (2 bits) must be set to 0. |
| return (buf[0] == 0xff) && ((buf[1] & 0xf6) == 0xf0); |
| } |
| |
| namespace mp2t { |
| |
| struct EsParserAdts::AdtsFrame { |
| // Pointer to the ES data. |
| const uint8_t* data; |
| |
| // Frame size; |
| int size; |
| |
| // Frame offset in the ES queue. |
| int64_t queue_offset; |
| }; |
| |
| bool EsParserAdts::LookForAdtsFrame(AdtsFrame* adts_frame) { |
| int es_size; |
| const uint8_t* es; |
| es_queue_->Peek(&es, &es_size); |
| |
| int max_offset = es_size - kADTSHeaderMinSize; |
| if (max_offset <= 0) return false; |
| |
| for (int offset = 0; offset < max_offset; offset++) { |
| const uint8_t* cur_buf = &es[offset]; |
| if (!isAdtsSyncWord(cur_buf)) continue; |
| |
| int frame_size = ExtractAdtsFrameSize(cur_buf); |
| if (frame_size < kADTSHeaderMinSize) { |
| // Too short to be an ADTS frame. |
| continue; |
| } |
| |
| int remaining_size = es_size - offset; |
| if (remaining_size < frame_size) { |
| // Not a full frame: will resume when we have more data. |
| es_queue_->Pop(offset); |
| return false; |
| } |
| |
| // Check whether there is another frame |
| // |size| apart from the current one. |
| if (remaining_size >= frame_size + 2 && |
| !isAdtsSyncWord(&cur_buf[frame_size])) { |
| continue; |
| } |
| |
| es_queue_->Pop(offset); |
| es_queue_->Peek(&adts_frame->data, &es_size); |
| adts_frame->queue_offset = es_queue_->head(); |
| adts_frame->size = frame_size; |
| DVLOG(LOG_LEVEL_ES) << "ADTS syncword @ pos=" << adts_frame->queue_offset |
| << " frame_size=" << adts_frame->size; |
| DVLOG(LOG_LEVEL_ES) << "ADTS header: " |
| << base::HexEncode(adts_frame->data, |
| kADTSHeaderMinSize); |
| return true; |
| } |
| |
| es_queue_->Pop(max_offset); |
| return false; |
| } |
| |
| void EsParserAdts::SkipAdtsFrame(const AdtsFrame& adts_frame) { |
| DCHECK_EQ(adts_frame.queue_offset, es_queue_->head()); |
| es_queue_->Pop(adts_frame.size); |
| } |
| |
| EsParserAdts::EsParserAdts(const NewAudioConfigCB& new_audio_config_cb, |
| const EmitBufferCB& emit_buffer_cb, |
| bool sbr_in_mimetype) |
| : new_audio_config_cb_(new_audio_config_cb), |
| emit_buffer_cb_(emit_buffer_cb), |
| sbr_in_mimetype_(sbr_in_mimetype) {} |
| |
| EsParserAdts::~EsParserAdts() {} |
| |
| bool EsParserAdts::ParseFromEsQueue() { |
| // Look for every ADTS frame in the ES buffer. |
| AdtsFrame adts_frame; |
| while (LookForAdtsFrame(&adts_frame)) { |
| // Update the audio configuration if needed. |
| DCHECK_GE(adts_frame.size, kADTSHeaderMinSize); |
| if (!UpdateAudioConfiguration(adts_frame.data, adts_frame.size)) |
| return false; |
| |
| // Get the PTS & the duration of this access unit. |
| TimingDesc current_timing_desc = |
| GetTimingDescriptor(adts_frame.queue_offset); |
| if (current_timing_desc.pts != kNoTimestamp) |
| audio_timestamp_helper_->SetBaseTimestamp(current_timing_desc.pts); |
| |
| if (audio_timestamp_helper_->base_timestamp() == kNoTimestamp) { |
| DVLOG(1) << "Skipping audio frame with unknown timestamp"; |
| SkipAdtsFrame(adts_frame); |
| continue; |
| } |
| base::TimeDelta current_pts = audio_timestamp_helper_->GetTimestamp(); |
| base::TimeDelta frame_duration = |
| audio_timestamp_helper_->GetFrameDuration(kSamplesPerAACFrame); |
| |
| // Emit an audio frame. |
| bool is_key_frame = true; |
| |
| // TODO(wolenetz/acolwell): Validate and use a common cross-parser TrackId |
| // type and allow multiple audio tracks. See https://crbug.com/341581. |
| scoped_refptr<StreamParserBuffer> stream_parser_buffer = |
| StreamParserBuffer::CopyFrom(adts_frame.data, adts_frame.size, |
| is_key_frame, DemuxerStream::AUDIO, |
| kMp2tAudioTrackId); |
| if (!stream_parser_buffer) { |
| return false; |
| } |
| stream_parser_buffer->set_timestamp(current_pts); |
| stream_parser_buffer->SetDecodeTimestamp( |
| DecodeTimestamp::FromPresentationTime(current_pts)); |
| stream_parser_buffer->set_duration(frame_duration); |
| emit_buffer_cb_.Run(stream_parser_buffer); |
| |
| // Update the PTS of the next frame. |
| audio_timestamp_helper_->AddFrames(kSamplesPerAACFrame); |
| |
| // Skip the current frame. |
| SkipAdtsFrame(adts_frame); |
| } |
| |
| return true; |
| } |
| |
| void EsParserAdts::Flush() {} |
| |
| void EsParserAdts::ResetInternal() { |
| last_audio_decoder_config_ = AudioDecoderConfig(); |
| } |
| |
| bool EsParserAdts::UpdateAudioConfiguration(const uint8_t* adts_header, |
| int size) { |
| int orig_sample_rate; |
| ChannelLayout channel_layout; |
| std::vector<uint8_t> extra_data; |
| if (adts_parser_.ParseFrameHeader(adts_header, size, nullptr, |
| &orig_sample_rate, &channel_layout, nullptr, |
| nullptr, &extra_data) <= 0) { |
| return false; |
| } |
| |
| // The following code is written according to ISO 14496 Part 3 Table 1.11 and |
| // Table 1.22. (Table 1.11 refers to the capping to 48000, Table 1.22 refers |
| // to SBR doubling the AAC sample rate.) |
| // TODO(damienv) : Extend sample rate cap to 96kHz for Level 5 content. |
| const int extended_samples_per_second = |
| sbr_in_mimetype_ ? std::min(2 * orig_sample_rate, 48000) |
| : orig_sample_rate; |
| |
| AudioDecoderConfig audio_decoder_config( |
| kCodecAAC, kSampleFormatS16, channel_layout, extended_samples_per_second, |
| extra_data, Unencrypted()); |
| |
| if (!audio_decoder_config.Matches(last_audio_decoder_config_)) { |
| DVLOG(1) << "Sampling frequency: " |
| << audio_decoder_config.samples_per_second() |
| << " SBR=" << sbr_in_mimetype_; |
| DVLOG(1) << "Channel layout: " |
| << ChannelLayoutToString(audio_decoder_config.channel_layout()); |
| |
| // For AAC audio with SBR (Spectral Band Replication) the sampling rate is |
| // doubled above, but AudioTimestampHelper should still use the original |
| // sample rate to compute audio timestamps and durations correctly. |
| |
| // Reset the timestamp helper to use a new time scale. |
| if (audio_timestamp_helper_ && |
| audio_timestamp_helper_->base_timestamp() != kNoTimestamp) { |
| base::TimeDelta base_timestamp = audio_timestamp_helper_->GetTimestamp(); |
| audio_timestamp_helper_.reset(new AudioTimestampHelper(orig_sample_rate)); |
| audio_timestamp_helper_->SetBaseTimestamp(base_timestamp); |
| } else { |
| audio_timestamp_helper_.reset(new AudioTimestampHelper(orig_sample_rate)); |
| } |
| // Audio config notification. |
| last_audio_decoder_config_ = audio_decoder_config; |
| new_audio_config_cb_.Run(audio_decoder_config); |
| } |
| |
| return true; |
| } |
| |
| } // namespace mp2t |
| } // namespace media |
| } // namespace cobalt |