Home | History | Annotate | Download | only in mp2t
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "media/mp2t/mp2t_stream_parser.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/stl_util.h"
     10 #include "media/base/audio_decoder_config.h"
     11 #include "media/base/buffers.h"
     12 #include "media/base/stream_parser_buffer.h"
     13 #include "media/base/text_track_config.h"
     14 #include "media/base/video_decoder_config.h"
     15 #include "media/mp2t/es_parser.h"
     16 #include "media/mp2t/es_parser_adts.h"
     17 #include "media/mp2t/es_parser_h264.h"
     18 #include "media/mp2t/mp2t_common.h"
     19 #include "media/mp2t/ts_packet.h"
     20 #include "media/mp2t/ts_section.h"
     21 #include "media/mp2t/ts_section_pat.h"
     22 #include "media/mp2t/ts_section_pes.h"
     23 #include "media/mp2t/ts_section_pmt.h"
     24 
     25 namespace media {
     26 namespace mp2t {
     27 
     28 enum StreamType {
     29   // ISO-13818.1 / ITU H.222 Table 2.34 "Stream type assignments"
     30   kStreamTypeMpeg1Audio = 0x3,
     31   kStreamTypeAAC = 0xf,
     32   kStreamTypeAVC = 0x1b,
     33 };
     34 
     35 class PidState {
     36  public:
     37   enum PidType {
     38     kPidPat,
     39     kPidPmt,
     40     kPidAudioPes,
     41     kPidVideoPes,
     42   };
     43 
     44   PidState(int pid, PidType pid_tyoe,
     45            scoped_ptr<TsSection> section_parser);
     46 
     47   // Extract the content of the TS packet and parse it.
     48   // Return true if successful.
     49   bool PushTsPacket(const TsPacket& ts_packet);
     50 
     51   // Flush the PID state (possibly emitting some pending frames)
     52   // and reset its state.
     53   void Flush();
     54 
     55   // Enable/disable the PID.
     56   // Disabling a PID will reset its state and ignore any further incoming TS
     57   // packets.
     58   void Enable();
     59   void Disable();
     60   bool IsEnabled() const;
     61 
     62   PidType pid_type() const { return pid_type_; }
     63 
     64  private:
     65   void ResetState();
     66 
     67   int pid_;
     68   PidType pid_type_;
     69   scoped_ptr<TsSection> section_parser_;
     70 
     71   bool enable_;
     72 
     73   int continuity_counter_;
     74 };
     75 
     76 PidState::PidState(int pid, PidType pid_type,
     77                    scoped_ptr<TsSection> section_parser)
     78   : pid_(pid),
     79     pid_type_(pid_type),
     80     section_parser_(section_parser.Pass()),
     81     enable_(false),
     82     continuity_counter_(-1) {
     83   DCHECK(section_parser_);
     84 }
     85 
     86 bool PidState::PushTsPacket(const TsPacket& ts_packet) {
     87   DCHECK_EQ(ts_packet.pid(), pid_);
     88 
     89   // The current PID is not part of the PID filter,
     90   // just discard the incoming TS packet.
     91   if (!enable_)
     92     return true;
     93 
     94   int expected_continuity_counter = (continuity_counter_ + 1) % 16;
     95   if (continuity_counter_ >= 0 &&
     96       ts_packet.continuity_counter() != expected_continuity_counter) {
     97     DVLOG(1) << "TS discontinuity detected for pid: " << pid_;
     98     return false;
     99   }
    100 
    101   bool status = section_parser_->Parse(
    102       ts_packet.payload_unit_start_indicator(),
    103       ts_packet.payload(),
    104       ts_packet.payload_size());
    105 
    106   // At the minimum, when parsing failed, auto reset the section parser.
    107   // Components that use the StreamParser can take further action if needed.
    108   if (!status) {
    109     DVLOG(1) << "Parsing failed for pid = " << pid_;
    110     ResetState();
    111   }
    112 
    113   return status;
    114 }
    115 
    116 void PidState::Flush() {
    117   section_parser_->Flush();
    118   ResetState();
    119 }
    120 
    121 void PidState::Enable() {
    122   enable_ = true;
    123 }
    124 
    125 void PidState::Disable() {
    126   if (!enable_)
    127     return;
    128 
    129   ResetState();
    130   enable_ = false;
    131 }
    132 
    133 bool PidState::IsEnabled() const {
    134   return enable_;
    135 }
    136 
    137 void PidState::ResetState() {
    138   section_parser_->Reset();
    139   continuity_counter_ = -1;
    140 }
    141 
    142 Mp2tStreamParser::BufferQueueWithConfig::BufferQueueWithConfig(
    143     bool is_cfg_sent,
    144     const AudioDecoderConfig& audio_cfg,
    145     const VideoDecoderConfig& video_cfg)
    146   : is_config_sent(is_cfg_sent),
    147     audio_config(audio_cfg),
    148     video_config(video_cfg) {
    149 }
    150 
    151 Mp2tStreamParser::BufferQueueWithConfig::~BufferQueueWithConfig() {
    152 }
    153 
    154 Mp2tStreamParser::Mp2tStreamParser(bool sbr_in_mimetype)
    155   : sbr_in_mimetype_(sbr_in_mimetype),
    156     selected_audio_pid_(-1),
    157     selected_video_pid_(-1),
    158     is_initialized_(false),
    159     segment_started_(false),
    160     first_video_frame_in_segment_(true) {
    161 }
    162 
    163 Mp2tStreamParser::~Mp2tStreamParser() {
    164   STLDeleteValues(&pids_);
    165 }
    166 
    167 void Mp2tStreamParser::Init(
    168     const InitCB& init_cb,
    169     const NewConfigCB& config_cb,
    170     const NewBuffersCB& new_buffers_cb,
    171     const NewTextBuffersCB& /* text_cb */ ,
    172     const NeedKeyCB& need_key_cb,
    173     const NewMediaSegmentCB& new_segment_cb,
    174     const base::Closure& end_of_segment_cb,
    175     const LogCB& log_cb) {
    176   DCHECK(!is_initialized_);
    177   DCHECK(init_cb_.is_null());
    178   DCHECK(!init_cb.is_null());
    179   DCHECK(!config_cb.is_null());
    180   DCHECK(!new_buffers_cb.is_null());
    181   DCHECK(!need_key_cb.is_null());
    182   DCHECK(!end_of_segment_cb.is_null());
    183 
    184   init_cb_ = init_cb;
    185   config_cb_ = config_cb;
    186   new_buffers_cb_ = new_buffers_cb;
    187   need_key_cb_ = need_key_cb;
    188   new_segment_cb_ = new_segment_cb;
    189   end_of_segment_cb_ = end_of_segment_cb;
    190   log_cb_ = log_cb;
    191 }
    192 
    193 void Mp2tStreamParser::Flush() {
    194   DVLOG(1) << "Mp2tStreamParser::Flush";
    195 
    196   // Flush the buffers and reset the pids.
    197   for (std::map<int, PidState*>::iterator it = pids_.begin();
    198        it != pids_.end(); ++it) {
    199     DVLOG(1) << "Flushing PID: " << it->first;
    200     PidState* pid_state = it->second;
    201     pid_state->Flush();
    202     delete pid_state;
    203   }
    204   pids_.clear();
    205   EmitRemainingBuffers();
    206   buffer_queue_chain_.clear();
    207 
    208   // End of the segment.
    209   // Note: does not need to invoke |end_of_segment_cb_| since flushing the
    210   // stream parser already involves the end of the current segment.
    211   segment_started_ = false;
    212   first_video_frame_in_segment_ = true;
    213 
    214   // Remove any bytes left in the TS buffer.
    215   // (i.e. any partial TS packet => less than 188 bytes).
    216   ts_byte_queue_.Reset();
    217 
    218   // Reset the selected PIDs.
    219   selected_audio_pid_ = -1;
    220   selected_video_pid_ = -1;
    221 }
    222 
    223 bool Mp2tStreamParser::Parse(const uint8* buf, int size) {
    224   DVLOG(1) << "Mp2tStreamParser::Parse size=" << size;
    225 
    226   // Add the data to the parser state.
    227   ts_byte_queue_.Push(buf, size);
    228 
    229   while (true) {
    230     const uint8* ts_buffer;
    231     int ts_buffer_size;
    232     ts_byte_queue_.Peek(&ts_buffer, &ts_buffer_size);
    233     if (ts_buffer_size < TsPacket::kPacketSize)
    234       break;
    235 
    236     // Synchronization.
    237     int skipped_bytes = TsPacket::Sync(ts_buffer, ts_buffer_size);
    238     if (skipped_bytes > 0) {
    239       DVLOG(1) << "Packet not aligned on a TS syncword:"
    240                << " skipped_bytes=" << skipped_bytes;
    241       ts_byte_queue_.Pop(skipped_bytes);
    242       continue;
    243     }
    244 
    245     // Parse the TS header, skipping 1 byte if the header is invalid.
    246     scoped_ptr<TsPacket> ts_packet(TsPacket::Parse(ts_buffer, ts_buffer_size));
    247     if (!ts_packet) {
    248       DVLOG(1) << "Error: invalid TS packet";
    249       ts_byte_queue_.Pop(1);
    250       continue;
    251     }
    252     DVLOG(LOG_LEVEL_TS)
    253         << "Processing PID=" << ts_packet->pid()
    254         << " start_unit=" << ts_packet->payload_unit_start_indicator();
    255 
    256     // Parse the section.
    257     std::map<int, PidState*>::iterator it = pids_.find(ts_packet->pid());
    258     if (it == pids_.end() &&
    259         ts_packet->pid() == TsSection::kPidPat) {
    260       // Create the PAT state here if needed.
    261       scoped_ptr<TsSection> pat_section_parser(
    262           new TsSectionPat(
    263               base::Bind(&Mp2tStreamParser::RegisterPmt,
    264                          base::Unretained(this))));
    265       scoped_ptr<PidState> pat_pid_state(
    266           new PidState(ts_packet->pid(), PidState::kPidPat,
    267                        pat_section_parser.Pass()));
    268       pat_pid_state->Enable();
    269       it = pids_.insert(
    270           std::pair<int, PidState*>(ts_packet->pid(),
    271                                     pat_pid_state.release())).first;
    272     }
    273 
    274     if (it != pids_.end()) {
    275       if (!it->second->PushTsPacket(*ts_packet))
    276         return false;
    277     } else {
    278       DVLOG(LOG_LEVEL_TS) << "Ignoring TS packet for pid: " << ts_packet->pid();
    279     }
    280 
    281     // Go to the next packet.
    282     ts_byte_queue_.Pop(TsPacket::kPacketSize);
    283   }
    284 
    285   RCHECK(FinishInitializationIfNeeded());
    286 
    287   // Emit the A/V buffers that kept accumulating during TS parsing.
    288   return EmitRemainingBuffers();
    289 }
    290 
    291 void Mp2tStreamParser::RegisterPmt(int program_number, int pmt_pid) {
    292   DVLOG(1) << "RegisterPmt:"
    293            << " program_number=" << program_number
    294            << " pmt_pid=" << pmt_pid;
    295 
    296   // Only one TS program is allowed. Ignore the incoming program map table,
    297   // if there is already one registered.
    298   for (std::map<int, PidState*>::iterator it = pids_.begin();
    299        it != pids_.end(); ++it) {
    300     PidState* pid_state = it->second;
    301     if (pid_state->pid_type() == PidState::kPidPmt) {
    302       DVLOG_IF(1, pmt_pid != it->first) << "More than one program is defined";
    303       return;
    304     }
    305   }
    306 
    307   // Create the PMT state here if needed.
    308   DVLOG(1) << "Create a new PMT parser";
    309   scoped_ptr<TsSection> pmt_section_parser(
    310       new TsSectionPmt(
    311           base::Bind(&Mp2tStreamParser::RegisterPes,
    312                      base::Unretained(this), pmt_pid)));
    313   scoped_ptr<PidState> pmt_pid_state(
    314       new PidState(pmt_pid, PidState::kPidPmt, pmt_section_parser.Pass()));
    315   pmt_pid_state->Enable();
    316   pids_.insert(std::pair<int, PidState*>(pmt_pid, pmt_pid_state.release()));
    317 }
    318 
    319 void Mp2tStreamParser::RegisterPes(int pmt_pid,
    320                                    int pes_pid,
    321                                    int stream_type) {
    322   // TODO(damienv): check there is no mismatch if the entry already exists.
    323   DVLOG(1) << "RegisterPes:"
    324            << " pes_pid=" << pes_pid
    325            << " stream_type=" << std::hex << stream_type << std::dec;
    326   std::map<int, PidState*>::iterator it = pids_.find(pes_pid);
    327   if (it != pids_.end())
    328     return;
    329 
    330   // Create a stream parser corresponding to the stream type.
    331   bool is_audio = false;
    332   scoped_ptr<EsParser> es_parser;
    333   if (stream_type == kStreamTypeAVC) {
    334     es_parser.reset(
    335         new EsParserH264(
    336             base::Bind(&Mp2tStreamParser::OnVideoConfigChanged,
    337                        base::Unretained(this),
    338                        pes_pid),
    339             base::Bind(&Mp2tStreamParser::OnEmitVideoBuffer,
    340                        base::Unretained(this),
    341                        pes_pid)));
    342   } else if (stream_type == kStreamTypeAAC) {
    343     es_parser.reset(
    344         new EsParserAdts(
    345             base::Bind(&Mp2tStreamParser::OnAudioConfigChanged,
    346                        base::Unretained(this),
    347                        pes_pid),
    348             base::Bind(&Mp2tStreamParser::OnEmitAudioBuffer,
    349                        base::Unretained(this),
    350                        pes_pid),
    351             sbr_in_mimetype_));
    352     is_audio = true;
    353   } else {
    354     return;
    355   }
    356 
    357   // Create the PES state here.
    358   DVLOG(1) << "Create a new PES state";
    359   scoped_ptr<TsSection> pes_section_parser(
    360       new TsSectionPes(es_parser.Pass()));
    361   PidState::PidType pid_type =
    362       is_audio ? PidState::kPidAudioPes : PidState::kPidVideoPes;
    363   scoped_ptr<PidState> pes_pid_state(
    364       new PidState(pes_pid, pid_type, pes_section_parser.Pass()));
    365   pids_.insert(std::pair<int, PidState*>(pes_pid, pes_pid_state.release()));
    366 
    367   // A new PES pid has been added, the PID filter might change.
    368   UpdatePidFilter();
    369 }
    370 
    371 void Mp2tStreamParser::UpdatePidFilter() {
    372   // Applies the HLS rule to select the default audio/video PIDs:
    373   // select the audio/video streams with the lowest PID.
    374   // TODO(damienv): this can be changed when the StreamParser interface
    375   // supports multiple audio/video streams.
    376   PidMap::iterator lowest_audio_pid = pids_.end();
    377   PidMap::iterator lowest_video_pid = pids_.end();
    378   for (PidMap::iterator it = pids_.begin(); it != pids_.end(); ++it) {
    379     int pid = it->first;
    380     PidState* pid_state = it->second;
    381     if (pid_state->pid_type() == PidState::kPidAudioPes &&
    382         (lowest_audio_pid == pids_.end() || pid < lowest_audio_pid->first))
    383       lowest_audio_pid = it;
    384     if (pid_state->pid_type() == PidState::kPidVideoPes &&
    385         (lowest_video_pid == pids_.end() || pid < lowest_video_pid->first))
    386       lowest_video_pid = it;
    387   }
    388 
    389   // Enable both the lowest audio and video PIDs.
    390   if (lowest_audio_pid != pids_.end()) {
    391     DVLOG(1) << "Enable audio pid: " << lowest_audio_pid->first;
    392     lowest_audio_pid->second->Enable();
    393     selected_audio_pid_ = lowest_audio_pid->first;
    394   }
    395   if (lowest_video_pid != pids_.end()) {
    396     DVLOG(1) << "Enable video pid: " << lowest_video_pid->first;
    397     lowest_video_pid->second->Enable();
    398     selected_video_pid_ = lowest_video_pid->first;
    399   }
    400 
    401   // Disable all the other audio and video PIDs.
    402   for (PidMap::iterator it = pids_.begin(); it != pids_.end(); ++it) {
    403     PidState* pid_state = it->second;
    404     if (it != lowest_audio_pid && it != lowest_video_pid &&
    405         (pid_state->pid_type() == PidState::kPidAudioPes ||
    406          pid_state->pid_type() == PidState::kPidVideoPes))
    407       pid_state->Disable();
    408   }
    409 }
    410 
    411 void Mp2tStreamParser::OnVideoConfigChanged(
    412     int pes_pid,
    413     const VideoDecoderConfig& video_decoder_config) {
    414   DVLOG(1) << "OnVideoConfigChanged for pid=" << pes_pid;
    415   DCHECK_EQ(pes_pid, selected_video_pid_);
    416   DCHECK(video_decoder_config.IsValidConfig());
    417 
    418   // Create a new entry in |buffer_queue_chain_| with the updated configs.
    419   BufferQueueWithConfig buffer_queue_with_config(
    420       false,
    421       buffer_queue_chain_.empty()
    422       ? AudioDecoderConfig() : buffer_queue_chain_.back().audio_config,
    423       video_decoder_config);
    424   buffer_queue_chain_.push_back(buffer_queue_with_config);
    425 
    426   // Replace any non valid config with the 1st valid entry.
    427   // This might happen if there was no available config before.
    428   for (std::list<BufferQueueWithConfig>::iterator it =
    429        buffer_queue_chain_.begin(); it != buffer_queue_chain_.end(); ++it) {
    430     if (it->video_config.IsValidConfig())
    431       break;
    432     it->video_config = video_decoder_config;
    433   }
    434 }
    435 
    436 void Mp2tStreamParser::OnAudioConfigChanged(
    437     int pes_pid,
    438     const AudioDecoderConfig& audio_decoder_config) {
    439   DVLOG(1) << "OnAudioConfigChanged for pid=" << pes_pid;
    440   DCHECK_EQ(pes_pid, selected_audio_pid_);
    441   DCHECK(audio_decoder_config.IsValidConfig());
    442 
    443   // Create a new entry in |buffer_queue_chain_| with the updated configs.
    444   BufferQueueWithConfig buffer_queue_with_config(
    445       false,
    446       audio_decoder_config,
    447       buffer_queue_chain_.empty()
    448       ? VideoDecoderConfig() : buffer_queue_chain_.back().video_config);
    449   buffer_queue_chain_.push_back(buffer_queue_with_config);
    450 
    451   // Replace any non valid config with the 1st valid entry.
    452   // This might happen if there was no available config before.
    453   for (std::list<BufferQueueWithConfig>::iterator it =
    454        buffer_queue_chain_.begin(); it != buffer_queue_chain_.end(); ++it) {
    455     if (it->audio_config.IsValidConfig())
    456       break;
    457     it->audio_config = audio_decoder_config;
    458   }
    459 }
    460 
    461 bool Mp2tStreamParser::FinishInitializationIfNeeded() {
    462   // Nothing to be done if already initialized.
    463   if (is_initialized_)
    464     return true;
    465 
    466   // Wait for more data to come to finish initialization.
    467   if (buffer_queue_chain_.empty())
    468     return true;
    469 
    470   // Wait for more data to come if one of the config is not available.
    471   BufferQueueWithConfig& queue_with_config = buffer_queue_chain_.front();
    472   if (selected_audio_pid_ > 0 &&
    473       !queue_with_config.audio_config.IsValidConfig())
    474     return true;
    475   if (selected_video_pid_ > 0 &&
    476       !queue_with_config.video_config.IsValidConfig())
    477     return true;
    478 
    479   // Pass the config before invoking the initialization callback.
    480   RCHECK(config_cb_.Run(queue_with_config.audio_config,
    481                         queue_with_config.video_config,
    482                         TextTrackConfigMap()));
    483   queue_with_config.is_config_sent = true;
    484 
    485   // For Mpeg2 TS, the duration is not known.
    486   DVLOG(1) << "Mpeg2TS stream parser initialization done";
    487   init_cb_.Run(true, kInfiniteDuration());
    488   is_initialized_ = true;
    489 
    490   return true;
    491 }
    492 
    493 void Mp2tStreamParser::OnEmitAudioBuffer(
    494     int pes_pid,
    495     scoped_refptr<StreamParserBuffer> stream_parser_buffer) {
    496   DCHECK_EQ(pes_pid, selected_audio_pid_);
    497 
    498   DVLOG(LOG_LEVEL_ES)
    499       << "OnEmitAudioBuffer: "
    500       << " size="
    501       << stream_parser_buffer->data_size()
    502       << " dts="
    503       << stream_parser_buffer->GetDecodeTimestamp().InMilliseconds()
    504       << " pts="
    505       << stream_parser_buffer->timestamp().InMilliseconds();
    506   stream_parser_buffer->set_timestamp(
    507       stream_parser_buffer->timestamp() - time_offset_);
    508   stream_parser_buffer->SetDecodeTimestamp(
    509       stream_parser_buffer->GetDecodeTimestamp() - time_offset_);
    510 
    511   // Ignore the incoming buffer if it is not associated with any config.
    512   if (buffer_queue_chain_.empty()) {
    513     DVLOG(1) << "Ignoring audio buffer with no corresponding audio config";
    514     return;
    515   }
    516 
    517   buffer_queue_chain_.back().audio_queue.push_back(stream_parser_buffer);
    518 }
    519 
    520 void Mp2tStreamParser::OnEmitVideoBuffer(
    521     int pes_pid,
    522     scoped_refptr<StreamParserBuffer> stream_parser_buffer) {
    523   DCHECK_EQ(pes_pid, selected_video_pid_);
    524 
    525   DVLOG(LOG_LEVEL_ES)
    526       << "OnEmitVideoBuffer"
    527       << " size="
    528       << stream_parser_buffer->data_size()
    529       << " dts="
    530       << stream_parser_buffer->GetDecodeTimestamp().InMilliseconds()
    531       << " pts="
    532       << stream_parser_buffer->timestamp().InMilliseconds()
    533       << " IsKeyframe="
    534       << stream_parser_buffer->IsKeyframe();
    535   stream_parser_buffer->set_timestamp(
    536       stream_parser_buffer->timestamp() - time_offset_);
    537   stream_parser_buffer->SetDecodeTimestamp(
    538       stream_parser_buffer->GetDecodeTimestamp() - time_offset_);
    539 
    540   // Ignore the incoming buffer if it is not associated with any config.
    541   if (buffer_queue_chain_.empty()) {
    542     DVLOG(1) << "Ignoring video buffer with no corresponding video config:"
    543              << " keyframe=" << stream_parser_buffer->IsKeyframe()
    544              << " dts="
    545              << stream_parser_buffer->GetDecodeTimestamp().InMilliseconds();
    546     return;
    547   }
    548 
    549   // A segment cannot start with a non key frame.
    550   // Ignore the frame if that's the case.
    551   if (first_video_frame_in_segment_ && !stream_parser_buffer->IsKeyframe()) {
    552     DVLOG(1) << "Ignoring non-key frame:"
    553              << " dts="
    554              << stream_parser_buffer->GetDecodeTimestamp().InMilliseconds();
    555     return;
    556   }
    557 
    558   first_video_frame_in_segment_ = false;
    559   buffer_queue_chain_.back().video_queue.push_back(stream_parser_buffer);
    560 }
    561 
    562 bool Mp2tStreamParser::EmitRemainingBuffers() {
    563   DVLOG(LOG_LEVEL_ES) << "Mp2tStreamParser::EmitRemainingBuffers";
    564 
    565   // No buffer should be sent until fully initialized.
    566   if (!is_initialized_)
    567     return true;
    568 
    569   if (buffer_queue_chain_.empty())
    570     return true;
    571 
    572   // Keep track of the last audio and video config sent.
    573   AudioDecoderConfig last_audio_config =
    574       buffer_queue_chain_.back().audio_config;
    575   VideoDecoderConfig last_video_config =
    576       buffer_queue_chain_.back().video_config;
    577 
    578   // Buffer emission.
    579   while (!buffer_queue_chain_.empty()) {
    580     // Start a segment if needed.
    581     if (!segment_started_) {
    582       DVLOG(1) << "Starting a new segment";
    583       segment_started_ = true;
    584       new_segment_cb_.Run();
    585     }
    586 
    587     // Update the audio and video config if needed.
    588     BufferQueueWithConfig& queue_with_config = buffer_queue_chain_.front();
    589     if (!queue_with_config.is_config_sent) {
    590       if (!config_cb_.Run(queue_with_config.audio_config,
    591                           queue_with_config.video_config,
    592                           TextTrackConfigMap()))
    593         return false;
    594       queue_with_config.is_config_sent = true;
    595     }
    596 
    597     // Add buffers.
    598     if (!queue_with_config.audio_queue.empty() ||
    599         !queue_with_config.video_queue.empty()) {
    600       if (!new_buffers_cb_.Run(queue_with_config.audio_queue,
    601                                queue_with_config.video_queue)) {
    602         return false;
    603       }
    604     }
    605 
    606     buffer_queue_chain_.pop_front();
    607   }
    608 
    609   // Push an empty queue with the last audio/video config
    610   // so that buffers with the same config can be added later on.
    611   BufferQueueWithConfig queue_with_config(
    612       true, last_audio_config, last_video_config);
    613   buffer_queue_chain_.push_back(queue_with_config);
    614 
    615   return true;
    616 }
    617 
    618 }  // namespace mp2t
    619 }  // namespace media
    620 
    621