OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 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/mpeg2/mpeg2ts_stream_parser.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/memory/scoped_ptr.h" | |
9 #include "media/base/audio_decoder_config.h" | |
10 #include "media/base/stream_parser_buffer.h" | |
11 #include "media/base/video_decoder_config.h" | |
12 #include "media/mpeg2/es_parser.h" | |
13 #include "media/mpeg2/es_parser_adts.h" | |
14 #include "media/mpeg2/es_parser_h264.h" | |
15 #include "media/mpeg2/mpeg2ts_common.h" | |
16 #include "media/mpeg2/mpeg2ts_pat.h" | |
17 #include "media/mpeg2/mpeg2ts_pes.h" | |
18 #include "media/mpeg2/mpeg2ts_pmt.h" | |
19 #include "media/mpeg2/mpeg2ts_section_parser.h" | |
20 #include "media/mpeg2/ts_packet.h" | |
21 | |
22 namespace { | |
23 | |
24 enum StreamType { | |
25 kStreamTypeMpeg1Audio = 0x3, | |
26 kStreamTypeAAC = 0xf, | |
27 kStreamTypeAVC = 0x1b, | |
28 }; | |
29 | |
30 } | |
31 | |
32 namespace media { | |
33 namespace mpeg2ts { | |
34 | |
35 class PidState { | |
36 public: | |
37 int pid; | |
38 int continuity_counter; | |
39 scoped_ptr<Mpeg2TsSectionParser> section_parser; | |
40 }; | |
41 | |
42 Mpeg2TsStreamParser::Mpeg2TsStreamParser() | |
43 : pids_deleter_(&pids_), | |
44 selected_pmt_pid_(-1), | |
45 selected_audio_pid_(-1), | |
46 selected_video_pid_(-1), | |
47 is_initialized_(false), | |
48 segment_started_(false) { | |
49 } | |
50 | |
51 Mpeg2TsStreamParser::~Mpeg2TsStreamParser() { | |
52 } | |
53 | |
54 void Mpeg2TsStreamParser::Init( | |
55 const InitCB& init_cb, | |
56 const NewConfigCB& config_cb, | |
57 const NewBuffersCB& new_buffers_cb, | |
58 const NewTextBuffersCB& text_cb, | |
59 const NeedKeyCB& need_key_cb, | |
60 const AddTextTrackCB& add_text_track_cb, | |
61 const NewMediaSegmentCB& new_segment_cb, | |
62 const base::Closure& end_of_segment_cb, | |
63 const LogCB& log_cb) { | |
64 DCHECK(init_cb_.is_null()); | |
65 DCHECK(!init_cb.is_null()); | |
66 DCHECK(!config_cb.is_null()); | |
67 DCHECK(!new_buffers_cb.is_null()); | |
68 DCHECK(!need_key_cb.is_null()); | |
69 DCHECK(!end_of_segment_cb.is_null()); | |
70 | |
71 init_cb_ = init_cb; | |
72 config_cb_ = config_cb; | |
73 new_buffers_cb_ = new_buffers_cb; | |
74 need_key_cb_ = need_key_cb; | |
75 new_segment_cb_ = new_segment_cb; | |
76 end_of_segment_cb_ = end_of_segment_cb; | |
77 log_cb_ = log_cb; | |
78 } | |
79 | |
80 void Mpeg2TsStreamParser::Flush() { | |
81 // Flush the buffers and reset the pids. | |
82 for (std::map<int, PidState*>::iterator it = pids_.begin(); | |
83 it != pids_.end(); ++it) { | |
84 LOG(INFO) << "Flushing PID: " << it->first; | |
85 PidState* pid_state = it->second; | |
86 pid_state->section_parser->Flush(); | |
87 delete pid_state; | |
88 } | |
89 pids_.clear(); | |
90 EmitRemainingBuffers(); | |
91 | |
92 // End of the segment. | |
93 end_of_segment_cb_.Run(); | |
94 segment_started_ = false; | |
95 | |
96 // Remove any bytes left in the TS buffer. | |
97 // (i.e. any partial TS packet => less than 188 bytes). | |
98 ts_buffer_.clear(); | |
99 | |
100 // Remove the programs and the corresponding PIDs. | |
101 programs_.clear(); | |
102 | |
103 // Reset the selected PIDs. | |
104 selected_pmt_pid_ = -1; | |
105 selected_audio_pid_ = -1; | |
106 selected_video_pid_ = -1; | |
107 | |
108 // Reset the audio and video configs. | |
109 audio_config_.reset(); | |
110 video_config_.reset(); | |
111 | |
112 // TODO(damienv): What to do with the remaining audio/video buffer queues ? | |
113 // Note: there should not be any. | |
114 LOG_IF(WARNING, !audio_buffer_queue_.empty()) | |
115 << "Flush: audio buffer queue not empty"; | |
116 LOG_IF(WARNING, !video_buffer_queue_.empty()) | |
117 << "Flush: video buffer queue not empty"; | |
118 audio_buffer_queue_.clear(); | |
119 video_buffer_queue_.clear(); | |
120 } | |
121 | |
122 bool Mpeg2TsStreamParser::Parse(const uint8* buf, int size) { | |
123 LOG(INFO) << "Mpeg2TsStreamParser::Parse size=" << size; | |
124 | |
125 // Add the data to the parser state. | |
126 int old_size = ts_buffer_.size(); | |
127 ts_buffer_.resize(old_size + size); | |
128 memcpy(&ts_buffer_[old_size], buf, size); | |
129 | |
130 int remaining_size = ts_buffer_.size(); | |
131 int pos = 0; | |
132 while (remaining_size >= TsPacket::kPacketSize) { | |
133 // Synchronization. | |
134 int skipped_bytes = TsPacket::Sync(&ts_buffer_[pos], remaining_size); | |
135 if (skipped_bytes > 0) { | |
136 LOG(WARNING) << "Packet not aligned on a TS syncword:" | |
137 << " skipped_bytes=" << skipped_bytes; | |
138 pos += skipped_bytes; | |
139 remaining_size -= skipped_bytes; | |
140 continue; | |
141 } | |
142 | |
143 // Parse the TS header. | |
144 scoped_ptr<TsPacket> ts_packet( | |
145 TsPacket::Parse(&ts_buffer_[pos], remaining_size)); | |
146 if (!ts_packet.get()) { | |
147 LOG(WARNING) << "Error: invalid TS packet"; | |
148 pos += 1; | |
149 remaining_size -= 1; | |
150 continue; | |
151 } | |
152 | |
153 VLOG(LOG_LEVEL_TS) | |
154 << "Processing PID=" << ts_packet->pid() | |
155 << " start_unit=" << ts_packet->payload_unit_start_indicator(); | |
156 | |
157 // Parse the section. | |
158 std::map<int, PidState*>::iterator it = | |
159 pids_.find(ts_packet->pid()); | |
160 if (it == pids_.end() && ts_packet->pid() == 0) { | |
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: Use Mpeg2TsSectionParser::kPidPat here. You m
damienv1
2013/09/04 01:37:14
Done.
| |
161 // Create the PAT state here if needed. | |
162 LOG(INFO) << "Create a new PAT parser"; | |
163 PidState* pat_state = new PidState; | |
164 pat_state->section_parser.reset( | |
165 new Mpeg2TsPatParser( | |
166 base::Bind(&Mpeg2TsStreamParser::RegisterPmt, | |
167 base::Unretained(this)))); | |
168 it = pids_.insert( | |
169 std::pair<int, PidState*>(ts_packet->pid(), pat_state)).first; | |
170 } | |
171 | |
172 LOG_IF(WARNING, it == pids_.end()) << "Ignoring TS packet for pid: " | |
173 << ts_packet->pid(); | |
174 if (it != pids_.end()) { | |
175 it->second->section_parser->Parse( | |
176 ts_packet->payload_unit_start_indicator(), | |
177 &ts_buffer_[pos+ts_packet->GetPayloadOffset()], | |
178 ts_packet->GetPayloadSize()); | |
179 } | |
180 | |
181 // Go to the next packet. | |
182 pos += TsPacket::kPacketSize; | |
183 remaining_size -= TsPacket::kPacketSize; | |
184 } | |
185 | |
186 // Check whether we are on a TS packet boundary. | |
187 if (remaining_size > 0) { | |
188 memmove(&ts_buffer_[0], &ts_buffer_[pos], remaining_size); | |
189 ts_buffer_.resize(remaining_size); | |
190 } else { | |
191 ts_buffer_.resize(0); | |
192 } | |
193 | |
194 EmitRemainingBuffers(); | |
195 | |
196 return true; | |
197 } | |
198 | |
199 void Mpeg2TsStreamParser::RegisterPmt(int program_number, int pmt_pid) { | |
200 LOG(INFO) << "RegisterPmt:" | |
201 << " program_number=" << program_number | |
202 << " pmt_pid=" << pmt_pid; | |
203 std::map<int, PidState*>::iterator it = pids_.find(pmt_pid); | |
204 if (it != pids_.end()) { | |
205 return; | |
206 } | |
207 | |
208 if (selected_pmt_pid_ < 0) { | |
209 selected_pmt_pid_ = pmt_pid; | |
210 } | |
211 LOG_IF(WARNING, selected_pmt_pid_ != pmt_pid) | |
212 << "More than one program is defined"; | |
213 | |
214 // Create the PMT state here if needed. | |
215 LOG(INFO) << "Create a new PMT parser"; | |
216 PidState* pmt_state = new PidState; | |
217 pmt_state->section_parser.reset( | |
218 new Mpeg2TsPmtParser( | |
219 base::Bind(&Mpeg2TsStreamParser::RegisterPes, | |
220 base::Unretained(this), pmt_pid))); | |
221 pids_.insert(std::pair<int, PidState*>(pmt_pid, pmt_state)); | |
222 } | |
223 | |
224 void Mpeg2TsStreamParser::RegisterPes(int pmt_pid, | |
225 int pes_pid, | |
226 int stream_type) { | |
227 // TODO(damienv): check there is no mismatch if the entry already exists. | |
228 LOG(INFO) << "RegisterPes:" | |
229 << " pes_pid=" << pes_pid | |
230 << " stream_type=" << std::hex << stream_type << std::dec; | |
231 std::map<int, PidState*>::iterator it = pids_.find(pes_pid); | |
232 if (it != pids_.end()) { | |
233 return; | |
234 } | |
235 | |
236 // TODO(damienv): add other formats if needed. | |
237 bool is_audio = (stream_type == kStreamTypeAAC); | |
238 bool is_video = (stream_type == kStreamTypeAVC); | |
239 | |
240 // Update the active tracks. | |
241 // Select the audio/video tracks with the lowest PID. | |
242 if (pmt_pid == selected_pmt_pid_) { | |
243 if (is_audio && | |
244 (selected_audio_pid_ < 0 || pes_pid < selected_audio_pid_)) { | |
245 selected_audio_pid_ = pes_pid; | |
246 } | |
247 if (is_video && | |
248 (selected_video_pid_ < 0 || pes_pid < selected_video_pid_)) { | |
249 selected_video_pid_ = pes_pid; | |
250 } | |
251 } | |
252 | |
253 // Create a stream parser corresponding to the stream type. | |
254 scoped_ptr<EsParser> es_parser; | |
255 if (stream_type == kStreamTypeAVC) { | |
256 es_parser.reset( | |
257 new EsParserH264( | |
258 base::Bind(&Mpeg2TsStreamParser::OnVideoConfigChanged, | |
259 base::Unretained(this), | |
260 pes_pid), | |
261 base::Bind(&Mpeg2TsStreamParser::OnEmitVideoBuffer, | |
262 base::Unretained(this), | |
263 pes_pid))); | |
264 } else if (stream_type == kStreamTypeAAC) { | |
265 es_parser.reset( | |
266 new EsParserAdts( | |
267 base::Bind(&Mpeg2TsStreamParser::OnAudioConfigChanged, | |
268 base::Unretained(this), | |
269 pes_pid), | |
270 base::Bind(&Mpeg2TsStreamParser::OnEmitAudioBuffer, | |
271 base::Unretained(this), | |
272 pes_pid))); | |
273 } | |
274 | |
275 // Create the PES state here. | |
276 LOG(INFO) << "Create a new PES state"; | |
277 PidState* pes_state = new PidState; | |
278 pes_state->section_parser.reset( | |
279 new Mpeg2TsPesParser(es_parser.release())); | |
280 pids_.insert(std::pair<int, PidState*>(pes_pid, pes_state)); | |
281 } | |
282 | |
283 void Mpeg2TsStreamParser::OnVideoConfigChanged( | |
284 int pes_pid, | |
285 const VideoDecoderConfig& video_decoder_config) { | |
286 DCHECK_GT(selected_video_pid_, 0); | |
287 LOG(INFO) << "OnVideoConfigChanged for pid=" << pes_pid; | |
288 if (pes_pid != selected_video_pid_) { | |
289 return; | |
290 } | |
291 | |
292 video_config_.reset(new VideoDecoderConfig()); | |
293 *video_config_ = video_decoder_config; | |
294 | |
295 OnAudioVideoConfigChanged(); | |
296 } | |
297 | |
298 void Mpeg2TsStreamParser::OnAudioConfigChanged( | |
299 int pes_pid, | |
300 const AudioDecoderConfig& audio_decoder_config) { | |
301 LOG(INFO) << "OnAudioConfigChanged"; | |
302 if (pes_pid != selected_audio_pid_) { | |
303 return; | |
304 } | |
305 | |
306 audio_config_.reset(new AudioDecoderConfig()); | |
307 *audio_config_ = audio_decoder_config; | |
308 | |
309 OnAudioVideoConfigChanged(); | |
310 } | |
311 | |
312 void Mpeg2TsStreamParser::OnAudioVideoConfigChanged() { | |
313 if (selected_audio_pid_ > 0 && !audio_config_) { | |
314 // Need to get the audio config as well before going any further. | |
315 return; | |
316 } | |
317 if (selected_video_pid_ > 0 && !video_config_) { | |
318 // Need to get the video config as well before going any further. | |
319 return; | |
320 } | |
321 | |
322 // Emit pending buffers only if already initialized. | |
323 // Buffers need to be emitted since these buffers do not necesseraly | |
324 // have the same config. | |
325 if (is_initialized_) { | |
326 // TODO(damienv): at initialization time, although unlikely, | |
327 // some situations like: | |
328 // VConfig0 VBuffer0 VBuffer1 VConfig1 VBuffer0 AConfig0 ABuffer0 | |
329 // cannot be handled properly since video buffers will be assigned the same | |
330 // video config. | |
331 EmitRemainingBuffers(); | |
332 } | |
333 | |
334 if (selected_audio_pid_ > 0 && selected_video_pid_ > 0) { | |
335 // Streams with both audio and video. | |
336 config_cb_.Run(*audio_config_, *video_config_); | |
337 } else if (selected_video_pid_ > 0) { | |
338 // Video stream only. | |
339 AudioDecoderConfig audio_config; | |
340 config_cb_.Run(audio_config, *video_config_); | |
341 } else if (selected_audio_pid_ > 0) { | |
342 // Audio stream only. | |
343 VideoDecoderConfig video_config; | |
344 config_cb_.Run(*audio_config_, video_config); | |
345 } else { | |
346 // At least one audio or video config should be set. | |
347 NOTREACHED(); | |
348 } | |
349 | |
350 if (!is_initialized_) { | |
351 // For Mpeg2 TS we don't know the stream duration. | |
352 LOG(INFO) << "Mpeg2TS stream parser initialization done"; | |
353 init_cb_.Run(true, base::TimeDelta()); | |
354 is_initialized_ = true; | |
355 } | |
356 } | |
357 | |
358 void Mpeg2TsStreamParser::OnEmitAudioBuffer( | |
359 int pes_pid, | |
360 scoped_refptr<StreamParserBuffer> stream_parser_buffer) { | |
361 // Since ChunkDemuxer currently handle only one audio and one video, | |
362 // the PID filter must be done inside the TS parser. | |
363 // Eventually, the PID filter will be done on the JS application side | |
364 // (using the HTML5 video element track selection). | |
365 if (pes_pid != selected_audio_pid_) { | |
366 return; | |
367 } | |
368 | |
369 VLOG(LOG_LEVEL_ES) | |
370 << "OnEmitAudioBuffer: " | |
371 << " size=" | |
372 << stream_parser_buffer->data_size() | |
373 << " dts=" | |
374 << stream_parser_buffer->GetDecodeTimestamp().InMilliseconds() | |
375 << " pts=" | |
376 << stream_parser_buffer->timestamp().InMilliseconds(); | |
377 stream_parser_buffer->set_timestamp( | |
378 stream_parser_buffer->timestamp() - time_offset_); | |
379 stream_parser_buffer->SetDecodeTimestamp( | |
380 stream_parser_buffer->GetDecodeTimestamp() - time_offset_); | |
381 audio_buffer_queue_.push_back(stream_parser_buffer); | |
382 } | |
383 | |
384 void Mpeg2TsStreamParser::OnEmitVideoBuffer( | |
385 int pes_pid, | |
386 scoped_refptr<StreamParserBuffer> stream_parser_buffer) { | |
387 // Since ChunkDemuxer currently handle only one audio and one video, | |
388 // the PID filter must be done inside the TS parser. | |
389 // Eventually, the PID filter will be done on the JS application side | |
390 // (using the HTML5 video element track selection). | |
391 if (pes_pid != selected_video_pid_) { | |
392 return; | |
393 } | |
394 | |
395 VLOG(LOG_LEVEL_ES) | |
396 << "OnEmitVideoBuffer" | |
397 << " size=" | |
398 << stream_parser_buffer->data_size() | |
399 << " dts=" | |
400 << stream_parser_buffer->GetDecodeTimestamp().InMilliseconds() | |
401 << " pts=" | |
402 << stream_parser_buffer->timestamp().InMilliseconds() | |
403 << " IsKeyframe=" | |
404 << stream_parser_buffer->IsKeyframe(); | |
405 stream_parser_buffer->set_timestamp( | |
406 stream_parser_buffer->timestamp() - time_offset_); | |
407 stream_parser_buffer->SetDecodeTimestamp( | |
408 stream_parser_buffer->GetDecodeTimestamp() - time_offset_); | |
409 video_buffer_queue_.push_back(stream_parser_buffer); | |
410 } | |
411 | |
412 void Mpeg2TsStreamParser::EmitRemainingBuffers() { | |
413 VLOG(LOG_LEVEL_ES) << "Mpeg2TsStreamParser::EmitRemainingBuffers"; | |
414 | |
415 // Segment cannot start with non key frames | |
416 // so replace non key frames with the 1st IDR. | |
417 // TODO(damienv): Non key frames could be replaced with a stuffing NAL | |
418 // as well, the most important is to avoid any gap between frames | |
419 // as MSE does not allow sparse video/audio buffers. | |
420 if (!segment_started_) { | |
421 StreamParser::BufferQueue::iterator it = video_buffer_queue_.begin(); | |
422 for ( ; it != video_buffer_queue_.end(); ++it) { | |
423 if ((*it)->IsKeyframe()) { | |
424 break; | |
425 } | |
426 } | |
427 if (it != video_buffer_queue_.end()) { | |
428 StreamParser::BufferQueue::iterator it2 = video_buffer_queue_.begin(); | |
429 while (!(*it2)->IsKeyframe()) { | |
430 base::TimeDelta dts = (*it2)->GetDecodeTimestamp(); | |
431 base::TimeDelta pts = (*it2)->timestamp(); | |
432 scoped_refptr<StreamParserBuffer> stream_parser_buffer = | |
433 StreamParserBuffer::CopyFrom( | |
434 (*it)->data(), (*it)->data_size(), true); | |
435 stream_parser_buffer->set_timestamp(pts); | |
436 stream_parser_buffer->SetDecodeTimestamp(dts); | |
437 LOG(WARNING) << "Replacing frame with an IDR @ pts=" | |
438 << pts.InMilliseconds(); | |
439 *it2 = stream_parser_buffer; | |
440 ++it2; | |
441 } | |
442 } else { | |
443 // TODO(damienv): is there a better way to handle this situation ? | |
444 LOG(WARNING) << "Only non key frames in the buffer queue"; | |
445 video_buffer_queue_.clear(); | |
446 } | |
447 } | |
448 | |
449 // Possibly start a segment if not done yet. | |
450 StartSegmentIfNeeded(); | |
451 | |
452 // Finally, add the video and audio buffers. | |
453 if (!video_buffer_queue_.empty() || | |
454 !audio_buffer_queue_.empty()) { | |
455 new_buffers_cb_.Run(audio_buffer_queue_, video_buffer_queue_); | |
456 audio_buffer_queue_.clear(); | |
457 video_buffer_queue_.clear(); | |
458 } | |
459 } | |
460 | |
461 void Mpeg2TsStreamParser::StartSegmentIfNeeded() { | |
462 if (segment_started_) { | |
463 return; | |
464 } | |
465 LOG(INFO) << "Starting a new segment"; | |
466 segment_started_ = true; | |
467 new_segment_cb_.Run(); | |
468 } | |
469 | |
470 } // namespace mpeg2ts | |
471 } // namespace media | |
OLD | NEW |