| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/filters/frame_processor.h" | 5 #include "media/filters/frame_processor.h" |
| 6 | 6 |
| 7 #include <cstdlib> | 7 #include <cstdlib> |
| 8 | 8 |
| 9 #include "base/stl_util.h" | 9 #include "base/stl_util.h" |
| 10 #include "media/base/buffers.h" | 10 #include "media/base/buffers.h" |
| (...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 325 | 325 |
| 326 return result; | 326 return result; |
| 327 } | 327 } |
| 328 | 328 |
| 329 bool FrameProcessor::HandlePartialAppendWindowTrimming( | 329 bool FrameProcessor::HandlePartialAppendWindowTrimming( |
| 330 base::TimeDelta append_window_start, | 330 base::TimeDelta append_window_start, |
| 331 base::TimeDelta append_window_end, | 331 base::TimeDelta append_window_end, |
| 332 const scoped_refptr<StreamParserBuffer>& buffer) { | 332 const scoped_refptr<StreamParserBuffer>& buffer) { |
| 333 DCHECK(buffer->duration() > base::TimeDelta()); | 333 DCHECK(buffer->duration() > base::TimeDelta()); |
| 334 DCHECK_EQ(DemuxerStream::AUDIO, buffer->type()); | 334 DCHECK_EQ(DemuxerStream::AUDIO, buffer->type()); |
| 335 DCHECK(buffer->IsKeyframe()); |
| 335 | 336 |
| 336 const base::TimeDelta frame_end_timestamp = | 337 const base::TimeDelta frame_end_timestamp = |
| 337 buffer->timestamp() + buffer->duration(); | 338 buffer->timestamp() + buffer->duration(); |
| 338 | 339 |
| 339 // Ignore any buffers which start after |append_window_start| or end after | |
| 340 // |append_window_end|. For simplicity, even those that start before | |
| 341 // |append_window_start|. | |
| 342 if (buffer->timestamp() > append_window_start || | |
| 343 frame_end_timestamp > append_window_end) { | |
| 344 // TODO(dalecurtis): Partial append window trimming could also be done | |
| 345 // around |append_window_end|, but is not necessary since splice frames | |
| 346 // cover overlaps there. | |
| 347 return false; | |
| 348 } | |
| 349 | |
| 350 // If the buffer is entirely before |append_window_start|, save it as preroll | 340 // If the buffer is entirely before |append_window_start|, save it as preroll |
| 351 // for the first buffer which overlaps |append_window_start|. | 341 // for the first buffer which overlaps |append_window_start|. |
| 352 if (buffer->timestamp() < append_window_start && | 342 if (buffer->timestamp() < append_window_start && |
| 353 frame_end_timestamp <= append_window_start) { | 343 frame_end_timestamp <= append_window_start) { |
| 354 audio_preroll_buffer_ = buffer; | 344 audio_preroll_buffer_ = buffer; |
| 355 return false; | 345 return false; |
| 356 } | 346 } |
| 357 | 347 |
| 358 // There's nothing to be done if we have no preroll and the buffer starts on | 348 // If the buffer is entirely after |append_window_end| there's nothing to do. |
| 359 // the append window start. | 349 if (buffer->timestamp() >= append_window_end) |
| 360 if (buffer->timestamp() == append_window_start && !audio_preroll_buffer_) | |
| 361 return false; | 350 return false; |
| 362 | 351 |
| 363 // See if a partial discard can be done around |append_window_start|. | 352 DCHECK(buffer->timestamp() >= append_window_start || |
| 364 DCHECK(buffer->timestamp() <= append_window_start); | 353 frame_end_timestamp > append_window_start); |
| 365 DCHECK(buffer->IsKeyframe()); | |
| 366 DVLOG(1) << "Truncating buffer which overlaps append window start." | |
| 367 << " presentation_timestamp " << buffer->timestamp().InSecondsF() | |
| 368 << " append_window_start " << append_window_start.InSecondsF(); | |
| 369 | 354 |
| 370 // If this isn't the first buffer discarded by the append window, try to use | 355 bool processed_buffer = false; |
| 371 // the last buffer discarded for preroll. This ensures that the partially | 356 |
| 372 // trimmed buffer can be correctly decoded. | 357 // If we have a preroll buffer see if we can attach it to the first buffer |
| 358 // overlapping or after |append_window_start|. |
| 373 if (audio_preroll_buffer_) { | 359 if (audio_preroll_buffer_) { |
| 374 // We only want to use the preroll buffer if it directly precedes (less than | 360 // We only want to use the preroll buffer if it directly precedes (less |
| 375 // one sample apart) the current buffer. | 361 // than one sample apart) the current buffer. |
| 376 const int64 delta = std::abs((audio_preroll_buffer_->timestamp() + | 362 const int64 delta = std::abs((audio_preroll_buffer_->timestamp() + |
| 377 audio_preroll_buffer_->duration() - | 363 audio_preroll_buffer_->duration() - |
| 378 buffer->timestamp()).InMicroseconds()); | 364 buffer->timestamp()).InMicroseconds()); |
| 379 if (delta < sample_duration_.InMicroseconds()) { | 365 if (delta < sample_duration_.InMicroseconds()) { |
| 366 DVLOG(1) << "Attaching audio preroll buffer [" |
| 367 << audio_preroll_buffer_->timestamp().InSecondsF() << ", " |
| 368 << (audio_preroll_buffer_->timestamp() + |
| 369 audio_preroll_buffer_->duration()).InSecondsF() << ") to " |
| 370 << buffer->timestamp().InSecondsF(); |
| 380 buffer->SetPrerollBuffer(audio_preroll_buffer_); | 371 buffer->SetPrerollBuffer(audio_preroll_buffer_); |
| 372 processed_buffer = true; |
| 381 } else { | 373 } else { |
| 382 // TODO(dalecurtis): Add a MEDIA_LOG() for when this is dropped unused. | 374 // TODO(dalecurtis): Add a MEDIA_LOG() for when this is dropped unused. |
| 383 } | 375 } |
| 384 audio_preroll_buffer_ = NULL; | 376 audio_preroll_buffer_ = NULL; |
| 385 } | 377 } |
| 386 | 378 |
| 387 // Decrease the duration appropriately. We only need to shorten the buffer if | 379 // See if a partial discard can be done around |append_window_start|. |
| 388 // it overlaps |append_window_start|. | |
| 389 if (buffer->timestamp() < append_window_start) { | 380 if (buffer->timestamp() < append_window_start) { |
| 381 DVLOG(1) << "Truncating buffer which overlaps append window start." |
| 382 << " presentation_timestamp " << buffer->timestamp().InSecondsF() |
| 383 << " frame_end_timestamp " << frame_end_timestamp.InSecondsF() |
| 384 << " append_window_start " << append_window_start.InSecondsF(); |
| 385 |
| 386 // Mark the overlapping portion of the buffer for discard. |
| 390 buffer->set_discard_padding(std::make_pair( | 387 buffer->set_discard_padding(std::make_pair( |
| 391 append_window_start - buffer->timestamp(), base::TimeDelta())); | 388 append_window_start - buffer->timestamp(), base::TimeDelta())); |
| 389 |
| 390 // Adjust the timestamp of this buffer forward to |append_window_start| and |
| 391 // decrease the duration to compensate. |
| 392 buffer->set_timestamp(append_window_start); |
| 393 buffer->SetDecodeTimestamp(append_window_start); |
| 392 buffer->set_duration(frame_end_timestamp - append_window_start); | 394 buffer->set_duration(frame_end_timestamp - append_window_start); |
| 395 processed_buffer = true; |
| 393 } | 396 } |
| 394 | 397 |
| 395 // Adjust the timestamp of this buffer forward to |append_window_start|. The | 398 // See if a partial discard can be done around |append_window_end|. |
| 396 // timestamps are always set, even if |buffer|'s timestamp is already set to | 399 if (frame_end_timestamp > append_window_end) { |
| 397 // |append_window_start|, to ensure the preroll buffer is setup correctly. | 400 DVLOG(1) << "Truncating buffer which overlaps append window end." |
| 398 buffer->set_timestamp(append_window_start); | 401 << " presentation_timestamp " << buffer->timestamp().InSecondsF() |
| 399 buffer->SetDecodeTimestamp(append_window_start); | 402 << " frame_end_timestamp " << frame_end_timestamp.InSecondsF() |
| 400 return true; | 403 << " append_window_end " << append_window_end.InSecondsF(); |
| 404 |
| 405 // Mark the overlapping portion of the buffer for discard. |
| 406 buffer->set_discard_padding( |
| 407 std::make_pair(buffer->discard_padding().first, |
| 408 frame_end_timestamp - append_window_end)); |
| 409 |
| 410 // Decrease the duration of the buffer to remove the discarded portion. |
| 411 buffer->set_duration(append_window_end - buffer->timestamp()); |
| 412 processed_buffer = true; |
| 413 } |
| 414 |
| 415 return processed_buffer; |
| 401 } | 416 } |
| 402 | 417 |
| 403 bool FrameProcessor::ProcessFrame( | 418 bool FrameProcessor::ProcessFrame( |
| 404 const scoped_refptr<StreamParserBuffer>& frame, | 419 const scoped_refptr<StreamParserBuffer>& frame, |
| 405 base::TimeDelta append_window_start, | 420 base::TimeDelta append_window_start, |
| 406 base::TimeDelta append_window_end, | 421 base::TimeDelta append_window_end, |
| 407 base::TimeDelta* timestamp_offset, | 422 base::TimeDelta* timestamp_offset, |
| 408 bool* new_media_segment) { | 423 bool* new_media_segment) { |
| 409 // Implements the loop within step 1 of the coded frame processing algorithm | 424 // Implements the loop within step 1 of the coded frame processing algorithm |
| 410 // for a single input frame per April 1, 2014 MSE spec editor's draft: | 425 // for a single input frame per April 1, 2014 MSE spec editor's draft: |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 557 | 572 |
| 558 // 7.6. Jump to the Loop Top step above to restart processing of the | 573 // 7.6. Jump to the Loop Top step above to restart processing of the |
| 559 // current coded frame. | 574 // current coded frame. |
| 560 DVLOG(3) << __FUNCTION__ << ": Discontinuity: reprocessing frame"; | 575 DVLOG(3) << __FUNCTION__ << ": Discontinuity: reprocessing frame"; |
| 561 continue; | 576 continue; |
| 562 } | 577 } |
| 563 } | 578 } |
| 564 | 579 |
| 565 // 9. Let frame end timestamp equal the sum of presentation timestamp and | 580 // 9. Let frame end timestamp equal the sum of presentation timestamp and |
| 566 // frame duration. | 581 // frame duration. |
| 567 const base::TimeDelta frame_end_timestamp = | 582 base::TimeDelta frame_end_timestamp = |
| 568 presentation_timestamp + frame_duration; | 583 presentation_timestamp + frame_duration; |
| 569 | 584 |
| 570 // 10. If presentation timestamp is less than appendWindowStart, then set | 585 // 10. If presentation timestamp is less than appendWindowStart, then set |
| 571 // the need random access point flag to true, drop the coded frame, and | 586 // the need random access point flag to true, drop the coded frame, and |
| 572 // jump to the top of the loop to start processing the next coded | 587 // jump to the top of the loop to start processing the next coded |
| 573 // frame. | 588 // frame. |
| 574 // Note: We keep the result of partial discard of a buffer that overlaps | 589 // Note: We keep the result of partial discard of a buffer that overlaps |
| 575 // |append_window_start| and does not end after |append_window_end|. | 590 // |append_window_start| and does not end after |append_window_end|. |
| 576 // 11. If frame end timestamp is greater than appendWindowEnd, then set the | 591 // 11. If frame end timestamp is greater than appendWindowEnd, then set the |
| 577 // need random access point flag to true, drop the coded frame, and jump | 592 // need random access point flag to true, drop the coded frame, and jump |
| 578 // to the top of the loop to start processing the next coded frame. | 593 // to the top of the loop to start processing the next coded frame. |
| 579 frame->set_timestamp(presentation_timestamp); | 594 frame->set_timestamp(presentation_timestamp); |
| 580 frame->SetDecodeTimestamp(decode_timestamp); | 595 frame->SetDecodeTimestamp(decode_timestamp); |
| 581 if (track_buffer->stream()->supports_partial_append_window_trimming() && | 596 if (track_buffer->stream()->supports_partial_append_window_trimming() && |
| 582 HandlePartialAppendWindowTrimming(append_window_start, | 597 HandlePartialAppendWindowTrimming(append_window_start, |
| 583 append_window_end, | 598 append_window_end, |
| 584 frame)) { | 599 frame)) { |
| 585 // If |frame| was shortened a discontinuity may exist, so treat the next | 600 // If |frame| was front-trimmed a discontinuity may exist, so treat the |
| 586 // frames appended as if they were the beginning of a new media segment. | 601 // next frames appended as if they were the beginning of a new media |
| 602 // segment. |
| 587 if (frame->timestamp() != presentation_timestamp && !sequence_mode_) | 603 if (frame->timestamp() != presentation_timestamp && !sequence_mode_) |
| 588 *new_media_segment = true; | 604 *new_media_segment = true; |
| 589 | 605 |
| 590 // |frame| has been partially trimmed or had preroll added. Though | 606 // |frame| has been partially trimmed or had preroll added. Though |
| 591 // |frame|'s duration may have changed, do not update |frame_duration| | 607 // |frame|'s duration may have changed, do not update |frame_duration| |
| 592 // here, so |track_buffer|'s last frame duration update uses original | 608 // here, so |track_buffer|'s last frame duration update uses original |
| 593 // frame duration and reduces spurious discontinuity detection. | 609 // frame duration and reduces spurious discontinuity detection. |
| 594 decode_timestamp = frame->GetDecodeTimestamp(); | 610 decode_timestamp = frame->GetDecodeTimestamp(); |
| 595 presentation_timestamp = frame->timestamp(); | 611 presentation_timestamp = frame->timestamp(); |
| 596 | 612 frame_end_timestamp = frame->timestamp() + frame->duration(); |
| 597 // The end timestamp of the frame should be unchanged. | |
| 598 DCHECK(frame_end_timestamp == presentation_timestamp + frame->duration()); | |
| 599 } | 613 } |
| 600 | 614 |
| 601 if (presentation_timestamp < append_window_start || | 615 if (presentation_timestamp < append_window_start || |
| 602 frame_end_timestamp > append_window_end) { | 616 frame_end_timestamp > append_window_end) { |
| 603 track_buffer->set_needs_random_access_point(true); | 617 track_buffer->set_needs_random_access_point(true); |
| 604 DVLOG(3) << "Dropping frame that is outside append window."; | 618 DVLOG(3) << "Dropping frame that is outside append window."; |
| 605 return true; | 619 return true; |
| 606 } | 620 } |
| 607 | 621 |
| 608 // Note: This step is relocated, versus April 1 spec, to allow append window | 622 // Note: This step is relocated, versus April 1 spec, to allow append window |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 683 DCHECK(group_end_timestamp_ >= base::TimeDelta()); | 697 DCHECK(group_end_timestamp_ >= base::TimeDelta()); |
| 684 | 698 |
| 685 return true; | 699 return true; |
| 686 } | 700 } |
| 687 | 701 |
| 688 NOTREACHED(); | 702 NOTREACHED(); |
| 689 return false; | 703 return false; |
| 690 } | 704 } |
| 691 | 705 |
| 692 } // namespace media | 706 } // namespace media |
| OLD | NEW |