OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 557 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
568 { | 568 { |
569 // According to MSE specification (https://w3c.github.io/media-source/#sourc
ebuffer-init-segment-received) step 3.1: | 569 // According to MSE specification (https://w3c.github.io/media-source/#sourc
ebuffer-init-segment-received) step 3.1: |
570 // > If more than one track for a single type are present (ie 2 audio tracks
), then the Track IDs match the ones in the first initialization segment. | 570 // > If more than one track for a single type are present (ie 2 audio tracks
), then the Track IDs match the ones in the first initialization segment. |
571 // I.e. we only need to search by TrackID if there is more than one track, o
therwise we can assume that the only | 571 // I.e. we only need to search by TrackID if there is more than one track, o
therwise we can assume that the only |
572 // track of the given type is the same one that we had in previous init segm
ents. | 572 // track of the given type is the same one that we had in previous init segm
ents. |
573 if (trackList.length() == 1) | 573 if (trackList.length() == 1) |
574 return trackList.anonymousIndexedGetter(0); | 574 return trackList.anonymousIndexedGetter(0); |
575 return trackList.getTrackById(id); | 575 return trackList.getTrackById(id); |
576 } | 576 } |
577 | 577 |
| 578 const TrackDefault* SourceBuffer::getTrackDefault(const AtomicString& trackType,
const AtomicString& byteStreamTrackId) const |
| 579 { |
| 580 // This is a helper for implementation of default track label and default tr
ack language algorithms. |
| 581 // defaultTrackLabel spec: https://w3c.github.io/media-source/#sourcebuffer-
default-track-label |
| 582 // defaultTrackLanguage spec: https://w3c.github.io/media-source/#sourcebuff
er-default-track-language |
| 583 |
| 584 // 1. If trackDefaults contains a TrackDefault object with a type attribute
equal to type and a byteStreamTrackId attribute equal to byteStreamTrackId, |
| 585 // then return the value of the label/language attribute on this matching ob
ject and abort these steps. |
| 586 // 2. If trackDefaults contains a TrackDefault object with a type attribute
equal to type and a byteStreamTrackId attribute equal to an empty string, |
| 587 // then return the value of the label/language attribute on this matching ob
ject and abort these steps. |
| 588 // 3. Return an empty string to the caller |
| 589 const TrackDefault* trackDefaultWithEmptyBytestreamId = nullptr; |
| 590 for (unsigned i = 0; i < m_trackDefaults->length(); ++i) { |
| 591 const TrackDefault* trackDefault = m_trackDefaults->item(i); |
| 592 if (trackDefault->type() != trackType) |
| 593 continue; |
| 594 if (trackDefault->byteStreamTrackID() == byteStreamTrackId) |
| 595 return trackDefault; |
| 596 if (!trackDefaultWithEmptyBytestreamId && trackDefault->byteStreamTrackI
D() == "") { |
| 597 trackDefaultWithEmptyBytestreamId = trackDefault; |
| 598 } |
| 599 } |
| 600 return trackDefaultWithEmptyBytestreamId; |
| 601 } |
| 602 |
| 603 AtomicString SourceBuffer::defaultTrackLabel(const AtomicString& trackType, cons
t AtomicString& byteStreamTrackId) const |
| 604 { |
| 605 // Spec: https://w3c.github.io/media-source/#sourcebuffer-default-track-labe
l |
| 606 const TrackDefault* trackDefault = getTrackDefault(trackType, byteStreamTrac
kId); |
| 607 return trackDefault ? AtomicString(trackDefault->label()) : ""; |
| 608 } |
| 609 |
| 610 AtomicString SourceBuffer::defaultTrackLanguage(const AtomicString& trackType, c
onst AtomicString& byteStreamTrackId) const |
| 611 { |
| 612 // Spec: https://w3c.github.io/media-source/#sourcebuffer-default-track-lang
uage |
| 613 const TrackDefault* trackDefault = getTrackDefault(trackType, byteStreamTrac
kId); |
| 614 return trackDefault ? AtomicString(trackDefault->language()) : ""; |
| 615 } |
| 616 |
578 WebVector<WebMediaPlayer::TrackId> SourceBuffer::initializationSegmentReceived(c
onst WebVector<MediaTrackInfo>& newTracks) | 617 WebVector<WebMediaPlayer::TrackId> SourceBuffer::initializationSegmentReceived(c
onst WebVector<MediaTrackInfo>& newTracks) |
579 { | 618 { |
580 WTF_LOG(Media, "SourceBuffer::initializationSegmentReceived %p tracks=%zu",
this, newTracks.size()); | 619 WTF_LOG(Media, "SourceBuffer::initializationSegmentReceived %p tracks=%zu",
this, newTracks.size()); |
581 ASSERT(m_source); | 620 ASSERT(m_source); |
582 ASSERT(m_source->mediaElement()); | 621 ASSERT(m_source->mediaElement()); |
583 ASSERT(m_updating); | 622 ASSERT(m_updating); |
584 | 623 |
585 // TODO(servolk): Implement proper 'initialization segment received' algorit
hm according to MSE spec: | 624 WebVector<WebMediaPlayer::TrackId> assignedTrackIds(newTracks.size()); |
| 625 |
| 626 if (!RuntimeEnabledFeatures::audioVideoTracksEnabled()) { |
| 627 for (size_t i = 0; i < newTracks.size(); ++i) { |
| 628 static WebMediaPlayer::TrackId nextTrackId = 0; |
| 629 assignedTrackIds[i] = ++nextTrackId; |
| 630 } |
| 631 if (!m_firstInitializationSegmentReceived) { |
| 632 m_source->setSourceBufferActive(this); |
| 633 m_firstInitializationSegmentReceived = true; |
| 634 } |
| 635 return assignedTrackIds; |
| 636 } |
| 637 |
| 638 |
| 639 // Implementation of Initialization Segment Received, see 3.5.8 at |
586 // https://w3c.github.io/media-source/#sourcebuffer-init-segment-received | 640 // https://w3c.github.io/media-source/#sourcebuffer-init-segment-received |
587 WebVector<WebMediaPlayer::TrackId> result(newTracks.size()); | 641 |
588 unsigned resultIdx = 0; | 642 // Sort newTracks into audio and video tracks to facilitate implementation |
589 for (const auto& trackInfo : newTracks) { | 643 // of subsequent steps of this algorithm. |
590 if (!RuntimeEnabledFeatures::audioVideoTracksEnabled()) { | 644 Vector<MediaTrackInfo> newAudioTracks; |
591 static WebMediaPlayer::TrackId nextTrackId = 0; | 645 Vector<MediaTrackInfo> newVideoTracks; |
592 result[resultIdx++] = ++nextTrackId; | 646 size_t resultIdx = 0; |
593 continue; | 647 for (const MediaTrackInfo& trackInfo : newTracks) { |
594 } | 648 const TrackBase* track = nullptr; |
595 | |
596 const TrackBase* trackBase = nullptr; | |
597 if (trackInfo.trackType == WebMediaPlayer::AudioTrack) { | 649 if (trackInfo.trackType == WebMediaPlayer::AudioTrack) { |
598 AudioTrack* audioTrack = nullptr; | 650 newAudioTracks.append(trackInfo); |
599 if (!m_firstInitializationSegmentReceived) { | 651 if (m_firstInitializationSegmentReceived) |
600 audioTrack = AudioTrack::create(trackInfo.byteStreamTrackId, tra
ckInfo.kind, trackInfo.label, trackInfo.language, false); | 652 track = findExistingTrackById(audioTracks(), trackInfo.byteStrea
mTrackId); |
601 SourceBufferTrackBaseSupplement::setSourceBuffer(*audioTrack, th
is); | |
602 audioTracks().add(audioTrack); | |
603 m_source->mediaElement()->audioTracks().add(audioTrack); | |
604 } else { | |
605 audioTrack = findExistingTrackById(audioTracks(), trackInfo.byte
StreamTrackId); | |
606 ASSERT(audioTrack); | |
607 } | |
608 trackBase = audioTrack; | |
609 result[resultIdx++] = audioTrack->trackId(); | |
610 } else if (trackInfo.trackType == WebMediaPlayer::VideoTrack) { | 653 } else if (trackInfo.trackType == WebMediaPlayer::VideoTrack) { |
611 VideoTrack* videoTrack = nullptr; | 654 newVideoTracks.append(trackInfo); |
612 if (!m_firstInitializationSegmentReceived) { | 655 if (m_firstInitializationSegmentReceived) |
613 videoTrack = VideoTrack::create(trackInfo.byteStreamTrackId, tra
ckInfo.kind, trackInfo.label, trackInfo.language, false); | 656 track = findExistingTrackById(videoTracks(), trackInfo.byteStrea
mTrackId); |
614 SourceBufferTrackBaseSupplement::setSourceBuffer(*videoTrack, th
is); | |
615 videoTracks().add(videoTrack); | |
616 m_source->mediaElement()->videoTracks().add(videoTrack); | |
617 } else { | |
618 videoTrack = findExistingTrackById(videoTracks(), trackInfo.byte
StreamTrackId); | |
619 ASSERT(videoTrack); | |
620 } | |
621 trackBase = videoTrack; | |
622 result[resultIdx++] = videoTrack->trackId(); | |
623 } else { | 657 } else { |
| 658 // TODO(servolk): Add handling of text tracks. |
624 NOTREACHED(); | 659 NOTREACHED(); |
625 } | 660 } |
626 (void)trackBase; | 661 |
627 #if !LOG_DISABLED | 662 if (m_firstInitializationSegmentReceived) { |
628 const char* logActionStr = m_firstInitializationSegmentReceived ? "using
existing" : "added"; | 663 if (!track) { |
629 const char* logTrackTypeStr = (trackInfo.trackType == WebMediaPlayer::Au
dioTrack) ? "audio" : "video"; | 664 WTF_LOG(Media, "SourceBuffer(%p)::initializationSegmentReceived
failed: tracks mismatch the first init segment.", this); |
630 WTF_LOG(Media, "Tracks (sb=%p): %s %sTrack %p trackId=%d id=%s label=%s
lang=%s", this, logActionStr, logTrackTypeStr, trackBase, trackBase->trackId(),
trackBase->id().utf8().data(), trackBase->label().utf8().data(), trackBase->lang
uage().utf8().data()); | 665 appendError(true); |
631 #endif | 666 return assignedTrackIds; |
632 } | 667 } |
633 | 668 assignedTrackIds[resultIdx++] = track->trackId(); |
| 669 } |
| 670 } |
| 671 |
| 672 // 1. Update the duration attribute if it currently equals NaN: |
| 673 // TODO(servolk): Pass also stream duration into initSegmentReceived. |
| 674 |
| 675 // 2. If the initialization segment has no audio, video, or text tracks, the
n run the append error algorithm with the decode error parameter set to true and
abort these steps. |
| 676 if (newTracks.size() == 0) { |
| 677 WTF_LOG(Media, "SourceBuffer(%p)::initializationSegmentReceived failed,
no tracks found in the init segment.", this); |
| 678 appendError(true); |
| 679 return assignedTrackIds; |
| 680 } |
| 681 |
| 682 // 3. If the first initialization segment received flag is true, then run th
e following steps: |
| 683 if (m_firstInitializationSegmentReceived) { |
| 684 // 3.1 Verify the following properties. If any of the checks fail then r
un the append error algorithm with the decode error parameter set to true and ab
ort these steps. |
| 685 bool tracksMatchFirstInitSegment = true; |
| 686 // - The number of audio, video, and text tracks match what was in the f
irst initialization segment. |
| 687 if (newAudioTracks.size() != audioTracks().length() || newVideoTracks.si
ze() != videoTracks().length()) { |
| 688 tracksMatchFirstInitSegment = false; |
| 689 } |
| 690 // - The codecs for each track, match what was specified in the first in
itialization segment. |
| 691 // This is currently done in MediaSourceState::OnNewConfigs. |
| 692 // - If more than one track for a single type are present (ie 2 audio tr
acks), then the Track IDs match the ones in the first initialization segment. |
| 693 if (tracksMatchFirstInitSegment && newAudioTracks.size() > 1) { |
| 694 for (size_t i = 0; i < newAudioTracks.size(); ++i) { |
| 695 const String& newTrackId = newVideoTracks[i].byteStreamTrackId; |
| 696 if (newTrackId != audioTracks().anonymousIndexedGetter(i)->id())
{ |
| 697 tracksMatchFirstInitSegment = false; |
| 698 break; |
| 699 } |
| 700 } |
| 701 } |
| 702 |
| 703 if (tracksMatchFirstInitSegment && newVideoTracks.size() > 1) { |
| 704 for (size_t i = 0; i < newVideoTracks.size(); ++i) { |
| 705 const String& newTrackId = newVideoTracks[i].byteStreamTrackId; |
| 706 if (newTrackId != videoTracks().anonymousIndexedGetter(i)->id())
{ |
| 707 tracksMatchFirstInitSegment = false; |
| 708 break; |
| 709 } |
| 710 } |
| 711 } |
| 712 |
| 713 if (!tracksMatchFirstInitSegment) { |
| 714 WTF_LOG(Media, "SourceBuffer(%p)::initializationSegmentReceived fail
ed: tracks mismatch the first init segment.", this); |
| 715 appendError(true); |
| 716 return assignedTrackIds; |
| 717 } |
| 718 |
| 719 // 3.2 Add the appropriate track descriptions from this initialization s
egment to each of the track buffers. |
| 720 // This is done in Chromium code in stream parsers and demuxer implement
ations. |
| 721 |
| 722 // 3.3 Set the need random access point flag on all track buffers to tru
e. |
| 723 // This is done in Chromium code, see MediaSourceState::OnNewConfigs. |
| 724 } |
| 725 |
| 726 // 4. Let active track flag equal false. |
| 727 m_activeTrack = false; |
| 728 |
| 729 // 5. If the first initialization segment received flag is false, then run t
he following steps: |
634 if (!m_firstInitializationSegmentReceived) { | 730 if (!m_firstInitializationSegmentReceived) { |
635 // 5. If active track flag equals true, then run the following steps: | 731 // 5.1 If the initialization segment contains tracks with codecs the use
r agent does not support, then run the append error algorithm with the decode er
ror parameter set to true and abort these steps. |
636 // 5.1. Add this SourceBuffer to activeSourceBuffers. | 732 // This is done in Chromium code, see MediaSourceState::OnNewConfigs. |
637 // 5.2. Queue a task to fire a simple event named addsourcebuffer at | 733 |
| 734 // 5.2 For each audio track in the initialization segment, run following
steps: |
| 735 for (const MediaTrackInfo& trackInfo : newAudioTracks) { |
| 736 // 5.2.1 Let audio byte stream track ID be the Track ID for the curr
ent track being processed. |
| 737 const auto& byteStreamTrackId = trackInfo.byteStreamTrackId; |
| 738 // 5.2.2 Let audio language be a BCP 47 language tag for the languag
e specified in the initialization segment for this track or an empty string if n
o language info is present. |
| 739 WebString language = trackInfo.language; |
| 740 // 5.2.3 If audio language equals an empty string or the 'und' BCP 4
7 value, then run the default track language algorithm with byteStreamTrackId se
t to |
| 741 // audio byte stream track ID and type set to "audio" and assign the
value returned by the algorithm to audio language. |
| 742 if (language.isEmpty() || language == "und") |
| 743 language = defaultTrackLanguage(TrackDefault::audioKeyword(), by
teStreamTrackId); |
| 744 // 5.2.4 Let audio label be a label specified in the initialization
segment for this track or an empty string if no label info is present. |
| 745 WebString label = trackInfo.label; |
| 746 // 5.3.5 If audio label equals an empty string, then run the default
track label algorithm with byteStreamTrackId set to audio byte stream track ID
and |
| 747 // type set to "audio" and assign the value returned by the algorith
m to audio label. |
| 748 if (label.isEmpty()) |
| 749 label = defaultTrackLabel(TrackDefault::audioKeyword(), byteStre
amTrackId); |
| 750 // 5.2.6 Let audio kinds be an array of kind strings specified in th
e initialization segment for this track or an empty array if no kind information
is provided. |
| 751 const auto& kind = trackInfo.kind; |
| 752 // 5.2.7 TODO(servolk): Implement track kind processing. |
| 753 // 5.2.8.2 Let new audio track be a new AudioTrack object. |
| 754 AudioTrack* audioTrack = AudioTrack::create(byteStreamTrackId, kind,
label, language, false); |
| 755 SourceBufferTrackBaseSupplement::setSourceBuffer(*audioTrack, this); |
| 756 assignedTrackIds[resultIdx++] = audioTrack->trackId(); |
| 757 // 5.2.8.7 If audioTracks.length equals 0, then run the following st
eps: |
| 758 if (audioTracks().length() == 0) { |
| 759 // 5.2.8.7.1 Set the enabled property on new audio track to true
. |
| 760 audioTrack->setEnabled(true); |
| 761 // 5.2.8.7.2 Set active track flag to true. |
| 762 m_activeTrack = true; |
| 763 } |
| 764 // 5.2.8.8 Add new audio track to the audioTracks attribute on this
SourceBuffer object. |
| 765 // 5.2.8.9 Queue a task to fire a trusted event named addtrack, that
does not bubble and is not cancelable, and that uses the TrackEvent interface,
at the AudioTrackList object referenced by the audioTracks attribute on this Sou
rceBuffer object. |
| 766 audioTracks().add(audioTrack); |
| 767 // 5.2.8.10 Add new audio track to the audioTracks attribute on the
HTMLMediaElement. |
| 768 // 5.2.8.11 Queue a task to fire a trusted event named addtrack, tha
t does not bubble and is not cancelable, and that uses the TrackEvent interface,
at the AudioTrackList object referenced by the audioTracks attribute on the HTM
LMediaElement. |
| 769 m_source->mediaElement()->audioTracks().add(audioTrack); |
| 770 } |
| 771 |
| 772 // 5.3. For each video track in the initialization segment, run followin
g steps: |
| 773 for (const MediaTrackInfo& trackInfo : newVideoTracks) { |
| 774 // 5.3.1 Let video byte stream track ID be the Track ID for the curr
ent track being processed. |
| 775 const auto& byteStreamTrackId = trackInfo.byteStreamTrackId; |
| 776 // 5.3.2 Let video language be a BCP 47 language tag for the languag
e specified in the initialization segment for this track or an empty string if n
o language info is present. |
| 777 WebString language = trackInfo.language; |
| 778 // 5.3.3 If video language equals an empty string or the 'und' BCP 4
7 value, then run the default track language algorithm with byteStreamTrackId se
t to |
| 779 // video byte stream track ID and type set to "video" and assign the
value returned by the algorithm to video language. |
| 780 if (language.isEmpty() || language == "und") |
| 781 language = defaultTrackLanguage(TrackDefault::videoKeyword(), by
teStreamTrackId); |
| 782 // 5.3.4 Let video label be a label specified in the initialization
segment for this track or an empty string if no label info is present. |
| 783 WebString label = trackInfo.label; |
| 784 // 5.3.5 If video label equals an empty string, then run the default
track label algorithm with byteStreamTrackId set to video byte stream track ID
and |
| 785 // type set to "video" and assign the value returned by the algorith
m to video label. |
| 786 if (label.isEmpty()) |
| 787 label = defaultTrackLabel(TrackDefault::videoKeyword(), byteStre
amTrackId); |
| 788 // 5.3.6 Let video kinds be an array of kind strings specified in th
e initialization segment for this track or an empty array if no kind information
is provided. |
| 789 const auto& kind = trackInfo.kind; |
| 790 // 5.3.7 TODO(servolk): Implement track kind processing. |
| 791 // 5.3.8.2 Let new video track be a new VideoTrack object. |
| 792 VideoTrack* videoTrack = VideoTrack::create(byteStreamTrackId, kind,
label, language, false); |
| 793 SourceBufferTrackBaseSupplement::setSourceBuffer(*videoTrack, this); |
| 794 assignedTrackIds[resultIdx++] = videoTrack->trackId(); |
| 795 // 5.3.8.7 If videoTracks.length equals 0, then run the following st
eps: |
| 796 if (videoTracks().length() == 0) { |
| 797 // 5.3.8.7.1 Set the selected property on new audio track to tru
e. |
| 798 videoTrack->setSelected(true); |
| 799 // 5.3.8.7.2 Set active track flag to true. |
| 800 m_activeTrack = true; |
| 801 } |
| 802 // 5.3.8.8 Add new video track to the videoTracks attribute on this
SourceBuffer object. |
| 803 // 5.3.8.9 Queue a task to fire a trusted event named addtrack, that
does not bubble and is not cancelable, and that uses the TrackEvent interface,
at the VideoTrackList object referenced by the videoTracks attribute on this Sou
rceBuffer object. |
| 804 videoTracks().add(videoTrack); |
| 805 // 5.3.8.10 Add new video track to the videoTracks attribute on the
HTMLMediaElement. |
| 806 // 5.3.8.11 Queue a task to fire a trusted event named addtrack, tha
t does not bubble and is not cancelable, and that uses the TrackEvent interface,
at the VideoTrackList object referenced by the videoTracks attribute on the HTM
LMediaElement. |
| 807 m_source->mediaElement()->videoTracks().add(videoTrack); |
| 808 } |
| 809 |
| 810 // 5.4 TODO(servolk): Add text track processing here. |
| 811 |
| 812 // 5.5 If active track flag equals true, then run the following steps: |
638 // activesourcebuffers. | 813 // activesourcebuffers. |
639 m_source->setSourceBufferActive(this); | 814 if (m_activeTrack) { |
640 | 815 // 5.5.1 Add this SourceBuffer to activeSourceBuffers. |
641 // 6. Set first initialization segment received flag to true. | 816 // 5.5.2 Queue a task to fire a simple event named addsourcebuffer a
t activeSourceBuffers |
| 817 m_source->setSourceBufferActive(this); |
| 818 } |
| 819 |
| 820 // 5.6. Set first initialization segment received flag to true. |
642 m_firstInitializationSegmentReceived = true; | 821 m_firstInitializationSegmentReceived = true; |
643 } | 822 } |
644 | 823 |
645 return result; | 824 return assignedTrackIds; |
646 } | 825 } |
647 | 826 |
648 bool SourceBuffer::hasPendingActivity() const | 827 bool SourceBuffer::hasPendingActivity() const |
649 { | 828 { |
650 return m_source; | 829 return m_source; |
651 } | 830 } |
652 | 831 |
653 void SourceBuffer::suspend() | 832 void SourceBuffer::suspend() |
654 { | 833 { |
655 m_appendBufferAsyncPartRunner->suspend(); | 834 m_appendBufferAsyncPartRunner->suspend(); |
(...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1023 visitor->trace(m_removeAsyncPartRunner); | 1202 visitor->trace(m_removeAsyncPartRunner); |
1024 visitor->trace(m_appendStreamAsyncPartRunner); | 1203 visitor->trace(m_appendStreamAsyncPartRunner); |
1025 visitor->trace(m_stream); | 1204 visitor->trace(m_stream); |
1026 visitor->trace(m_audioTracks); | 1205 visitor->trace(m_audioTracks); |
1027 visitor->trace(m_videoTracks); | 1206 visitor->trace(m_videoTracks); |
1028 EventTargetWithInlineData::trace(visitor); | 1207 EventTargetWithInlineData::trace(visitor); |
1029 ActiveDOMObject::trace(visitor); | 1208 ActiveDOMObject::trace(visitor); |
1030 } | 1209 } |
1031 | 1210 |
1032 } // namespace blink | 1211 } // namespace blink |
OLD | NEW |