| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 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/filters/media_source_state.h" | |
| 6 | |
| 7 #include <set> | |
| 8 | |
| 9 #include "base/callback_helpers.h" | |
| 10 #include "base/command_line.h" | |
| 11 #include "base/strings/string_number_conversions.h" | |
| 12 #include "media/base/media_switches.h" | |
| 13 #include "media/base/media_track.h" | |
| 14 #include "media/base/media_tracks.h" | |
| 15 #include "media/base/mime_util.h" | |
| 16 #include "media/filters/chunk_demuxer.h" | |
| 17 #include "media/filters/frame_processor.h" | |
| 18 #include "media/filters/source_buffer_stream.h" | |
| 19 | |
| 20 namespace media { | |
| 21 | |
| 22 enum { | |
| 23 // Limits the number of MEDIA_LOG() calls warning the user that a muxed stream | |
| 24 // media segment is missing a block from at least one of the audio or video | |
| 25 // tracks. | |
| 26 kMaxMissingTrackInSegmentLogs = 10, | |
| 27 }; | |
| 28 | |
| 29 namespace { | |
| 30 | |
| 31 TimeDelta EndTimestamp(const StreamParser::BufferQueue& queue) { | |
| 32 return queue.back()->timestamp() + queue.back()->duration(); | |
| 33 } | |
| 34 | |
| 35 // Check the input |text_configs| and |bytestream_ids| and return false if | |
| 36 // duplicate track ids are detected. | |
| 37 bool CheckBytestreamTrackIds( | |
| 38 const MediaTracks& tracks, | |
| 39 const StreamParser::TextTrackConfigMap& text_configs) { | |
| 40 std::set<StreamParser::TrackId> bytestream_ids; | |
| 41 for (const auto& track : tracks.tracks()) { | |
| 42 const StreamParser::TrackId& track_id = track->bytestream_track_id(); | |
| 43 if (bytestream_ids.find(track_id) != bytestream_ids.end()) { | |
| 44 return false; | |
| 45 } | |
| 46 bytestream_ids.insert(track_id); | |
| 47 } | |
| 48 for (const auto& text_track : text_configs) { | |
| 49 const StreamParser::TrackId& track_id = text_track.first; | |
| 50 if (bytestream_ids.find(track_id) != bytestream_ids.end()) { | |
| 51 return false; | |
| 52 } | |
| 53 bytestream_ids.insert(track_id); | |
| 54 } | |
| 55 return true; | |
| 56 } | |
| 57 | |
| 58 } // namespace | |
| 59 | |
| 60 // List of time ranges for each SourceBuffer. | |
| 61 // static | |
| 62 Ranges<TimeDelta> MediaSourceState::ComputeRangesIntersection( | |
| 63 const RangesList& active_ranges, | |
| 64 bool ended) { | |
| 65 // TODO(servolk): Perhaps this can be removed in favor of blink implementation | |
| 66 // (MediaSource::buffered)? Currently this is only used on Android and for | |
| 67 // updating DemuxerHost's buffered ranges during AppendData() as well as | |
| 68 // SourceBuffer.buffered property implementation. | |
| 69 // Implementation of HTMLMediaElement.buffered algorithm in MSE spec. | |
| 70 // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-sourc
e.html#dom-htmlmediaelement.buffered | |
| 71 | |
| 72 // Step 1: If activeSourceBuffers.length equals 0 then return an empty | |
| 73 // TimeRanges object and abort these steps. | |
| 74 if (active_ranges.empty()) | |
| 75 return Ranges<TimeDelta>(); | |
| 76 | |
| 77 // Step 2: Let active ranges be the ranges returned by buffered for each | |
| 78 // SourceBuffer object in activeSourceBuffers. | |
| 79 // Step 3: Let highest end time be the largest range end time in the active | |
| 80 // ranges. | |
| 81 TimeDelta highest_end_time; | |
| 82 for (const auto& range : active_ranges) { | |
| 83 if (!range.size()) | |
| 84 continue; | |
| 85 | |
| 86 highest_end_time = std::max(highest_end_time, range.end(range.size() - 1)); | |
| 87 } | |
| 88 | |
| 89 // Step 4: Let intersection ranges equal a TimeRange object containing a | |
| 90 // single range from 0 to highest end time. | |
| 91 Ranges<TimeDelta> intersection_ranges; | |
| 92 intersection_ranges.Add(TimeDelta(), highest_end_time); | |
| 93 | |
| 94 // Step 5: For each SourceBuffer object in activeSourceBuffers run the | |
| 95 // following steps: | |
| 96 for (const auto& range : active_ranges) { | |
| 97 // Step 5.1: Let source ranges equal the ranges returned by the buffered | |
| 98 // attribute on the current SourceBuffer. | |
| 99 Ranges<TimeDelta> source_ranges = range; | |
| 100 | |
| 101 // Step 5.2: If readyState is "ended", then set the end time on the last | |
| 102 // range in source ranges to highest end time. | |
| 103 if (ended && source_ranges.size()) { | |
| 104 source_ranges.Add(source_ranges.start(source_ranges.size() - 1), | |
| 105 highest_end_time); | |
| 106 } | |
| 107 | |
| 108 // Step 5.3: Let new intersection ranges equal the intersection between | |
| 109 // the intersection ranges and the source ranges. | |
| 110 // Step 5.4: Replace the ranges in intersection ranges with the new | |
| 111 // intersection ranges. | |
| 112 intersection_ranges = intersection_ranges.IntersectionWith(source_ranges); | |
| 113 } | |
| 114 | |
| 115 return intersection_ranges; | |
| 116 } | |
| 117 | |
| 118 MediaSourceState::MediaSourceState( | |
| 119 std::unique_ptr<StreamParser> stream_parser, | |
| 120 std::unique_ptr<FrameProcessor> frame_processor, | |
| 121 const CreateDemuxerStreamCB& create_demuxer_stream_cb, | |
| 122 const scoped_refptr<MediaLog>& media_log) | |
| 123 : create_demuxer_stream_cb_(create_demuxer_stream_cb), | |
| 124 timestamp_offset_during_append_(NULL), | |
| 125 parsing_media_segment_(false), | |
| 126 stream_parser_(stream_parser.release()), | |
| 127 frame_processor_(frame_processor.release()), | |
| 128 media_log_(media_log), | |
| 129 state_(UNINITIALIZED), | |
| 130 auto_update_timestamp_offset_(false) { | |
| 131 DCHECK(!create_demuxer_stream_cb_.is_null()); | |
| 132 DCHECK(frame_processor_); | |
| 133 } | |
| 134 | |
| 135 MediaSourceState::~MediaSourceState() { | |
| 136 Shutdown(); | |
| 137 } | |
| 138 | |
| 139 void MediaSourceState::Init( | |
| 140 const StreamParser::InitCB& init_cb, | |
| 141 const std::string& expected_codecs, | |
| 142 const StreamParser::EncryptedMediaInitDataCB& encrypted_media_init_data_cb, | |
| 143 const NewTextTrackCB& new_text_track_cb) { | |
| 144 DCHECK_EQ(state_, UNINITIALIZED); | |
| 145 new_text_track_cb_ = new_text_track_cb; | |
| 146 init_cb_ = init_cb; | |
| 147 | |
| 148 std::vector<std::string> expected_codecs_parsed; | |
| 149 ParseCodecString(expected_codecs, &expected_codecs_parsed, false); | |
| 150 | |
| 151 std::vector<AudioCodec> expected_acodecs; | |
| 152 std::vector<VideoCodec> expected_vcodecs; | |
| 153 for (const auto& codec_id : expected_codecs_parsed) { | |
| 154 AudioCodec acodec = StringToAudioCodec(codec_id); | |
| 155 if (acodec != kUnknownAudioCodec) { | |
| 156 expected_audio_codecs_.push_back(acodec); | |
| 157 continue; | |
| 158 } | |
| 159 VideoCodec vcodec = StringToVideoCodec(codec_id); | |
| 160 if (vcodec != kUnknownVideoCodec) { | |
| 161 expected_video_codecs_.push_back(vcodec); | |
| 162 continue; | |
| 163 } | |
| 164 MEDIA_LOG(INFO, media_log_) << "Unrecognized media codec: " << codec_id; | |
| 165 } | |
| 166 | |
| 167 state_ = PENDING_PARSER_CONFIG; | |
| 168 stream_parser_->Init( | |
| 169 base::Bind(&MediaSourceState::OnSourceInitDone, base::Unretained(this)), | |
| 170 base::Bind(&MediaSourceState::OnNewConfigs, base::Unretained(this), | |
| 171 expected_codecs), | |
| 172 base::Bind(&MediaSourceState::OnNewBuffers, base::Unretained(this)), | |
| 173 new_text_track_cb_.is_null(), encrypted_media_init_data_cb, | |
| 174 base::Bind(&MediaSourceState::OnNewMediaSegment, base::Unretained(this)), | |
| 175 base::Bind(&MediaSourceState::OnEndOfMediaSegment, | |
| 176 base::Unretained(this)), | |
| 177 media_log_); | |
| 178 } | |
| 179 | |
| 180 void MediaSourceState::SetSequenceMode(bool sequence_mode) { | |
| 181 DCHECK(!parsing_media_segment_); | |
| 182 | |
| 183 frame_processor_->SetSequenceMode(sequence_mode); | |
| 184 } | |
| 185 | |
| 186 void MediaSourceState::SetGroupStartTimestampIfInSequenceMode( | |
| 187 base::TimeDelta timestamp_offset) { | |
| 188 DCHECK(!parsing_media_segment_); | |
| 189 | |
| 190 frame_processor_->SetGroupStartTimestampIfInSequenceMode(timestamp_offset); | |
| 191 } | |
| 192 | |
| 193 void MediaSourceState::SetTracksWatcher( | |
| 194 const Demuxer::MediaTracksUpdatedCB& tracks_updated_cb) { | |
| 195 DCHECK(init_segment_received_cb_.is_null()); | |
| 196 DCHECK(!tracks_updated_cb.is_null()); | |
| 197 init_segment_received_cb_ = tracks_updated_cb; | |
| 198 } | |
| 199 | |
| 200 bool MediaSourceState::Append(const uint8_t* data, | |
| 201 size_t length, | |
| 202 TimeDelta append_window_start, | |
| 203 TimeDelta append_window_end, | |
| 204 TimeDelta* timestamp_offset) { | |
| 205 append_in_progress_ = true; | |
| 206 DCHECK(timestamp_offset); | |
| 207 DCHECK(!timestamp_offset_during_append_); | |
| 208 append_window_start_during_append_ = append_window_start; | |
| 209 append_window_end_during_append_ = append_window_end; | |
| 210 timestamp_offset_during_append_ = timestamp_offset; | |
| 211 | |
| 212 // TODO(wolenetz/acolwell): Curry and pass a NewBuffersCB here bound with | |
| 213 // append window and timestamp offset pointer. See http://crbug.com/351454. | |
| 214 bool result = stream_parser_->Parse(data, length); | |
| 215 if (!result) { | |
| 216 MEDIA_LOG(ERROR, media_log_) | |
| 217 << __func__ << ": stream parsing failed. Data size=" << length | |
| 218 << " append_window_start=" << append_window_start.InSecondsF() | |
| 219 << " append_window_end=" << append_window_end.InSecondsF(); | |
| 220 } | |
| 221 timestamp_offset_during_append_ = NULL; | |
| 222 append_in_progress_ = false; | |
| 223 return result; | |
| 224 } | |
| 225 | |
| 226 void MediaSourceState::ResetParserState(TimeDelta append_window_start, | |
| 227 TimeDelta append_window_end, | |
| 228 base::TimeDelta* timestamp_offset) { | |
| 229 DCHECK(timestamp_offset); | |
| 230 DCHECK(!timestamp_offset_during_append_); | |
| 231 timestamp_offset_during_append_ = timestamp_offset; | |
| 232 append_window_start_during_append_ = append_window_start; | |
| 233 append_window_end_during_append_ = append_window_end; | |
| 234 | |
| 235 stream_parser_->Flush(); | |
| 236 timestamp_offset_during_append_ = NULL; | |
| 237 | |
| 238 frame_processor_->Reset(); | |
| 239 parsing_media_segment_ = false; | |
| 240 media_segment_has_data_for_track_.clear(); | |
| 241 } | |
| 242 | |
| 243 void MediaSourceState::Remove(TimeDelta start, | |
| 244 TimeDelta end, | |
| 245 TimeDelta duration) { | |
| 246 for (const auto& it : audio_streams_) { | |
| 247 it.second->Remove(start, end, duration); | |
| 248 } | |
| 249 | |
| 250 for (const auto& it : video_streams_) { | |
| 251 it.second->Remove(start, end, duration); | |
| 252 } | |
| 253 | |
| 254 for (const auto& it : text_streams_) { | |
| 255 it.second->Remove(start, end, duration); | |
| 256 } | |
| 257 } | |
| 258 | |
| 259 bool MediaSourceState::EvictCodedFrames(DecodeTimestamp media_time, | |
| 260 size_t newDataSize) { | |
| 261 size_t total_buffered_size = 0; | |
| 262 for (const auto& it : audio_streams_) | |
| 263 total_buffered_size += it.second->GetBufferedSize(); | |
| 264 for (const auto& it : video_streams_) | |
| 265 total_buffered_size += it.second->GetBufferedSize(); | |
| 266 for (const auto& it : text_streams_) | |
| 267 total_buffered_size += it.second->GetBufferedSize(); | |
| 268 | |
| 269 DVLOG(3) << __func__ << " media_time=" << media_time.InSecondsF() | |
| 270 << " newDataSize=" << newDataSize | |
| 271 << " total_buffered_size=" << total_buffered_size; | |
| 272 | |
| 273 if (total_buffered_size == 0) | |
| 274 return true; | |
| 275 | |
| 276 bool success = true; | |
| 277 for (const auto& it : audio_streams_) { | |
| 278 uint64_t curr_size = it.second->GetBufferedSize(); | |
| 279 if (curr_size == 0) | |
| 280 continue; | |
| 281 uint64_t estimated_new_size = newDataSize * curr_size / total_buffered_size; | |
| 282 DCHECK_LE(estimated_new_size, SIZE_MAX); | |
| 283 success &= it.second->EvictCodedFrames( | |
| 284 media_time, static_cast<size_t>(estimated_new_size)); | |
| 285 } | |
| 286 for (const auto& it : video_streams_) { | |
| 287 uint64_t curr_size = it.second->GetBufferedSize(); | |
| 288 if (curr_size == 0) | |
| 289 continue; | |
| 290 uint64_t estimated_new_size = newDataSize * curr_size / total_buffered_size; | |
| 291 DCHECK_LE(estimated_new_size, SIZE_MAX); | |
| 292 success &= it.second->EvictCodedFrames( | |
| 293 media_time, static_cast<size_t>(estimated_new_size)); | |
| 294 } | |
| 295 for (const auto& it : text_streams_) { | |
| 296 uint64_t curr_size = it.second->GetBufferedSize(); | |
| 297 if (curr_size == 0) | |
| 298 continue; | |
| 299 uint64_t estimated_new_size = newDataSize * curr_size / total_buffered_size; | |
| 300 DCHECK_LE(estimated_new_size, SIZE_MAX); | |
| 301 success &= it.second->EvictCodedFrames( | |
| 302 media_time, static_cast<size_t>(estimated_new_size)); | |
| 303 } | |
| 304 | |
| 305 DVLOG(3) << __func__ << " success=" << success; | |
| 306 return success; | |
| 307 } | |
| 308 | |
| 309 Ranges<TimeDelta> MediaSourceState::GetBufferedRanges(TimeDelta duration, | |
| 310 bool ended) const { | |
| 311 RangesList ranges_list; | |
| 312 for (const auto& it : audio_streams_) | |
| 313 ranges_list.push_back(it.second->GetBufferedRanges(duration)); | |
| 314 | |
| 315 for (const auto& it : video_streams_) | |
| 316 ranges_list.push_back(it.second->GetBufferedRanges(duration)); | |
| 317 | |
| 318 for (const auto& it : text_streams_) | |
| 319 ranges_list.push_back(it.second->GetBufferedRanges(duration)); | |
| 320 | |
| 321 return ComputeRangesIntersection(ranges_list, ended); | |
| 322 } | |
| 323 | |
| 324 TimeDelta MediaSourceState::GetHighestPresentationTimestamp() const { | |
| 325 TimeDelta max_pts; | |
| 326 | |
| 327 for (const auto& it : audio_streams_) { | |
| 328 max_pts = std::max(max_pts, it.second->GetHighestPresentationTimestamp()); | |
| 329 } | |
| 330 | |
| 331 for (const auto& it : video_streams_) { | |
| 332 max_pts = std::max(max_pts, it.second->GetHighestPresentationTimestamp()); | |
| 333 } | |
| 334 | |
| 335 for (const auto& it : text_streams_) { | |
| 336 max_pts = std::max(max_pts, it.second->GetHighestPresentationTimestamp()); | |
| 337 } | |
| 338 | |
| 339 return max_pts; | |
| 340 } | |
| 341 | |
| 342 TimeDelta MediaSourceState::GetMaxBufferedDuration() const { | |
| 343 TimeDelta max_duration; | |
| 344 | |
| 345 for (const auto& it : audio_streams_) { | |
| 346 max_duration = std::max(max_duration, it.second->GetBufferedDuration()); | |
| 347 } | |
| 348 | |
| 349 for (const auto& it : video_streams_) { | |
| 350 max_duration = std::max(max_duration, it.second->GetBufferedDuration()); | |
| 351 } | |
| 352 | |
| 353 for (const auto& it : text_streams_) { | |
| 354 max_duration = std::max(max_duration, it.second->GetBufferedDuration()); | |
| 355 } | |
| 356 | |
| 357 return max_duration; | |
| 358 } | |
| 359 | |
| 360 void MediaSourceState::StartReturningData() { | |
| 361 for (const auto& it : audio_streams_) { | |
| 362 it.second->StartReturningData(); | |
| 363 } | |
| 364 | |
| 365 for (const auto& it : video_streams_) { | |
| 366 it.second->StartReturningData(); | |
| 367 } | |
| 368 | |
| 369 for (const auto& it : text_streams_) { | |
| 370 it.second->StartReturningData(); | |
| 371 } | |
| 372 } | |
| 373 | |
| 374 void MediaSourceState::AbortReads() { | |
| 375 for (const auto& it : audio_streams_) { | |
| 376 it.second->AbortReads(); | |
| 377 } | |
| 378 | |
| 379 for (const auto& it : video_streams_) { | |
| 380 it.second->AbortReads(); | |
| 381 } | |
| 382 | |
| 383 for (const auto& it : text_streams_) { | |
| 384 it.second->AbortReads(); | |
| 385 } | |
| 386 } | |
| 387 | |
| 388 void MediaSourceState::Seek(TimeDelta seek_time) { | |
| 389 for (const auto& it : audio_streams_) { | |
| 390 it.second->Seek(seek_time); | |
| 391 } | |
| 392 | |
| 393 for (const auto& it : video_streams_) { | |
| 394 it.second->Seek(seek_time); | |
| 395 } | |
| 396 | |
| 397 for (const auto& it : text_streams_) { | |
| 398 it.second->Seek(seek_time); | |
| 399 } | |
| 400 } | |
| 401 | |
| 402 void MediaSourceState::CompletePendingReadIfPossible() { | |
| 403 for (const auto& it : audio_streams_) { | |
| 404 it.second->CompletePendingReadIfPossible(); | |
| 405 } | |
| 406 | |
| 407 for (const auto& it : video_streams_) { | |
| 408 it.second->CompletePendingReadIfPossible(); | |
| 409 } | |
| 410 | |
| 411 for (const auto& it : text_streams_) { | |
| 412 it.second->CompletePendingReadIfPossible(); | |
| 413 } | |
| 414 } | |
| 415 | |
| 416 void MediaSourceState::OnSetDuration(TimeDelta duration) { | |
| 417 for (const auto& it : audio_streams_) { | |
| 418 it.second->OnSetDuration(duration); | |
| 419 } | |
| 420 | |
| 421 for (const auto& it : video_streams_) { | |
| 422 it.second->OnSetDuration(duration); | |
| 423 } | |
| 424 | |
| 425 for (const auto& it : text_streams_) { | |
| 426 it.second->OnSetDuration(duration); | |
| 427 } | |
| 428 } | |
| 429 | |
| 430 void MediaSourceState::MarkEndOfStream() { | |
| 431 for (const auto& it : audio_streams_) { | |
| 432 it.second->MarkEndOfStream(); | |
| 433 } | |
| 434 | |
| 435 for (const auto& it : video_streams_) { | |
| 436 it.second->MarkEndOfStream(); | |
| 437 } | |
| 438 | |
| 439 for (const auto& it : text_streams_) { | |
| 440 it.second->MarkEndOfStream(); | |
| 441 } | |
| 442 } | |
| 443 | |
| 444 void MediaSourceState::UnmarkEndOfStream() { | |
| 445 for (const auto& it : audio_streams_) { | |
| 446 it.second->UnmarkEndOfStream(); | |
| 447 } | |
| 448 | |
| 449 for (const auto& it : video_streams_) { | |
| 450 it.second->UnmarkEndOfStream(); | |
| 451 } | |
| 452 | |
| 453 for (const auto& it : text_streams_) { | |
| 454 it.second->UnmarkEndOfStream(); | |
| 455 } | |
| 456 } | |
| 457 | |
| 458 void MediaSourceState::Shutdown() { | |
| 459 for (const auto& it : audio_streams_) { | |
| 460 it.second->Shutdown(); | |
| 461 } | |
| 462 | |
| 463 for (const auto& it : video_streams_) { | |
| 464 it.second->Shutdown(); | |
| 465 } | |
| 466 | |
| 467 for (const auto& it : text_streams_) { | |
| 468 it.second->Shutdown(); | |
| 469 } | |
| 470 } | |
| 471 | |
| 472 void MediaSourceState::SetMemoryLimits(DemuxerStream::Type type, | |
| 473 size_t memory_limit) { | |
| 474 switch (type) { | |
| 475 case DemuxerStream::AUDIO: | |
| 476 for (const auto& it : audio_streams_) { | |
| 477 it.second->SetStreamMemoryLimit(memory_limit); | |
| 478 } | |
| 479 break; | |
| 480 case DemuxerStream::VIDEO: | |
| 481 for (const auto& it : video_streams_) { | |
| 482 it.second->SetStreamMemoryLimit(memory_limit); | |
| 483 } | |
| 484 break; | |
| 485 case DemuxerStream::TEXT: | |
| 486 for (const auto& it : text_streams_) { | |
| 487 it.second->SetStreamMemoryLimit(memory_limit); | |
| 488 } | |
| 489 break; | |
| 490 case DemuxerStream::UNKNOWN: | |
| 491 case DemuxerStream::NUM_TYPES: | |
| 492 NOTREACHED(); | |
| 493 break; | |
| 494 } | |
| 495 } | |
| 496 | |
| 497 bool MediaSourceState::IsSeekWaitingForData() const { | |
| 498 for (const auto& it : audio_streams_) { | |
| 499 if (it.second->IsSeekWaitingForData()) | |
| 500 return true; | |
| 501 } | |
| 502 | |
| 503 for (const auto& it : video_streams_) { | |
| 504 if (it.second->IsSeekWaitingForData()) | |
| 505 return true; | |
| 506 } | |
| 507 | |
| 508 // NOTE: We are intentionally not checking the text tracks | |
| 509 // because text tracks are discontinuous and may not have data | |
| 510 // for the seek position. This is ok and playback should not be | |
| 511 // stalled because we don't have cues. If cues, with timestamps after | |
| 512 // the seek time, eventually arrive they will be delivered properly | |
| 513 // in response to ChunkDemuxerStream::Read() calls. | |
| 514 | |
| 515 return false; | |
| 516 } | |
| 517 | |
| 518 bool MediaSourceState::OnNewConfigs( | |
| 519 std::string expected_codecs, | |
| 520 std::unique_ptr<MediaTracks> tracks, | |
| 521 const StreamParser::TextTrackConfigMap& text_configs) { | |
| 522 DCHECK(tracks.get()); | |
| 523 DVLOG(1) << __func__ << " expected_codecs=" << expected_codecs | |
| 524 << " tracks=" << tracks->tracks().size(); | |
| 525 DCHECK_GE(state_, PENDING_PARSER_CONFIG); | |
| 526 | |
| 527 // Check that there is no clashing bytestream track ids. | |
| 528 if (!CheckBytestreamTrackIds(*tracks, text_configs)) { | |
| 529 MEDIA_LOG(ERROR, media_log_) << "Duplicate bytestream track ids detected"; | |
| 530 for (const auto& track : tracks->tracks()) { | |
| 531 const StreamParser::TrackId& track_id = track->bytestream_track_id(); | |
| 532 MEDIA_LOG(DEBUG, media_log_) << TrackTypeToStr(track->type()) << " track " | |
| 533 << " bytestream track id=" << track_id; | |
| 534 } | |
| 535 return false; | |
| 536 } | |
| 537 | |
| 538 // MSE spec allows new configs to be emitted only during Append, but not | |
| 539 // during Flush or parser reset operations. | |
| 540 CHECK(append_in_progress_); | |
| 541 | |
| 542 bool success = true; | |
| 543 | |
| 544 // TODO(wolenetz): Update codec string strictness, if necessary, once spec | |
| 545 // issue https://github.com/w3c/media-source/issues/161 is resolved. | |
| 546 std::vector<AudioCodec> expected_acodecs = expected_audio_codecs_; | |
| 547 std::vector<VideoCodec> expected_vcodecs = expected_video_codecs_; | |
| 548 | |
| 549 for (const auto& track : tracks->tracks()) { | |
| 550 const auto& track_id = track->bytestream_track_id(); | |
| 551 | |
| 552 if (track->type() == MediaTrack::Audio) { | |
| 553 AudioDecoderConfig audio_config = tracks->getAudioConfig(track_id); | |
| 554 DVLOG(1) << "Audio track_id=" << track_id | |
| 555 << " config: " << audio_config.AsHumanReadableString(); | |
| 556 DCHECK(audio_config.IsValidConfig()); | |
| 557 | |
| 558 const auto& it = std::find(expected_acodecs.begin(), | |
| 559 expected_acodecs.end(), audio_config.codec()); | |
| 560 if (it == expected_acodecs.end()) { | |
| 561 MEDIA_LOG(ERROR, media_log_) << "Audio stream codec " | |
| 562 << GetCodecName(audio_config.codec()) | |
| 563 << " doesn't match SourceBuffer codecs."; | |
| 564 return false; | |
| 565 } | |
| 566 expected_acodecs.erase(it); | |
| 567 | |
| 568 ChunkDemuxerStream* stream = nullptr; | |
| 569 if (!first_init_segment_received_) { | |
| 570 DCHECK(audio_streams_.find(track_id) == audio_streams_.end()); | |
| 571 stream = create_demuxer_stream_cb_.Run(DemuxerStream::AUDIO); | |
| 572 if (!stream || !frame_processor_->AddTrack(track_id, stream)) { | |
| 573 MEDIA_LOG(ERROR, media_log_) << "Failed to create audio stream."; | |
| 574 return false; | |
| 575 } | |
| 576 audio_streams_[track_id] = stream; | |
| 577 media_log_->SetBooleanProperty("found_audio_stream", true); | |
| 578 media_log_->SetStringProperty("audio_codec_name", | |
| 579 GetCodecName(audio_config.codec())); | |
| 580 } else { | |
| 581 if (audio_streams_.size() > 1) { | |
| 582 auto it = audio_streams_.find(track_id); | |
| 583 if (it != audio_streams_.end()) | |
| 584 stream = it->second; | |
| 585 } else { | |
| 586 // If there is only one audio track then bytestream id might change in | |
| 587 // a new init segment. So update our state and notify frame processor. | |
| 588 const auto& it = audio_streams_.begin(); | |
| 589 if (it != audio_streams_.end()) { | |
| 590 stream = it->second; | |
| 591 if (it->first != track_id) { | |
| 592 frame_processor_->UpdateTrack(it->first, track_id); | |
| 593 audio_streams_[track_id] = stream; | |
| 594 audio_streams_.erase(it->first); | |
| 595 } | |
| 596 } | |
| 597 } | |
| 598 if (!stream) { | |
| 599 MEDIA_LOG(ERROR, media_log_) << "Got unexpected audio track" | |
| 600 << " track_id=" << track_id; | |
| 601 return false; | |
| 602 } | |
| 603 } | |
| 604 | |
| 605 track->set_id(stream->media_track_id()); | |
| 606 frame_processor_->OnPossibleAudioConfigUpdate(audio_config); | |
| 607 success &= stream->UpdateAudioConfig(audio_config, media_log_); | |
| 608 } else if (track->type() == MediaTrack::Video) { | |
| 609 VideoDecoderConfig video_config = tracks->getVideoConfig(track_id); | |
| 610 DVLOG(1) << "Video track_id=" << track_id | |
| 611 << " config: " << video_config.AsHumanReadableString(); | |
| 612 DCHECK(video_config.IsValidConfig()); | |
| 613 | |
| 614 const auto& it = std::find(expected_vcodecs.begin(), | |
| 615 expected_vcodecs.end(), video_config.codec()); | |
| 616 if (it == expected_vcodecs.end()) { | |
| 617 MEDIA_LOG(ERROR, media_log_) << "Video stream codec " | |
| 618 << GetCodecName(video_config.codec()) | |
| 619 << " doesn't match SourceBuffer codecs."; | |
| 620 return false; | |
| 621 } | |
| 622 expected_vcodecs.erase(it); | |
| 623 | |
| 624 ChunkDemuxerStream* stream = nullptr; | |
| 625 if (!first_init_segment_received_) { | |
| 626 DCHECK(video_streams_.find(track_id) == video_streams_.end()); | |
| 627 stream = create_demuxer_stream_cb_.Run(DemuxerStream::VIDEO); | |
| 628 if (!stream || !frame_processor_->AddTrack(track_id, stream)) { | |
| 629 MEDIA_LOG(ERROR, media_log_) << "Failed to create video stream."; | |
| 630 return false; | |
| 631 } | |
| 632 video_streams_[track_id] = stream; | |
| 633 media_log_->SetBooleanProperty("found_video_stream", true); | |
| 634 media_log_->SetStringProperty("video_codec_name", | |
| 635 GetCodecName(video_config.codec())); | |
| 636 } else { | |
| 637 if (video_streams_.size() > 1) { | |
| 638 auto it = video_streams_.find(track_id); | |
| 639 if (it != video_streams_.end()) | |
| 640 stream = it->second; | |
| 641 } else { | |
| 642 // If there is only one video track then bytestream id might change in | |
| 643 // a new init segment. So update our state and notify frame processor. | |
| 644 const auto& it = video_streams_.begin(); | |
| 645 if (it != video_streams_.end()) { | |
| 646 stream = it->second; | |
| 647 if (it->first != track_id) { | |
| 648 frame_processor_->UpdateTrack(it->first, track_id); | |
| 649 video_streams_[track_id] = stream; | |
| 650 video_streams_.erase(it->first); | |
| 651 } | |
| 652 } | |
| 653 } | |
| 654 if (!stream) { | |
| 655 MEDIA_LOG(ERROR, media_log_) << "Got unexpected video track" | |
| 656 << " track_id=" << track_id; | |
| 657 return false; | |
| 658 } | |
| 659 } | |
| 660 | |
| 661 track->set_id(stream->media_track_id()); | |
| 662 success &= stream->UpdateVideoConfig(video_config, media_log_); | |
| 663 } else { | |
| 664 MEDIA_LOG(ERROR, media_log_) << "Error: unsupported media track type " | |
| 665 << track->type(); | |
| 666 return false; | |
| 667 } | |
| 668 } | |
| 669 | |
| 670 if (!expected_acodecs.empty() || !expected_vcodecs.empty()) { | |
| 671 for (const auto& acodec : expected_acodecs) { | |
| 672 MEDIA_LOG(ERROR, media_log_) << "Initialization segment misses expected " | |
| 673 << GetCodecName(acodec) << " track."; | |
| 674 } | |
| 675 for (const auto& vcodec : expected_vcodecs) { | |
| 676 MEDIA_LOG(ERROR, media_log_) << "Initialization segment misses expected " | |
| 677 << GetCodecName(vcodec) << " track."; | |
| 678 } | |
| 679 return false; | |
| 680 } | |
| 681 | |
| 682 if (text_streams_.empty()) { | |
| 683 for (auto itr = text_configs.begin(); itr != text_configs.end(); ++itr) { | |
| 684 ChunkDemuxerStream* const text_stream = | |
| 685 create_demuxer_stream_cb_.Run(DemuxerStream::TEXT); | |
| 686 if (!frame_processor_->AddTrack(itr->first, text_stream)) { | |
| 687 success &= false; | |
| 688 MEDIA_LOG(ERROR, media_log_) << "Failed to add text track ID " | |
| 689 << itr->first << " to frame processor."; | |
| 690 break; | |
| 691 } | |
| 692 text_stream->UpdateTextConfig(itr->second, media_log_); | |
| 693 text_streams_[itr->first] = text_stream; | |
| 694 new_text_track_cb_.Run(text_stream, itr->second); | |
| 695 } | |
| 696 } else { | |
| 697 const size_t text_count = text_streams_.size(); | |
| 698 if (text_configs.size() != text_count) { | |
| 699 success &= false; | |
| 700 MEDIA_LOG(ERROR, media_log_) | |
| 701 << "The number of text track configs changed."; | |
| 702 } else if (text_count == 1) { | |
| 703 auto config_itr = text_configs.begin(); | |
| 704 auto stream_itr = text_streams_.begin(); | |
| 705 ChunkDemuxerStream* text_stream = stream_itr->second; | |
| 706 TextTrackConfig old_config = text_stream->text_track_config(); | |
| 707 TextTrackConfig new_config( | |
| 708 config_itr->second.kind(), config_itr->second.label(), | |
| 709 config_itr->second.language(), old_config.id()); | |
| 710 if (!new_config.Matches(old_config)) { | |
| 711 success &= false; | |
| 712 MEDIA_LOG(ERROR, media_log_) | |
| 713 << "New text track config does not match old one."; | |
| 714 } else { | |
| 715 StreamParser::TrackId old_id = stream_itr->first; | |
| 716 StreamParser::TrackId new_id = config_itr->first; | |
| 717 if (new_id != old_id) { | |
| 718 if (frame_processor_->UpdateTrack(old_id, new_id)) { | |
| 719 text_streams_.clear(); | |
| 720 text_streams_[config_itr->first] = text_stream; | |
| 721 } else { | |
| 722 success &= false; | |
| 723 MEDIA_LOG(ERROR, media_log_) | |
| 724 << "Error remapping single text track number"; | |
| 725 } | |
| 726 } | |
| 727 } | |
| 728 } else { | |
| 729 for (auto config_itr = text_configs.begin(); | |
| 730 config_itr != text_configs.end(); ++config_itr) { | |
| 731 auto stream_itr = text_streams_.find(config_itr->first); | |
| 732 if (stream_itr == text_streams_.end()) { | |
| 733 success &= false; | |
| 734 MEDIA_LOG(ERROR, media_log_) | |
| 735 << "Unexpected text track configuration for track ID " | |
| 736 << config_itr->first; | |
| 737 break; | |
| 738 } | |
| 739 | |
| 740 const TextTrackConfig& new_config = config_itr->second; | |
| 741 ChunkDemuxerStream* stream = stream_itr->second; | |
| 742 TextTrackConfig old_config = stream->text_track_config(); | |
| 743 if (!new_config.Matches(old_config)) { | |
| 744 success &= false; | |
| 745 MEDIA_LOG(ERROR, media_log_) << "New text track config for track ID " | |
| 746 << config_itr->first | |
| 747 << " does not match old one."; | |
| 748 break; | |
| 749 } | |
| 750 } | |
| 751 } | |
| 752 } | |
| 753 | |
| 754 if (audio_streams_.empty() && video_streams_.empty()) { | |
| 755 DVLOG(1) << __func__ << ": couldn't find a valid audio or video stream"; | |
| 756 return false; | |
| 757 } | |
| 758 | |
| 759 frame_processor_->SetAllTrackBuffersNeedRandomAccessPoint(); | |
| 760 | |
| 761 if (!first_init_segment_received_) { | |
| 762 first_init_segment_received_ = true; | |
| 763 SetStreamMemoryLimits(); | |
| 764 } | |
| 765 | |
| 766 DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed"); | |
| 767 if (success) { | |
| 768 if (state_ == PENDING_PARSER_CONFIG) | |
| 769 state_ = PENDING_PARSER_INIT; | |
| 770 DCHECK(!init_segment_received_cb_.is_null()); | |
| 771 init_segment_received_cb_.Run(std::move(tracks)); | |
| 772 } | |
| 773 | |
| 774 return success; | |
| 775 } | |
| 776 | |
| 777 void MediaSourceState::SetStreamMemoryLimits() { | |
| 778 auto cmd_line = base::CommandLine::ForCurrentProcess(); | |
| 779 | |
| 780 std::string audio_buf_limit_switch = | |
| 781 cmd_line->GetSwitchValueASCII(switches::kMSEAudioBufferSizeLimit); | |
| 782 unsigned audio_buf_size_limit = 0; | |
| 783 if (base::StringToUint(audio_buf_limit_switch, &audio_buf_size_limit) && | |
| 784 audio_buf_size_limit > 0) { | |
| 785 MEDIA_LOG(INFO, media_log_) | |
| 786 << "Custom audio per-track SourceBuffer size limit=" | |
| 787 << audio_buf_size_limit; | |
| 788 for (const auto& it : audio_streams_) { | |
| 789 it.second->SetStreamMemoryLimit(audio_buf_size_limit); | |
| 790 } | |
| 791 } | |
| 792 | |
| 793 std::string video_buf_limit_switch = | |
| 794 cmd_line->GetSwitchValueASCII(switches::kMSEVideoBufferSizeLimit); | |
| 795 unsigned video_buf_size_limit = 0; | |
| 796 if (base::StringToUint(video_buf_limit_switch, &video_buf_size_limit) && | |
| 797 video_buf_size_limit > 0) { | |
| 798 MEDIA_LOG(INFO, media_log_) | |
| 799 << "Custom video per-track SourceBuffer size limit=" | |
| 800 << video_buf_size_limit; | |
| 801 for (const auto& it : video_streams_) { | |
| 802 it.second->SetStreamMemoryLimit(video_buf_size_limit); | |
| 803 } | |
| 804 } | |
| 805 } | |
| 806 | |
| 807 void MediaSourceState::OnNewMediaSegment() { | |
| 808 DVLOG(2) << "OnNewMediaSegment()"; | |
| 809 DCHECK_EQ(state_, PARSER_INITIALIZED); | |
| 810 parsing_media_segment_ = true; | |
| 811 media_segment_has_data_for_track_.clear(); | |
| 812 } | |
| 813 | |
| 814 void MediaSourceState::OnEndOfMediaSegment() { | |
| 815 DVLOG(2) << "OnEndOfMediaSegment()"; | |
| 816 DCHECK_EQ(state_, PARSER_INITIALIZED); | |
| 817 parsing_media_segment_ = false; | |
| 818 | |
| 819 for (const auto& it : audio_streams_) { | |
| 820 if (!media_segment_has_data_for_track_[it.first]) { | |
| 821 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_missing_track_logs_, | |
| 822 kMaxMissingTrackInSegmentLogs) | |
| 823 << "Media segment did not contain any coded frames for track " | |
| 824 << it.first << ", mismatching initialization segment. Therefore, MSE" | |
| 825 " coded frame processing may not interoperably detect" | |
| 826 " discontinuities in appended media."; | |
| 827 } | |
| 828 } | |
| 829 for (const auto& it : video_streams_) { | |
| 830 if (!media_segment_has_data_for_track_[it.first]) { | |
| 831 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_missing_track_logs_, | |
| 832 kMaxMissingTrackInSegmentLogs) | |
| 833 << "Media segment did not contain any coded frames for track " | |
| 834 << it.first << ", mismatching initialization segment. Therefore, MSE" | |
| 835 " coded frame processing may not interoperably detect" | |
| 836 " discontinuities in appended media."; | |
| 837 } | |
| 838 } | |
| 839 } | |
| 840 | |
| 841 bool MediaSourceState::OnNewBuffers( | |
| 842 const StreamParser::BufferQueueMap& buffer_queue_map) { | |
| 843 DVLOG(2) << __func__ << " buffer_queues=" << buffer_queue_map.size(); | |
| 844 DCHECK_EQ(state_, PARSER_INITIALIZED); | |
| 845 DCHECK(timestamp_offset_during_append_); | |
| 846 DCHECK(parsing_media_segment_); | |
| 847 | |
| 848 for (const auto& it : buffer_queue_map) { | |
| 849 const StreamParser::BufferQueue& bufq = it.second; | |
| 850 DCHECK(!bufq.empty()); | |
| 851 media_segment_has_data_for_track_[it.first] = true; | |
| 852 } | |
| 853 | |
| 854 const TimeDelta timestamp_offset_before_processing = | |
| 855 *timestamp_offset_during_append_; | |
| 856 | |
| 857 // Calculate the new timestamp offset for audio/video tracks if the stream | |
| 858 // parser has requested automatic updates. | |
| 859 TimeDelta new_timestamp_offset = timestamp_offset_before_processing; | |
| 860 if (auto_update_timestamp_offset_) { | |
| 861 TimeDelta min_end_timestamp = kNoTimestamp; | |
| 862 for (const auto& it : buffer_queue_map) { | |
| 863 const StreamParser::BufferQueue& bufq = it.second; | |
| 864 DCHECK(!bufq.empty()); | |
| 865 if (min_end_timestamp == kNoTimestamp || | |
| 866 EndTimestamp(bufq) < min_end_timestamp) { | |
| 867 min_end_timestamp = EndTimestamp(bufq); | |
| 868 DCHECK_NE(kNoTimestamp, min_end_timestamp); | |
| 869 } | |
| 870 } | |
| 871 if (min_end_timestamp != kNoTimestamp) | |
| 872 new_timestamp_offset += min_end_timestamp; | |
| 873 } | |
| 874 | |
| 875 if (!frame_processor_->ProcessFrames( | |
| 876 buffer_queue_map, append_window_start_during_append_, | |
| 877 append_window_end_during_append_, timestamp_offset_during_append_)) { | |
| 878 return false; | |
| 879 } | |
| 880 | |
| 881 // Only update the timestamp offset if the frame processor hasn't already. | |
| 882 if (auto_update_timestamp_offset_ && | |
| 883 timestamp_offset_before_processing == *timestamp_offset_during_append_) { | |
| 884 *timestamp_offset_during_append_ = new_timestamp_offset; | |
| 885 } | |
| 886 | |
| 887 return true; | |
| 888 } | |
| 889 void MediaSourceState::OnSourceInitDone( | |
| 890 const StreamParser::InitParameters& params) { | |
| 891 DCHECK_EQ(state_, PENDING_PARSER_INIT); | |
| 892 state_ = PARSER_INITIALIZED; | |
| 893 auto_update_timestamp_offset_ = params.auto_update_timestamp_offset; | |
| 894 base::ResetAndReturn(&init_cb_).Run(params); | |
| 895 } | |
| 896 | |
| 897 } // namespace media | |
| OLD | NEW |