1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "CameraPreviewMediaStream.h"
7 #include "CameraCommon.h"
10 * Maximum number of outstanding invalidates before we start to drop frames;
11 * if we hit this threshold, it is an indicator that the main thread is
12 * either very busy or the device is busy elsewhere (e.g. encoding or
13 * persisting video data).
15 #define MAX_INVALIDATE_PENDING 4
17 using namespace mozilla::layers
;
18 using namespace mozilla::dom
;
22 static const TrackID TRACK_VIDEO
= 2;
25 FakeMediaStreamGraph::DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed
<nsIRunnable
> aRunnable
)
27 nsRefPtr
<nsIRunnable
> task
= aRunnable
;
28 NS_DispatchToMainThread(task
);
31 CameraPreviewMediaStream::CameraPreviewMediaStream(DOMMediaStream
* aWrapper
)
32 : MediaStream(aWrapper
)
33 , mMutex("mozilla::camera::CameraPreviewMediaStream")
34 , mInvalidatePending(0)
37 , mTrackCreated(false)
39 SetGraphImpl(MediaStreamGraph::GetInstance());
40 mFakeMediaStreamGraph
= new FakeMediaStreamGraph();
45 CameraPreviewMediaStream::AddAudioOutput(void* aKey
)
50 CameraPreviewMediaStream::SetAudioOutputVolume(void* aKey
, float aVolume
)
55 CameraPreviewMediaStream::RemoveAudioOutput(void* aKey
)
60 CameraPreviewMediaStream::AddVideoOutput(VideoFrameContainer
* aContainer
)
62 MutexAutoLock
lock(mMutex
);
63 nsRefPtr
<VideoFrameContainer
> container
= aContainer
;
64 AddVideoOutputImpl(container
.forget());
66 if (mVideoOutputs
.Length() > 1) {
70 for (uint32_t j
= 0; j
< mListeners
.Length(); ++j
) {
71 MediaStreamListener
* l
= mListeners
[j
];
72 l
->NotifyConsumptionChanged(mFakeMediaStreamGraph
, MediaStreamListener::CONSUMED
);
77 CameraPreviewMediaStream::RemoveVideoOutput(VideoFrameContainer
* aContainer
)
79 MutexAutoLock
lock(mMutex
);
80 RemoveVideoOutputImpl(aContainer
);
82 if (!mVideoOutputs
.IsEmpty()) {
86 for (uint32_t j
= 0; j
< mListeners
.Length(); ++j
) {
87 MediaStreamListener
* l
= mListeners
[j
];
88 l
->NotifyConsumptionChanged(mFakeMediaStreamGraph
, MediaStreamListener::NOT_CONSUMED
);
93 CameraPreviewMediaStream::ChangeExplicitBlockerCount(int32_t aDelta
)
98 CameraPreviewMediaStream::AddListener(MediaStreamListener
* aListener
)
100 MutexAutoLock
lock(mMutex
);
102 MediaStreamListener
* listener
= *mListeners
.AppendElement() = aListener
;
103 listener
->NotifyBlockingChanged(mFakeMediaStreamGraph
, MediaStreamListener::UNBLOCKED
);
104 listener
->NotifyHasCurrentData(mFakeMediaStreamGraph
);
108 CameraPreviewMediaStream::RemoveListener(MediaStreamListener
* aListener
)
110 MutexAutoLock
lock(mMutex
);
112 nsRefPtr
<MediaStreamListener
> listener(aListener
);
113 mListeners
.RemoveElement(aListener
);
114 listener
->NotifyEvent(mFakeMediaStreamGraph
, MediaStreamListener::EVENT_REMOVED
);
118 CameraPreviewMediaStream::OnPreviewStateChange(bool aActive
)
121 MutexAutoLock
lock(mMutex
);
122 if (!mTrackCreated
) {
123 mTrackCreated
= true;
124 VideoSegment tmpSegment
;
125 for (uint32_t j
= 0; j
< mListeners
.Length(); ++j
) {
126 MediaStreamListener
* l
= mListeners
[j
];
127 l
->NotifyQueuedTrackChanges(mFakeMediaStreamGraph
, TRACK_VIDEO
, 0, 0,
128 MediaStreamListener::TRACK_EVENT_CREATED
,
136 CameraPreviewMediaStream::Destroy()
138 MutexAutoLock
lock(mMutex
);
139 mMainThreadDestroyed
= true;
144 CameraPreviewMediaStream::Invalidate()
146 MutexAutoLock
lock(mMutex
);
147 --mInvalidatePending
;
148 for (nsTArray
<nsRefPtr
<VideoFrameContainer
> >::size_type i
= 0; i
< mVideoOutputs
.Length(); ++i
) {
149 VideoFrameContainer
* output
= mVideoOutputs
[i
];
150 output
->Invalidate();
155 CameraPreviewMediaStream::RateLimit(bool aLimit
)
161 CameraPreviewMediaStream::SetCurrentFrame(const gfxIntSize
& aIntrinsicSize
, Image
* aImage
)
164 MutexAutoLock
lock(mMutex
);
166 if (mInvalidatePending
> 0) {
167 if (mRateLimit
|| mInvalidatePending
> MAX_INVALIDATE_PENDING
) {
169 DOM_CAMERA_LOGW("Discard preview frame %d, %d invalidation(s) pending",
170 mDiscardedFrames
, mInvalidatePending
);
174 DOM_CAMERA_LOGI("Update preview frame, %d invalidation(s) pending",
177 mDiscardedFrames
= 0;
179 TimeStamp now
= TimeStamp::Now();
180 for (nsTArray
<nsRefPtr
<VideoFrameContainer
> >::size_type i
= 0; i
< mVideoOutputs
.Length(); ++i
) {
181 VideoFrameContainer
* output
= mVideoOutputs
[i
];
182 output
->SetCurrentFrame(aIntrinsicSize
, aImage
, now
);
185 ++mInvalidatePending
;
188 nsCOMPtr
<nsIRunnable
> event
=
189 NS_NewRunnableMethod(this, &CameraPreviewMediaStream::Invalidate
);
190 NS_DispatchToMainThread(event
);
194 CameraPreviewMediaStream::ClearCurrentFrame()
196 MutexAutoLock
lock(mMutex
);
198 for (nsTArray
<nsRefPtr
<VideoFrameContainer
> >::size_type i
= 0; i
< mVideoOutputs
.Length(); ++i
) {
199 VideoFrameContainer
* output
= mVideoOutputs
[i
];
200 output
->ClearCurrentFrame();
201 nsCOMPtr
<nsIRunnable
> event
=
202 NS_NewRunnableMethod(output
, &VideoFrameContainer::Invalidate
);
203 NS_DispatchToMainThread(event
);