Chromium Code Reviews| 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 { | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: Please add spec reference for these magic num
damienv1
2013/09/04 01:37:13
Done.
| |
| 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 LOG(INFO) << "Mpeg2TsStreamParser::Mpeg2TsStreamParser"; | |
| 50 } | |
| 51 | |
| 52 Mpeg2TsStreamParser::~Mpeg2TsStreamParser() { | |
| 53 } | |
| 54 | |
| 55 void Mpeg2TsStreamParser::Init( | |
| 56 const InitCB& init_cb, | |
| 57 const NewConfigCB& config_cb, | |
| 58 const NewBuffersCB& new_buffers_cb, | |
| 59 const NewTextBuffersCB& text_cb, | |
| 60 const NeedKeyCB& need_key_cb, | |
| 61 const AddTextTrackCB& add_text_track_cb, | |
| 62 const NewMediaSegmentCB& new_segment_cb, | |
| 63 const base::Closure& end_of_segment_cb, | |
| 64 const LogCB& log_cb) { | |
| 65 DCHECK(init_cb_.is_null()); | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit:DCHECK(!is_initialized_)?
damienv1
2013/09/04 01:37:13
Done.
| |
| 66 DCHECK(!init_cb.is_null()); | |
| 67 DCHECK(!config_cb.is_null()); | |
| 68 DCHECK(!new_buffers_cb.is_null()); | |
| 69 DCHECK(!need_key_cb.is_null()); | |
| 70 DCHECK(!end_of_segment_cb.is_null()); | |
| 71 | |
| 72 init_cb_ = init_cb; | |
| 73 config_cb_ = config_cb; | |
| 74 new_buffers_cb_ = new_buffers_cb; | |
| 75 need_key_cb_ = need_key_cb; | |
| 76 new_segment_cb_ = new_segment_cb; | |
| 77 end_of_segment_cb_ = end_of_segment_cb; | |
| 78 log_cb_ = log_cb; | |
| 79 } | |
| 80 | |
| 81 void Mpeg2TsStreamParser::Flush() { | |
| 82 LOG(INFO) << "Mpeg2TsStreamParser::Flush()"; | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: Use DVLOG everywhere instead of LOG. DVLOG(1)
damienv1
2013/09/04 01:37:13
Done.
| |
| 83 | |
| 84 // Flush the buffers and reset the pids. | |
| 85 for (std::map<int, PidState*>::iterator it = pids_.begin(); | |
| 86 it != pids_.end(); ++it) { | |
| 87 LOG(INFO) << "Flushing PID: " << it->first; | |
| 88 PidState* pid_state = it->second; | |
| 89 pid_state->section_parser->Flush(); | |
| 90 delete pid_state; | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
If you are deleting the pid_state, why do you need
damienv1
2013/09/04 01:37:13
For video, there might be some pending buffers:
a
| |
| 91 } | |
| 92 pids_.clear(); | |
| 93 EmitRemainingBuffers(); | |
| 94 | |
| 95 // End of the segment. | |
| 96 end_of_segment_cb_.Run(); | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
You shouldn't need to make this call. Are you runn
damienv1
2013/09/04 01:37:13
I was thinking it was needed. Checked without, it
| |
| 97 segment_started_ = false; | |
| 98 | |
| 99 // Remove any bytes left in the TS buffer. | |
| 100 // (i.e. any partial TS packet => less than 188 bytes). | |
| 101 ts_buffer_.clear(); | |
| 102 | |
| 103 // Remove the programs and the corresponding PIDs. | |
| 104 programs_.clear(); | |
| 105 | |
| 106 // Reset the selected PIDs. | |
| 107 selected_pmt_pid_ = -1; | |
| 108 selected_audio_pid_ = -1; | |
| 109 selected_video_pid_ = -1; | |
| 110 | |
| 111 // Reset the audio and video configs. | |
| 112 audio_config_.reset(); | |
| 113 video_config_.reset(); | |
| 114 | |
| 115 // TODO(damienv): What to do with the remaining audio/video buffer queues ? | |
| 116 // Note: there should not be any. | |
| 117 LOG_IF(WARNING, !audio_buffer_queue_.empty()) | |
| 118 << "Flush: audio buffer queue not empty"; | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: Make these DCHECKS and place them right after
damienv1
2013/09/04 01:37:13
Done.
| |
| 119 LOG_IF(WARNING, !video_buffer_queue_.empty()) | |
| 120 << "Flush: video buffer queue not empty"; | |
| 121 audio_buffer_queue_.clear(); | |
| 122 video_buffer_queue_.clear(); | |
| 123 } | |
| 124 | |
| 125 bool Mpeg2TsStreamParser::Parse(const uint8* buf, int size) { | |
| 126 LOG(INFO) << "Mpeg2TsStreamParser::Parse size=" << size; | |
| 127 | |
| 128 // Add the data to the parser state. | |
| 129 int old_size = ts_buffer_.size(); | |
| 130 ts_buffer_.resize(old_size + size); | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
Convert ts_buffer_ to a media::ByteQueue so you do
damienv1
2013/09/04 01:37:13
Done.
| |
| 131 memcpy(&ts_buffer_[old_size], buf, size); | |
| 132 | |
| 133 int remaining_size = ts_buffer_.size(); | |
| 134 int pos = 0; | |
| 135 while (remaining_size >= TsPacket::kPacketSize) { | |
| 136 // Synchronization. | |
| 137 int skipped_bytes = TsPacket::Sync(&ts_buffer_[pos], remaining_size); | |
| 138 if (skipped_bytes > 0) { | |
| 139 LOG(WARNING) << "Packet not aligned on a TS syncword:" | |
| 140 << " skipped_bytes=" << skipped_bytes; | |
| 141 pos += skipped_bytes; | |
| 142 remaining_size -= skipped_bytes; | |
| 143 continue; | |
| 144 } | |
| 145 | |
| 146 // Parse the TS header. | |
| 147 scoped_ptr<TsPacket> ts_packet( | |
| 148 TsPacket::Parse(&ts_buffer_[pos], remaining_size)); | |
| 149 if (!ts_packet.get()) { | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: You shouldn't need the get().
damienv1
2013/09/04 01:37:13
Done.
| |
| 150 LOG(WARNING) << "Error: invalid TS packet"; | |
| 151 pos += 1; | |
| 152 remaining_size -= 1; | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: You should probably put an upper bound on inv
damienv1
2013/09/04 01:37:13
Not sure if this is the right behavior yet.
I woul
| |
| 153 continue; | |
| 154 } | |
| 155 | |
| 156 VLOG(LOG_LEVEL_TS) | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: Use DVLOG instead of VLOG here and everywhere
damienv1
2013/09/04 01:37:13
Done.
| |
| 157 << "Processing PID=" << ts_packet->pid() | |
| 158 << " start_unit=" << ts_packet->payload_unit_start_indicator(); | |
| 159 | |
| 160 // Parse the section. | |
| 161 std::map<int, PidState*>::iterator it = | |
| 162 pids_.find(ts_packet->pid()); | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: This should fit on the line above.
damienv1
2013/09/04 01:37:13
Done.
| |
| 163 if (it == pids_.end() && ts_packet->pid() == 0) { | |
| 164 // Create the PAT state here if needed. | |
| 165 LOG(INFO) << "Create a new PAT parser"; | |
| 166 PidState* pat_state = new PidState; | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: Use scoped_ptr<PidState> here and .release()
damienv1
2013/09/04 01:37:13
Done.
| |
| 167 pat_state->section_parser.reset( | |
| 168 new Mpeg2TsPatParser( | |
| 169 base::Bind(&Mpeg2TsStreamParser::RegisterPmt, | |
| 170 base::Unretained(this)))); | |
| 171 it = pids_.insert( | |
| 172 std::pair<int, PidState*>(ts_packet->pid(), pat_state)).first; | |
| 173 } | |
| 174 | |
| 175 LOG_IF(WARNING, it == pids_.end()) << "Ignoring TS packet for pid: " | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: Just place an unconditional log statement in
damienv1
2013/09/04 01:37:13
Done.
| |
| 176 << ts_packet->pid(); | |
| 177 if (it != pids_.end()) { | |
| 178 it->second->section_parser->Parse( | |
| 179 ts_packet->payload_unit_start_indicator(), | |
| 180 &ts_buffer_[pos+ts_packet->GetPayloadOffset()], | |
| 181 ts_packet->GetPayloadSize()); | |
| 182 } | |
| 183 | |
| 184 // Go to the next packet. | |
| 185 pos += TsPacket::kPacketSize; | |
| 186 remaining_size -= TsPacket::kPacketSize; | |
| 187 } | |
| 188 | |
| 189 // Check whether we are on a TS packet boundary. | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: This comment seems stale. It doesn't appear t
damienv1
2013/09/04 01:37:13
Done.
| |
| 190 if (remaining_size > 0) { | |
| 191 memmove(&ts_buffer_[0], &ts_buffer_[pos], remaining_size); | |
| 192 ts_buffer_.resize(remaining_size); | |
| 193 } else { | |
| 194 ts_buffer_.resize(0); | |
| 195 } | |
| 196 | |
| 197 EmitRemainingBuffers(); | |
| 198 | |
| 199 return true; | |
| 200 } | |
| 201 | |
| 202 void Mpeg2TsStreamParser::RegisterPmt(int program_number, int pmt_pid) { | |
| 203 LOG(INFO) << "RegisterPmt:" | |
| 204 << " program_number=" << program_number | |
| 205 << " pmt_pid=" << pmt_pid; | |
| 206 std::map<int, PidState*>::iterator it = pids_.find(pmt_pid); | |
| 207 if (it != pids_.end()) { | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: remove {} here and on other 1 line statements
damienv1
2013/09/04 01:37:13
Done.
| |
| 208 return; | |
| 209 } | |
| 210 | |
| 211 if (selected_pmt_pid_ < 0) { | |
| 212 selected_pmt_pid_ = pmt_pid; | |
| 213 } | |
| 214 LOG_IF(WARNING, selected_pmt_pid_ != pmt_pid) | |
| 215 << "More than one program is defined"; | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
If more than one program is present then an error
damienv1
2013/09/04 01:37:13
When there are multiple programs, the Mpeg2 TS str
acolwell GONE FROM CHROMIUM
2013/09/05 18:29:09
Multiple programs are not allowed by the spec. Sig
| |
| 216 | |
| 217 // Create the PMT state here if needed. | |
| 218 LOG(INFO) << "Create a new PMT parser"; | |
| 219 PidState* pmt_state = new PidState; | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: Use scoped_ptr<PidState> here and release() b
damienv1
2013/09/04 01:37:13
Done.
| |
| 220 pmt_state->section_parser.reset( | |
| 221 new Mpeg2TsPmtParser( | |
| 222 base::Bind(&Mpeg2TsStreamParser::RegisterPes, | |
| 223 base::Unretained(this), pmt_pid))); | |
| 224 pids_.insert(std::pair<int, PidState*>(pmt_pid, pmt_state)); | |
| 225 } | |
| 226 | |
| 227 void Mpeg2TsStreamParser::RegisterPes(int pmt_pid, | |
| 228 int pes_pid, | |
| 229 int stream_type) { | |
| 230 // TODO(damienv): check there is no mismatch if the entry already exists. | |
| 231 LOG(INFO) << "RegisterPes:" | |
| 232 << " pes_pid=" << pes_pid | |
| 233 << " stream_type=" << std::hex << stream_type << std::dec; | |
| 234 std::map<int, PidState*>::iterator it = pids_.find(pes_pid); | |
| 235 if (it != pids_.end()) { | |
| 236 return; | |
| 237 } | |
| 238 | |
| 239 // TODO(damienv): add other formats if needed. | |
| 240 bool is_audio = (stream_type == kStreamTypeAAC); | |
| 241 bool is_video = (stream_type == kStreamTypeAVC); | |
| 242 | |
| 243 // Update the active tracks. | |
| 244 // Select the audio/video tracks with the lowest PID. | |
| 245 if (pmt_pid == selected_pmt_pid_) { | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: reverse condition and early return. We only r
damienv1
2013/09/04 01:37:13
Done. I changed the behavior of the automatic PID
| |
| 246 if (is_audio && | |
| 247 (selected_audio_pid_ < 0 || pes_pid < selected_audio_pid_)) { | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
If the selected_xxx_pid_ changes downward, shouldn
damienv1
2013/09/04 01:37:13
I changed the behavior of the PidState.
The PidSta
| |
| 248 selected_audio_pid_ = pes_pid; | |
| 249 } | |
| 250 if (is_video && | |
| 251 (selected_video_pid_ < 0 || pes_pid < selected_video_pid_)) { | |
| 252 selected_video_pid_ = pes_pid; | |
| 253 } | |
| 254 } | |
| 255 | |
| 256 // Create a stream parser corresponding to the stream type. | |
| 257 scoped_ptr<EsParser> es_parser; | |
| 258 if (stream_type == kStreamTypeAVC) { | |
| 259 es_parser.reset( | |
| 260 new EsParserH264( | |
| 261 base::Bind(&Mpeg2TsStreamParser::OnVideoConfigChanged, | |
| 262 base::Unretained(this), | |
| 263 pes_pid), | |
| 264 base::Bind(&Mpeg2TsStreamParser::OnEmitVideoBuffer, | |
| 265 base::Unretained(this), | |
| 266 pes_pid))); | |
| 267 } else if (stream_type == kStreamTypeAAC) { | |
| 268 es_parser.reset( | |
| 269 new EsParserAdts( | |
| 270 base::Bind(&Mpeg2TsStreamParser::OnAudioConfigChanged, | |
| 271 base::Unretained(this), | |
| 272 pes_pid), | |
| 273 base::Bind(&Mpeg2TsStreamParser::OnEmitAudioBuffer, | |
| 274 base::Unretained(this), | |
| 275 pes_pid))); | |
| 276 } | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: else return. We don't need state for a strem_
damienv1
2013/09/04 01:37:13
Done.
| |
| 277 | |
| 278 // Create the PES state here. | |
| 279 LOG(INFO) << "Create a new PES state"; | |
| 280 PidState* pes_state = new PidState; | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
ditto. Perhaps an AddPidParser(pid, parser) helper
damienv1
2013/09/04 01:37:13
Code has been slightly refactored, and this helper
| |
| 281 pes_state->section_parser.reset( | |
| 282 new Mpeg2TsPesParser(es_parser.release())); | |
| 283 pids_.insert(std::pair<int, PidState*>(pes_pid, pes_state)); | |
| 284 } | |
| 285 | |
| 286 void Mpeg2TsStreamParser::OnVideoConfigChanged( | |
| 287 int pes_pid, | |
| 288 const VideoDecoderConfig& video_decoder_config) { | |
| 289 DCHECK_GT(selected_video_pid_, 0); | |
| 290 LOG(INFO) << "OnVideoConfigChanged for pid=" << pes_pid; | |
| 291 if (pes_pid != selected_video_pid_) { | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: It seems like this should be a DCHECK_NE(pes_
damienv1
2013/09/04 01:37:13
Done.
| |
| 292 return; | |
| 293 } | |
| 294 | |
| 295 video_config_.reset(new VideoDecoderConfig()); | |
| 296 *video_config_ = video_decoder_config; | |
| 297 | |
| 298 OnAudioVideoConfigChanged(); | |
| 299 } | |
| 300 | |
| 301 void Mpeg2TsStreamParser::OnAudioConfigChanged( | |
| 302 int pes_pid, | |
| 303 const AudioDecoderConfig& audio_decoder_config) { | |
| 304 LOG(INFO) << "OnAudioConfigChanged"; | |
| 305 if (pes_pid != selected_audio_pid_) { | |
| 306 return; | |
| 307 } | |
| 308 | |
| 309 audio_config_.reset(new AudioDecoderConfig()); | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: You shouldn't need a pointer here. You can ju
damienv1
2013/09/04 01:37:13
Done.
| |
| 310 *audio_config_ = audio_decoder_config; | |
| 311 | |
| 312 OnAudioVideoConfigChanged(); | |
| 313 } | |
| 314 | |
| 315 void Mpeg2TsStreamParser::OnAudioVideoConfigChanged() { | |
| 316 if (selected_audio_pid_ > 0 && !audio_config_) { | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
It seems like this should be DCHECK(!audio_config_
damienv1
2013/09/04 01:37:13
I slightly changed the design so this comment shou
| |
| 317 // Need to get the audio config as well before going any further. | |
| 318 return; | |
| 319 } | |
| 320 if (selected_video_pid_ > 0 && !video_config_) { | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
ditto for video.
damienv1
2013/09/04 01:37:13
Ditto
| |
| 321 // Need to get the video config as well before going any further. | |
| 322 return; | |
| 323 } | |
| 324 | |
| 325 // Emit pending buffers only if already initialized. | |
| 326 // Buffers need to be emitted since these buffers do not necesseraly | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: s/ necesseraly/necessarily/
damienv1
2013/09/04 01:37:13
Not relevant anymore.
| |
| 327 // have the same config. | |
| 328 if (is_initialized_) { | |
| 329 EmitRemainingBuffers(); | |
| 330 } | |
| 331 | |
| 332 if (selected_audio_pid_ > 0 && selected_video_pid_ > 0) { | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
Removing the pointer usage would remove the need f
damienv1
2013/09/04 01:37:13
Done.
| |
| 333 // Streams with both audio and video. | |
| 334 config_cb_.Run(*audio_config_, *video_config_); | |
| 335 } else if (selected_video_pid_ > 0) { | |
| 336 // Video stream only. | |
| 337 AudioDecoderConfig audio_config; | |
| 338 config_cb_.Run(audio_config, *video_config_); | |
| 339 } else if (selected_audio_pid_ > 0) { | |
| 340 // Audio stream only. | |
| 341 VideoDecoderConfig video_config; | |
| 342 config_cb_.Run(*audio_config_, video_config); | |
| 343 } else { | |
| 344 // At least one audio or video config should be set. | |
| 345 NOTREACHED(); | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
This should be a DCHECK(audio_config_.IsValidConfi
damienv1
2013/09/04 01:37:13
Removed.
| |
| 346 } | |
| 347 | |
| 348 if (!is_initialized_) { | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: reverse condition and return early.
damienv1
2013/09/04 01:37:13
I usually use "early return" for the exceptional f
acolwell GONE FROM CHROMIUM
2013/09/05 18:29:09
In this case, it isn't about exceptional flow. It
| |
| 349 // For Mpeg2 TS we don't know the stream duration. | |
| 350 LOG(INFO) << "Mpeg2TS stream parser initialization done"; | |
| 351 init_cb_.Run(true, base::TimeDelta()); | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: kInfiniteDuration() should be used here inste
damienv1
2013/09/04 01:37:13
Done.
| |
| 352 is_initialized_ = true; | |
| 353 } | |
| 354 } | |
| 355 | |
| 356 void Mpeg2TsStreamParser::OnEmitAudioBuffer( | |
| 357 int pes_pid, | |
| 358 scoped_refptr<StreamParserBuffer> stream_parser_buffer) { | |
| 359 // Since ChunkDemuxer currently handle only one audio and one video, | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: Do not reference ChunkDemuxer here. "Since St
damienv1
2013/09/04 01:37:13
Done.
| |
| 360 // the PID filter must be done inside the TS parser. | |
| 361 // Eventually, the PID filter will be done on the JS application side | |
| 362 // (using the HTML5 video element track selection). | |
| 363 if (pes_pid != selected_audio_pid_) { | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: It seems like this should be a DCHECK. We sho
damienv1
2013/09/04 01:37:13
Done.
| |
| 364 return; | |
| 365 } | |
| 366 | |
| 367 VLOG(LOG_LEVEL_ES) | |
| 368 << "OnEmitAudioBuffer: " | |
| 369 << " size=" | |
| 370 << stream_parser_buffer->data_size() | |
| 371 << " dts=" | |
| 372 << stream_parser_buffer->GetDecodeTimestamp().InMilliseconds() | |
| 373 << " pts=" | |
| 374 << stream_parser_buffer->timestamp().InMilliseconds(); | |
| 375 stream_parser_buffer->set_timestamp( | |
| 376 stream_parser_buffer->timestamp() - time_offset_); | |
| 377 stream_parser_buffer->SetDecodeTimestamp( | |
| 378 stream_parser_buffer->GetDecodeTimestamp() - time_offset_); | |
| 379 audio_buffer_queue_.push_back(stream_parser_buffer); | |
| 380 } | |
| 381 | |
| 382 void Mpeg2TsStreamParser::OnEmitVideoBuffer( | |
| 383 int pes_pid, | |
| 384 scoped_refptr<StreamParserBuffer> stream_parser_buffer) { | |
| 385 // Since ChunkDemuxer currently handle only one audio and one video, | |
| 386 // the PID filter must be done inside the TS parser. | |
| 387 // Eventually, the PID filter will be done on the JS application side | |
| 388 // (using the HTML5 video element track selection). | |
| 389 if (pes_pid != selected_video_pid_) { | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
ditto
damienv1
2013/09/04 01:37:13
Done.
| |
| 390 return; | |
| 391 } | |
| 392 | |
| 393 VLOG(LOG_LEVEL_ES) | |
| 394 << "OnEmitVideoBuffer" | |
| 395 << " size=" | |
| 396 << stream_parser_buffer->data_size() | |
| 397 << " dts=" | |
| 398 << stream_parser_buffer->GetDecodeTimestamp().InMilliseconds() | |
| 399 << " pts=" | |
| 400 << stream_parser_buffer->timestamp().InMilliseconds() | |
| 401 << " IsKeyframe=" | |
| 402 << stream_parser_buffer->IsKeyframe(); | |
| 403 stream_parser_buffer->set_timestamp( | |
| 404 stream_parser_buffer->timestamp() - time_offset_); | |
| 405 stream_parser_buffer->SetDecodeTimestamp( | |
| 406 stream_parser_buffer->GetDecodeTimestamp() - time_offset_); | |
| 407 video_buffer_queue_.push_back(stream_parser_buffer); | |
| 408 } | |
| 409 | |
| 410 void Mpeg2TsStreamParser::EmitRemainingBuffers() { | |
| 411 LOG(INFO) << "Mpeg2TsStreamParser::EmitRemainingBuffers"; | |
| 412 // Segment cannot start with non key frames | |
| 413 // so replace non key frames with the 1st IDR. | |
| 414 // TODO(damienv): Non key frames could be replaced with a stuffing NAL | |
| 415 // as well, the most important is to avoid any gap between frames | |
| 416 // as MSE does not allow sparse video/audio buffers. | |
| 417 if (!segment_started_) { | |
| 418 StreamParser::BufferQueue::iterator it = video_buffer_queue_.begin(); | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
I don't think this is the right solution. The pars
damienv1
2013/09/04 01:37:13
I removed this section but I don't really see how
| |
| 419 for ( ; it != video_buffer_queue_.end(); ++it) { | |
| 420 if ((*it)->IsKeyframe()) { | |
| 421 break; | |
| 422 } | |
| 423 } | |
| 424 if (it != video_buffer_queue_.end()) { | |
| 425 StreamParser::BufferQueue::iterator it2 = video_buffer_queue_.begin(); | |
| 426 while (!(*it2)->IsKeyframe()) { | |
| 427 base::TimeDelta dts = (*it2)->GetDecodeTimestamp(); | |
| 428 base::TimeDelta pts = (*it2)->timestamp(); | |
| 429 scoped_refptr<StreamParserBuffer> stream_parser_buffer = | |
| 430 StreamParserBuffer::CopyFrom( | |
| 431 (*it)->data(), (*it)->data_size(), true); | |
| 432 stream_parser_buffer->set_timestamp(pts); | |
| 433 stream_parser_buffer->SetDecodeTimestamp(dts); | |
| 434 LOG(WARNING) << "Replacing frame with an IDR @ pts=" | |
| 435 << pts.InMilliseconds(); | |
| 436 *it2 = stream_parser_buffer; | |
| 437 ++it2; | |
| 438 } | |
| 439 } else { | |
| 440 LOG(WARNING) << "Only non key frames in the buffer queue"; | |
| 441 // video_buffer_queue_.clear(); | |
| 442 } | |
| 443 } | |
| 444 | |
| 445 // Possibly start a segment if not done yet. | |
| 446 StartSegmentIfNeeded(); | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: Just move this into the block below and inlin
damienv1
2013/09/04 01:37:13
Done.
| |
| 447 | |
| 448 // Finally, add the video and audio buffers. | |
| 449 if (!video_buffer_queue_.empty() || | |
| 450 !audio_buffer_queue_.empty()) { | |
|
acolwell GONE FROM CHROMIUM
2013/08/29 20:44:24
nit: reverse condition and return early.
damienv1
2013/09/04 01:37:13
Done.
| |
| 451 new_buffers_cb_.Run(audio_buffer_queue_, video_buffer_queue_); | |
| 452 audio_buffer_queue_.clear(); | |
| 453 video_buffer_queue_.clear(); | |
| 454 } | |
| 455 } | |
| 456 | |
| 457 void Mpeg2TsStreamParser::StartSegmentIfNeeded() { | |
| 458 if (segment_started_) { | |
| 459 return; | |
| 460 } | |
| 461 if (video_buffer_queue_.empty() && audio_buffer_queue_.empty()) { | |
| 462 LOG(WARNING) << "Start a new segment but no buffer is available"; | |
| 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 |