Changeset 273461 in webkit


Ignore:
Timestamp:
Feb 24, 2021 4:57:53 PM (17 months ago)
Author:
commit-queue@webkit.org
Message:

[MSE] Media segment is incorrectly dropped when using negative timestampOffset or when source buffer appendWindow is set.
https://bugs.webkit.org/show_bug.cgi?id=222260

Patch by Jean-Yves Avenard <Jean-Yves Avenard> on 2021-02-24
Reviewed by Eric Carlson.

Source/WebCore:

Test: media/media-source/media-source-timestampoffset-trim.html

CoreMedia packs multiple audio frames together into a single CMSampleBuffer,
this allows for faster processing and easier insertion into the track buffer tree.
However, per mediasoure spec [1], a frame is to be dropped according to
its start time and duration. So if only the beginning of the MediaSample
was to be dropped, we would have incorrectly dropped the lot.
We now split the MediaSample if it is going to be dropped to ensure that
all usable content is inserted into the track buffer.
Audio splicing isn't done yet, but this gets us closer to it.

[1] https://w3c.github.io/media-source/#sourcebuffer-coded-frame-processing

  • platform/graphics/SourceBufferPrivate.cpp:

(WebCore::SourceBufferPrivate::didReceiveSample):

  • platform/graphics/SourceBufferPrivate.h:
  • platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:

(WebCore::SourceBufferPrivateAVFObjC::didParseInitializationData):
(WebCore::SourceBufferPrivateAVFObjC::didProvideMediaDataForTrackId):

  • platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.cpp:

(WebCore::SourceBufferPrivateGStreamer::didReceiveSample):

  • platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.h:

LayoutTests:

  • media/media-source/media-source-timestampoffset-trim.html:
