• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
pid_type() const62   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 
PidState(int pid,PidType pid_type,scoped_ptr<TsSection> section_parser)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 
PushTsPacket(const TsPacket & ts_packet)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 
Flush()116 void PidState::Flush() {
117   section_parser_->Flush();
118   ResetState();
119 }
120 
Enable()121 void PidState::Enable() {
122   enable_ = true;
123 }
124 
Disable()125 void PidState::Disable() {
126   if (!enable_)
127     return;
128 
129   ResetState();
130   enable_ = false;
131 }
132 
IsEnabled() const133 bool PidState::IsEnabled() const {
134   return enable_;
135 }
136 
ResetState()137 void PidState::ResetState() {
138   section_parser_->Reset();
139   continuity_counter_ = -1;
140 }
141 
BufferQueueWithConfig(bool is_cfg_sent,const AudioDecoderConfig & audio_cfg,const VideoDecoderConfig & video_cfg)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 
~BufferQueueWithConfig()151 Mp2tStreamParser::BufferQueueWithConfig::~BufferQueueWithConfig() {
152 }
153 
Mp2tStreamParser(bool sbr_in_mimetype)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 
~Mp2tStreamParser()163 Mp2tStreamParser::~Mp2tStreamParser() {
164   STLDeleteValues(&pids_);
165 }
166 
Init(const InitCB & init_cb,const NewConfigCB & config_cb,const NewBuffersCB & new_buffers_cb,const NewTextBuffersCB &,const NeedKeyCB & need_key_cb,const NewMediaSegmentCB & new_segment_cb,const base::Closure & end_of_segment_cb,const LogCB & log_cb)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 
Flush()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 
Parse(const uint8 * buf,int size)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 
RegisterPmt(int program_number,int pmt_pid)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 
RegisterPes(int pmt_pid,int pes_pid,int stream_type)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 
UpdatePidFilter()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 
OnVideoConfigChanged(int pes_pid,const VideoDecoderConfig & video_decoder_config)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 
OnAudioConfigChanged(int pes_pid,const AudioDecoderConfig & audio_decoder_config)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 
FinishInitializationIfNeeded()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 
OnEmitAudioBuffer(int pes_pid,scoped_refptr<StreamParserBuffer> stream_parser_buffer)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 
OnEmitVideoBuffer(int pes_pid,scoped_refptr<StreamParserBuffer> stream_parser_buffer)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 
EmitRemainingBuffers()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