gecko-dev/dom/media/VideoStreamTrack.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

101 lines
3.5 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "VideoStreamTrack.h"
#include "MediaStreamGraph.h"
#include "MediaStreamListener.h"
#include "nsContentUtils.h"
#include "VideoFrameContainer.h"
namespace mozilla {
class VideoOutput : public DirectMediaStreamTrackListener {
protected:
virtual ~VideoOutput() = default;
public:
explicit VideoOutput(VideoFrameContainer* aContainer)
: mVideoFrameContainer(aContainer) {}
void NotifyRealtimeTrackData(MediaStreamGraph* aGraph,
StreamTime aTrackOffset,
const MediaSegment& aMedia) override {
MOZ_ASSERT(aMedia.GetType() == MediaSegment::VIDEO);
const VideoSegment& video = static_cast<const VideoSegment&>(aMedia);
Bug 1506093 - Fix DecodedStream A/V sync. r=padenot DecodedStream sends video to its video tracks by initially buffering a set of images, then appending future ones by adding them one by one. A long time ago we refactored how MediaStreamGraph sends images to the screen, i.e., to an ImageContainer. It used to send all future frames to ImageContainer::SetCurrentFrames each time it sent something. After the refactor we just forward any new frame from a direct listener to ImageContainer::SetCurrentFrames. So in case DecodedStream has already sent 10 future frames to its track, and sends another, we end up calling ImageContainer::SetCurrentFrames(frame11). However, this is not how ImageContainer works. The refactor was wrong. Even though the timestamp for frame11 is after a previously buffered frame, it will be ignored. SetCurrentFrames wipes any previously set frames. Hence the word "Current" in its name. This patch largely restores the old behaviour by adding a thin buffering layer between the MSG (in a direct listener) and the ImageContainer. This does not give 100% identical frame sync to VideoSink (how we normally render video), because VideoSink can update the timestamps of already-pushed images by pushing them again. We can't do that here because the SourceMediaStream API only allows appending. It does however get in sync for frames appended after the first frame has been rendered. Differential Revision: https://phabricator.services.mozilla.com/D22897 --HG-- extra : moz-landing-system : lando
2019-03-22 11:41:48 +00:00
mSegment.ForgetUpToTime(TimeStamp::Now());
for (VideoSegment::ConstChunkIterator i(video); !i.IsEnded(); i.Next()) {
if (!mLastFrameTime.IsNull() && i->mTimeStamp < mLastFrameTime) {
// Time can go backwards if the source is a captured MediaDecoder and
// it seeks, as the previously buffered frames would stretch into the
// future. If this happens, we clear the buffered frames and start over.
mSegment.Clear();
}
const VideoFrame& f = i->mFrame;
mSegment.AppendFrame(do_AddRef(f.GetImage()), f.GetIntrinsicSize(),
Bug 1506093 - Fix DecodedStream A/V sync. r=padenot DecodedStream sends video to its video tracks by initially buffering a set of images, then appending future ones by adding them one by one. A long time ago we refactored how MediaStreamGraph sends images to the screen, i.e., to an ImageContainer. It used to send all future frames to ImageContainer::SetCurrentFrames each time it sent something. After the refactor we just forward any new frame from a direct listener to ImageContainer::SetCurrentFrames. So in case DecodedStream has already sent 10 future frames to its track, and sends another, we end up calling ImageContainer::SetCurrentFrames(frame11). However, this is not how ImageContainer works. The refactor was wrong. Even though the timestamp for frame11 is after a previously buffered frame, it will be ignored. SetCurrentFrames wipes any previously set frames. Hence the word "Current" in its name. This patch largely restores the old behaviour by adding a thin buffering layer between the MSG (in a direct listener) and the ImageContainer. This does not give 100% identical frame sync to VideoSink (how we normally render video), because VideoSink can update the timestamps of already-pushed images by pushing them again. We can't do that here because the SourceMediaStream API only allows appending. It does however get in sync for frames appended after the first frame has been rendered. Differential Revision: https://phabricator.services.mozilla.com/D22897 --HG-- extra : moz-landing-system : lando
2019-03-22 11:41:48 +00:00
f.GetPrincipalHandle(), f.GetForceBlack(),
i->mTimeStamp);
mLastFrameTime = i->mTimeStamp;
}
mVideoFrameContainer->SetCurrentFrames(mSegment);
}
Bug 1506093 - Fix DecodedStream A/V sync. r=padenot DecodedStream sends video to its video tracks by initially buffering a set of images, then appending future ones by adding them one by one. A long time ago we refactored how MediaStreamGraph sends images to the screen, i.e., to an ImageContainer. It used to send all future frames to ImageContainer::SetCurrentFrames each time it sent something. After the refactor we just forward any new frame from a direct listener to ImageContainer::SetCurrentFrames. So in case DecodedStream has already sent 10 future frames to its track, and sends another, we end up calling ImageContainer::SetCurrentFrames(frame11). However, this is not how ImageContainer works. The refactor was wrong. Even though the timestamp for frame11 is after a previously buffered frame, it will be ignored. SetCurrentFrames wipes any previously set frames. Hence the word "Current" in its name. This patch largely restores the old behaviour by adding a thin buffering layer between the MSG (in a direct listener) and the ImageContainer. This does not give 100% identical frame sync to VideoSink (how we normally render video), because VideoSink can update the timestamps of already-pushed images by pushing them again. We can't do that here because the SourceMediaStream API only allows appending. It does however get in sync for frames appended after the first frame has been rendered. Differential Revision: https://phabricator.services.mozilla.com/D22897 --HG-- extra : moz-landing-system : lando
2019-03-22 11:41:48 +00:00
void NotifyRemoved() override {
mSegment.Clear();
mVideoFrameContainer->ClearFrames();
}
Bug 1506093 - Fix DecodedStream A/V sync. r=padenot DecodedStream sends video to its video tracks by initially buffering a set of images, then appending future ones by adding them one by one. A long time ago we refactored how MediaStreamGraph sends images to the screen, i.e., to an ImageContainer. It used to send all future frames to ImageContainer::SetCurrentFrames each time it sent something. After the refactor we just forward any new frame from a direct listener to ImageContainer::SetCurrentFrames. So in case DecodedStream has already sent 10 future frames to its track, and sends another, we end up calling ImageContainer::SetCurrentFrames(frame11). However, this is not how ImageContainer works. The refactor was wrong. Even though the timestamp for frame11 is after a previously buffered frame, it will be ignored. SetCurrentFrames wipes any previously set frames. Hence the word "Current" in its name. This patch largely restores the old behaviour by adding a thin buffering layer between the MSG (in a direct listener) and the ImageContainer. This does not give 100% identical frame sync to VideoSink (how we normally render video), because VideoSink can update the timestamps of already-pushed images by pushing them again. We can't do that here because the SourceMediaStream API only allows appending. It does however get in sync for frames appended after the first frame has been rendered. Differential Revision: https://phabricator.services.mozilla.com/D22897 --HG-- extra : moz-landing-system : lando
2019-03-22 11:41:48 +00:00
void NotifyEnded() override { mSegment.Clear(); }
Bug 1506093 - Fix DecodedStream A/V sync. r=padenot DecodedStream sends video to its video tracks by initially buffering a set of images, then appending future ones by adding them one by one. A long time ago we refactored how MediaStreamGraph sends images to the screen, i.e., to an ImageContainer. It used to send all future frames to ImageContainer::SetCurrentFrames each time it sent something. After the refactor we just forward any new frame from a direct listener to ImageContainer::SetCurrentFrames. So in case DecodedStream has already sent 10 future frames to its track, and sends another, we end up calling ImageContainer::SetCurrentFrames(frame11). However, this is not how ImageContainer works. The refactor was wrong. Even though the timestamp for frame11 is after a previously buffered frame, it will be ignored. SetCurrentFrames wipes any previously set frames. Hence the word "Current" in its name. This patch largely restores the old behaviour by adding a thin buffering layer between the MSG (in a direct listener) and the ImageContainer. This does not give 100% identical frame sync to VideoSink (how we normally render video), because VideoSink can update the timestamps of already-pushed images by pushing them again. We can't do that here because the SourceMediaStream API only allows appending. It does however get in sync for frames appended after the first frame has been rendered. Differential Revision: https://phabricator.services.mozilla.com/D22897 --HG-- extra : moz-landing-system : lando
2019-03-22 11:41:48 +00:00
TimeStamp mLastFrameTime;
VideoSegment mSegment;
const RefPtr<VideoFrameContainer> mVideoFrameContainer;
};
namespace dom {
VideoStreamTrack::VideoStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
TrackID aInputTrackID,
MediaStreamTrackSource* aSource,
const MediaTrackConstraints& aConstraints)
: MediaStreamTrack(aStream, aTrackID, aInputTrackID, aSource,
aConstraints) {}
void VideoStreamTrack::Destroy() {
mVideoOutputs.Clear();
MediaStreamTrack::Destroy();
}
void VideoStreamTrack::AddVideoOutput(VideoFrameContainer* aSink) {
for (const auto& output : mVideoOutputs) {
if (output->mVideoFrameContainer == aSink) {
MOZ_ASSERT_UNREACHABLE("A VideoFrameContainer was already added");
return;
}
}
RefPtr<VideoOutput>& output =
*mVideoOutputs.AppendElement(MakeRefPtr<VideoOutput>(aSink));
Bug 1506093 - Fix DecodedStream A/V sync. r=padenot DecodedStream sends video to its video tracks by initially buffering a set of images, then appending future ones by adding them one by one. A long time ago we refactored how MediaStreamGraph sends images to the screen, i.e., to an ImageContainer. It used to send all future frames to ImageContainer::SetCurrentFrames each time it sent something. After the refactor we just forward any new frame from a direct listener to ImageContainer::SetCurrentFrames. So in case DecodedStream has already sent 10 future frames to its track, and sends another, we end up calling ImageContainer::SetCurrentFrames(frame11). However, this is not how ImageContainer works. The refactor was wrong. Even though the timestamp for frame11 is after a previously buffered frame, it will be ignored. SetCurrentFrames wipes any previously set frames. Hence the word "Current" in its name. This patch largely restores the old behaviour by adding a thin buffering layer between the MSG (in a direct listener) and the ImageContainer. This does not give 100% identical frame sync to VideoSink (how we normally render video), because VideoSink can update the timestamps of already-pushed images by pushing them again. We can't do that here because the SourceMediaStream API only allows appending. It does however get in sync for frames appended after the first frame has been rendered. Differential Revision: https://phabricator.services.mozilla.com/D22897 --HG-- extra : moz-landing-system : lando
2019-03-22 11:41:48 +00:00
AddDirectListener(output);
AddListener(output);
}
void VideoStreamTrack::RemoveVideoOutput(VideoFrameContainer* aSink) {
for (const auto& output : nsTArray<RefPtr<VideoOutput>>(mVideoOutputs)) {
if (output->mVideoFrameContainer == aSink) {
mVideoOutputs.RemoveElement(output);
Bug 1506093 - Fix DecodedStream A/V sync. r=padenot DecodedStream sends video to its video tracks by initially buffering a set of images, then appending future ones by adding them one by one. A long time ago we refactored how MediaStreamGraph sends images to the screen, i.e., to an ImageContainer. It used to send all future frames to ImageContainer::SetCurrentFrames each time it sent something. After the refactor we just forward any new frame from a direct listener to ImageContainer::SetCurrentFrames. So in case DecodedStream has already sent 10 future frames to its track, and sends another, we end up calling ImageContainer::SetCurrentFrames(frame11). However, this is not how ImageContainer works. The refactor was wrong. Even though the timestamp for frame11 is after a previously buffered frame, it will be ignored. SetCurrentFrames wipes any previously set frames. Hence the word "Current" in its name. This patch largely restores the old behaviour by adding a thin buffering layer between the MSG (in a direct listener) and the ImageContainer. This does not give 100% identical frame sync to VideoSink (how we normally render video), because VideoSink can update the timestamps of already-pushed images by pushing them again. We can't do that here because the SourceMediaStream API only allows appending. It does however get in sync for frames appended after the first frame has been rendered. Differential Revision: https://phabricator.services.mozilla.com/D22897 --HG-- extra : moz-landing-system : lando
2019-03-22 11:41:48 +00:00
RemoveDirectListener(output);
RemoveListener(output);
}
}
}
void VideoStreamTrack::GetLabel(nsAString& aLabel, CallerType aCallerType) {
if (nsContentUtils::ResistFingerprinting(aCallerType)) {
aLabel.AssignLiteral("Internal Camera");
return;
}
MediaStreamTrack::GetLabel(aLabel, aCallerType);
}
} // namespace dom
} // namespace mozilla