Location:
trunk
Files:
2 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r273444 r273461  
     12021-02-24  Jean-Yves Avenard  <jya@apple.com>
     2
     3        [MSE] Media segment is incorrectly dropped when using negative timestampOffset or when source buffer appendWindow is set.
     4        https://bugs.webkit.org/show_bug.cgi?id=222260
     5
     6        Reviewed by Eric Carlson.
     7
     8        * media/media-source/media-source-timestampoffset-trim.html:
     9
    1102021-02-24  Chris Dumez  <cdumez@apple.com>
    211
  • trunk/Source/WebCore/ChangeLog

    r273453 r273461  
     12021-02-24  Jean-Yves Avenard  <jya@apple.com>
     2
     3        [MSE] Media segment is incorrectly dropped when using negative timestampOffset or when source buffer appendWindow is set.
     4        https://bugs.webkit.org/show_bug.cgi?id=222260
     5
     6        Reviewed by Eric Carlson.
     7
     8        Test: media/media-source/media-source-timestampoffset-trim.html
     9
     10        CoreMedia packs multiple audio frames together into a single CMSampleBuffer,
     11        this allows for faster processing and easier insertion into the track buffer tree.
     12        However, per mediasoure spec [1], a frame is to be dropped according to
     13        its start time and duration. So if only the beginning of the MediaSample
     14        was to be dropped, we would have incorrectly dropped the lot.
     15        We now split the MediaSample if it is going to be dropped to ensure that
     16        all usable content is inserted into the track buffer.
     17        Audio splicing isn't done yet, but this gets us closer to it.
     18
     19        [1] https://w3c.github.io/media-source/#sourcebuffer-coded-frame-processing
     20
     21        * platform/graphics/SourceBufferPrivate.cpp:
     22        (WebCore::SourceBufferPrivate::didReceiveSample):
     23        * platform/graphics/SourceBufferPrivate.h:
     24        * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
     25        (WebCore::SourceBufferPrivateAVFObjC::didParseInitializationData):
     26        (WebCore::SourceBufferPrivateAVFObjC::didProvideMediaDataForTrackId):
     27        * platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.cpp:
     28        (WebCore::SourceBufferPrivateGStreamer::didReceiveSample):
     29        * platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.h:
     30
    1312021-02-24  Chris Dumez  <cdumez@apple.com>
    232
  • trunk/Source/WebCore/platform/graphics/SourceBufferPrivate.cpp

    r272696 r273461  
    817817}
    818818
    819 void SourceBufferPrivate::didReceiveSample(MediaSample& sample)
     819void SourceBufferPrivate::didReceiveSample(Ref<MediaSample>&& originalSample)
    820820{
    821821    if (!m_isAttached)
     
    843843    // 1. For each coded frame in the media segment run the following steps:
    844844    // 1.1. Loop Top
     845
     846    Ref<MediaSample> sample = WTFMove(originalSample);
     847
    845848    do {
    846849        MediaTime presentationTimestamp;
     
    851854        // 1.2 Let frame duration be a double precision floating point representation of the coded frame's
    852855        // duration in seconds.
    853         MediaTime frameDuration = sample.duration();
     856        MediaTime frameDuration = sample->duration();
    854857
    855858        if (m_shouldGenerateTimestamps) {
     
    866869            // 1. Let presentation timestamp be a double precision floating point representation of
    867870            // the coded frame's presentation timestamp in seconds.
    868             presentationTimestamp = sample.presentationTime();
     871            presentationTimestamp = sample->presentationTime();
    869872
    870873            // 2. Let decode timestamp be a double precision floating point representation of the coded frame's
    871874            // decode timestamp in seconds.
    872             decodeTimestamp = sample.decodeTime();
     875            decodeTimestamp = sample->decodeTime();
    873876        }
    874877
     
    896899        // NOTE: this is out-of-order, but we need TrackBuffer to be able to cache the results of timestamp offset rounding
    897900        // 1.5 Let track buffer equal the track buffer that the coded frame will be added to.
    898         AtomString trackID = sample.trackID();
     901        AtomString trackID = sample->trackID();
    899902        auto it = m_trackBufferMap.find(trackID);
    900903        if (it == m_trackBufferMap.end()) {
     
    976979        if (m_appendMode == SourceBufferAppendMode::Sequence) {
    977980            // Use the generated timestamps instead of the sample's timestamps.
    978             sample.setTimestamps(presentationTimestamp, decodeTimestamp);
     981            sample->setTimestamps(presentationTimestamp, decodeTimestamp);
    979982        } else if (trackBuffer.roundedTimestampOffset) {
    980983            // Reflect the timestamp offset into the sample.
    981             sample.offsetTimestampsBy(trackBuffer.roundedTimestampOffset);
    982         }
    983 
    984         DEBUG_LOG(LOGIDENTIFIER, sample);
     984            sample->offsetTimestampsBy(trackBuffer.roundedTimestampOffset);
     985        }
     986
     987        DEBUG_LOG(LOGIDENTIFIER, sample.get());
    985988
    986989        // 1.7 Let frame end timestamp equal the sum of presentation timestamp and frame duration.
     
    994997        // the next coded frame.
    995998        if (presentationTimestamp < m_appendWindowStart || frameEndTimestamp > m_appendWindowEnd) {
     999            // 1.8 Note.
     1000            // Some implementations MAY choose to collect some of these coded frames with presentation
     1001            // timestamp less than appendWindowStart and use them to generate a splice at the first coded
     1002            // frame that has a presentation timestamp greater than or equal to appendWindowStart even if
     1003            // that frame is not a random access point. Supporting this requires multiple decoders or
     1004            // faster than real-time decoding so for now this behavior will not be a normative
     1005            // requirement.
     1006            // 1.9 Note.
     1007            // Some implementations MAY choose to collect coded frames with presentation timestamp less
     1008            // than appendWindowEnd and frame end timestamp greater than appendWindowEnd and use them to
     1009            // generate a splice across the portion of the collected coded frames within the append
     1010            // window at time of collection, and the beginning portion of later processed frames which
     1011            // only partially overlap the end of the collected coded frames. Supporting this requires
     1012            // multiple decoders or faster than real-time decoding so for now this behavior will not be a
     1013            // normative requirement. In conjunction with collecting coded frames that span
     1014            // appendWindowStart, implementations MAY thus support gapless audio splicing.
     1015            // Audio MediaSamples are typically made of packed audio samples. Trim sample to make it fit within the appendWindow.
     1016            if (sample->isDivisable()) {
     1017                std::pair<RefPtr<MediaSample>, RefPtr<MediaSample>> replacementSamples = sample->divide(m_appendWindowStart);
     1018                if (replacementSamples.second) {
     1019                    replacementSamples = replacementSamples.second->divide(m_appendWindowEnd);
     1020                    if (replacementSamples.first) {
     1021                        sample = replacementSamples.first.releaseNonNull();
     1022                        if (m_appendMode != SourceBufferAppendMode::Sequence && trackBuffer.roundedTimestampOffset)
     1023                            sample->offsetTimestampsBy(-trackBuffer.roundedTimestampOffset);
     1024                        continue;
     1025                    }
     1026                }
     1027            }
    9961028            trackBuffer.needRandomAccessFlag = true;
    9971029            m_client->sourceBufferPrivateDidDropSample();
     
    10141046            // 1.11.1 If the coded frame is not a random access point, then drop the coded frame and jump
    10151047            // to the top of the loop to start processing the next coded frame.
    1016             if (!sample.isSync()) {
     1048            if (!sample->isSync()) {
    10171049                m_client->sourceBufferPrivateDidDropSample();
    10181050                return;
     
    10821114        // the how the MSE specification should handlie this secnario.
    10831115        do {
    1084             if (!sample.isSync())
     1116            if (!sample->isSync())
    10851117                break;
    10861118
    1087             DecodeOrderSampleMap::KeyType decodeKey(sample.decodeTime(), sample.presentationTime());
     1119            DecodeOrderSampleMap::KeyType decodeKey(sample->decodeTime(), sample->presentationTime());
    10881120            auto nextSampleInDecodeOrder = trackBuffer.samples.decodeOrder().findSampleAfterDecodeKey(decodeKey);
    10891121            if (nextSampleInDecodeOrder == trackBuffer.samples.decodeOrder().end())
     
    11521184            // timestamp < presentationTime, but whose decode timestamp >= decodeTime. These will eventually cause
    11531185            // a decode error if left in place, so remove these samples as well.
    1154             DecodeOrderSampleMap::KeyType decodeKey(sample.decodeTime(), sample.presentationTime());
     1186            DecodeOrderSampleMap::KeyType decodeKey(sample->decodeTime(), sample->presentationTime());
    11551187            auto samplesWithHigherDecodeTimes = trackBuffer.samples.decodeOrder().findSamplesBetweenDecodeKeys(decodeKey, erasedSamples.decodeOrder().begin()->first);
    11561188            if (samplesWithHigherDecodeTimes.first != samplesWithHigherDecodeTimes.second)
     
    11981230        // If the frame is after the discontinuity boundary, the enqueueing algorithm will hold it there until samples
    11991231        // with earlier timestamps are enqueued. The decode queue is not FIFO, but rather an ordered map.
    1200         DecodeOrderSampleMap::KeyType decodeKey(sample.decodeTime(), sample.presentationTime());
     1232        DecodeOrderSampleMap::KeyType decodeKey(sample->decodeTime(), sample->presentationTime());
    12011233        if (trackBuffer.lastEnqueuedDecodeKey.first.isInvalid() || decodeKey > trackBuffer.lastEnqueuedDecodeKey) {
    1202             trackBuffer.decodeQueue.insert(DecodeOrderSampleMap::MapType::value_type(decodeKey, &sample));
    1203 
    1204             if (trackBuffer.minimumEnqueuedPresentationTime.isValid() && sample.presentationTime() < trackBuffer.minimumEnqueuedPresentationTime)
     1234            trackBuffer.decodeQueue.insert(DecodeOrderSampleMap::MapType::value_type(decodeKey, &sample.get()));
     1235
     1236            if (trackBuffer.minimumEnqueuedPresentationTime.isValid() && sample->presentationTime() < trackBuffer.minimumEnqueuedPresentationTime)
    12051237                trackBuffer.needsMinimumUpcomingPresentationTimeUpdating = true;
    12061238        }
  • trunk/Source/WebCore/platform/graphics/SourceBufferPrivate.h

    r272632 r273461  
    162162    void reenqueSamples(const AtomString& trackID);
    163163    WEBCORE_EXPORT void didReceiveInitializationSegment(SourceBufferPrivateClient::InitializationSegment&&, CompletionHandler<void()>&&);
    164     WEBCORE_EXPORT void didReceiveSample(MediaSample&);
     164    WEBCORE_EXPORT void didReceiveSample(Ref<MediaSample>&&);
    165165    void provideMediaData(const AtomString& trackID);
    166166    uint64_t totalTrackBufferSizeInBytes() const;
  • trunk/Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm

    r272439 r273461  
    410410                if (trackId == m_enabledVideoTrackID || m_audioRenderers.contains(trackId)) {
    411411                    DEBUG_LOG(LOGIDENTIFIER, mediaSample.get());
    412                     didReceiveSample(mediaSample);
     412                    didReceiveSample(WTFMove(mediaSample));
    413413                }
    414414            }
     
    451451
    452452    DEBUG_LOG(LOGIDENTIFIER, mediaSample.get());
    453     didReceiveSample(mediaSample);
     453    didReceiveSample(WTFMove(mediaSample));
    454454}
    455455
  • trunk/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.cpp

    r271056 r273461  
    187187}
    188188
    189 void SourceBufferPrivateGStreamer::didReceiveSample(MediaSample& sample)
    190 {
    191     SourceBufferPrivate::didReceiveSample(sample);
     189void SourceBufferPrivateGStreamer::didReceiveSample(Ref<MediaSample>&& sample)
     190{
     191    SourceBufferPrivate::didReceiveSample(WTFMove(sample));
    192192}
    193193
  • trunk/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.h

    r270765 r273461  
    7474
    7575    void didReceiveInitializationSegment(SourceBufferPrivateClient::InitializationSegment&&, CompletionHandler<void()>&&);
    76     void didReceiveSample(MediaSample&);
     76    void didReceiveSample(Ref<MediaSample>&&);
    7777    void didReceiveAllPendingSamples();
    7878    void appendParsingFailed();
Note: See TracChangeset for help on using the changeset viewer.