|
Lines 375-393
void AppendPipeline::handleEndOfAppend()
a/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp_sec1
|
| 375 |
sourceBufferPrivate().didReceiveAllPendingSamples(); |
375 |
sourceBufferPrivate().didReceiveAllPendingSamples(); |
| 376 |
} |
376 |
} |
| 377 |
|
377 |
|
|
|
378 |
static GstClockTime bufferTimeToStreamTimeClamped(const GstSegment* segment, GstClockTime bufferTime) |
| 379 |
{ |
| 380 |
guint64 streamTime; |
| 381 |
int result = gst_segment_to_stream_time_full(segment, GST_FORMAT_TIME, bufferTime, &streamTime); |
| 382 |
if (!result) { |
| 383 |
GST_ERROR("Couldn't map buffer time %" GST_TIME_FORMAT " to segment %" GST_PTR_FORMAT, GST_TIME_ARGS(bufferTime), segment); |
| 384 |
return bufferTime; |
| 385 |
} |
| 386 |
if (result < 0) |
| 387 |
return 0; // Clamp negative timestamps down to zero. |
| 388 |
return streamTime; |
| 389 |
} |
| 390 |
|
| 378 |
void AppendPipeline::appsinkNewSample(const Track& track, GRefPtr<GstSample>&& sample) |
391 |
void AppendPipeline::appsinkNewSample(const Track& track, GRefPtr<GstSample>&& sample) |
| 379 |
{ |
392 |
{ |
| 380 |
ASSERT(isMainThread()); |
393 |
ASSERT(isMainThread()); |
| 381 |
|
394 |
|
| 382 |
if (UNLIKELY(!gst_sample_get_buffer(sample.get()))) { |
395 |
{ |
| 383 |
GST_WARNING("Received sample without buffer from appsink."); |
396 |
GstBuffer* buffer = gst_sample_get_buffer(sample.get()); |
| 384 |
return; |
397 |
if (UNLIKELY(!buffer)) { |
| 385 |
} |
398 |
GST_WARNING("Received sample without buffer from appsink."); |
|
|
399 |
return; |
| 400 |
} |
| 386 |
|
401 |
|
| 387 |
if (!GST_BUFFER_PTS_IS_VALID(gst_sample_get_buffer(sample.get()))) { |
402 |
if (!GST_BUFFER_PTS_IS_VALID(buffer)) { |
| 388 |
// When demuxing Vorbis, matroskademux creates several PTS-less frames with header information. We don't need those. |
403 |
// When demuxing Vorbis, matroskademux creates several PTS-less frames with header information. We don't need those. |
| 389 |
GST_DEBUG("Ignoring sample without PTS: %" GST_PTR_FORMAT, gst_sample_get_buffer(sample.get())); |
404 |
GST_DEBUG("Ignoring sample without PTS: %" GST_PTR_FORMAT, gst_sample_get_buffer(sample.get())); |
| 390 |
return; |
405 |
return; |
|
|
406 |
} |
| 407 |
|
| 408 |
GstSegment* segment = gst_sample_get_segment(sample.get()); |
| 409 |
bool hasMappedTime = false; |
| 410 |
GstClockTime pts = GST_BUFFER_PTS(buffer); |
| 411 |
GstClockTime dts = GST_BUFFER_DTS(buffer); |
| 412 |
GstClockTime duration = GST_BUFFER_DURATION(buffer); |
| 413 |
if (segment && (segment->time || segment->start)) { |
| 414 |
// MP4 has the concept of edit lists, where some buffer time needs to be offsetted, often very slightly, |
| 415 |
// to get exact timestamps. |
| 416 |
pts = bufferTimeToStreamTimeClamped(segment, GST_BUFFER_PTS(buffer)); |
| 417 |
dts = bufferTimeToStreamTimeClamped(segment, GST_BUFFER_DTS(buffer)); |
| 418 |
GST_TRACE_OBJECT(track.appsinkPad.get(), "Mapped buffer to segment, PTS %" GST_TIME_FORMAT " -> %" GST_TIME_FORMAT " DTS %" GST_TIME_FORMAT " -> %" GST_TIME_FORMAT, |
| 419 |
GST_TIME_ARGS(GST_BUFFER_PTS(buffer)), GST_TIME_ARGS(pts), GST_TIME_ARGS(GST_BUFFER_DTS(buffer)), GST_TIME_ARGS(dts)); |
| 420 |
hasMappedTime = true; |
| 421 |
} else if (!dts && pts > 0 && pts <= 100'000'000) { |
| 422 |
// Because a track presentation time starting at some close to zero, but not exactly zero time can cause unexpected |
| 423 |
// results for applications, we extend the duration of this first sample to the left so that it starts at zero. |
| 424 |
// This is relevant for files that should have an edit list but don't, or when using GStreamer < 1.16, where |
| 425 |
// edit lists are not parsed in push-mode. |
| 426 |
|
| 427 |
GST_DEBUG("Extending first sample of track '%s' to make it start at PTS=0 %" GST_PTR_FORMAT, track.trackId.string().utf8().data(), buffer); |
| 428 |
duration += pts; |
| 429 |
pts = 0; |
| 430 |
hasMappedTime = true; |
| 431 |
} |
| 432 |
|
| 433 |
if (hasMappedTime) { |
| 434 |
sample = adoptGRef(gst_sample_make_writable(sample.leakRef())); |
| 435 |
GRefPtr<GstBuffer> newBuffer = gst_sample_get_buffer(sample.get()); |
| 436 |
gst_sample_set_buffer(sample.get(), nullptr); |
| 437 |
newBuffer = adoptGRef(gst_buffer_make_writable(newBuffer.leakRef())); |
| 438 |
GST_BUFFER_PTS(newBuffer.get()) = pts; |
| 439 |
GST_BUFFER_DTS(newBuffer.get()) = dts; |
| 440 |
GST_BUFFER_DURATION(newBuffer.get()) = duration; |
| 441 |
gst_sample_set_buffer(sample.get(), newBuffer.leakRef()); |
| 442 |
} |
| 391 |
} |
443 |
} |
| 392 |
|
444 |
|
| 393 |
auto mediaSample = MediaSampleGStreamer::create(WTFMove(sample), track.presentationSize, track.trackId); |
445 |
auto mediaSample = MediaSampleGStreamer::create(WTFMove(sample), track.presentationSize, track.trackId); |
|
Lines 399-423
void AppendPipeline::appsinkNewSample(const Track& track, GRefPtr<GstSample>&& s
a/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp_sec2
|
| 399 |
mediaSample->duration().toString().utf8().data(), |
451 |
mediaSample->duration().toString().utf8().data(), |
| 400 |
mediaSample->presentationSize().width(), mediaSample->presentationSize().height()); |
452 |
mediaSample->presentationSize().width(), mediaSample->presentationSize().height()); |
| 401 |
|
453 |
|
| 402 |
// Hack, rework when GStreamer >= 1.16 becomes a requirement: |
|
|
| 403 |
// We're not applying edit lists. GStreamer < 1.16 doesn't emit the correct segments to do so. |
| 404 |
// GStreamer fix in https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/commit/c2a0da8096009f0f99943f78dc18066965be60f9 |
| 405 |
// Also, in order to apply them we would need to convert the timestamps to stream time, which we're not currently |
| 406 |
// doing for consistency between GStreamer versions. |
| 407 |
// |
| 408 |
// In consequence, the timestamps we're handling here are unedited track time. In track time, the first sample is |
| 409 |
// guaranteed to have DTS == 0, but in the case of streams with B-frames, often PTS > 0. Edit lists fix this by |
| 410 |
// offsetting all timestamps by that amount in movie time, but we can't do that if we don't have access to them. |
| 411 |
// (We could assume the track PTS of the sample with track DTS = 0 is the offset, but we don't have any guarantee |
| 412 |
// we will get appended that sample first, or ever). |
| 413 |
// |
| 414 |
// Because a track presentation time starting at some close to zero, but not exactly zero time can cause unexpected |
| 415 |
// results for applications, we extend the duration of this first sample to the left so that it starts at zero. |
| 416 |
if (mediaSample->decodeTime() == MediaTime::zeroTime() && mediaSample->presentationTime() > MediaTime::zeroTime() && mediaSample->presentationTime() <= MediaTime(1, 10)) { |
| 417 |
GST_DEBUG("Extending first sample to make it start at PTS=0"); |
| 418 |
mediaSample->extendToTheBeginning(); |
| 419 |
} |
| 420 |
|
| 421 |
m_sourceBufferPrivate.didReceiveSample(mediaSample.get()); |
454 |
m_sourceBufferPrivate.didReceiveSample(mediaSample.get()); |
| 422 |
} |
455 |
} |
| 423 |
|
456 |
|