1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla code.
17 : *
18 : * The Initial Developer of the Original Code is the Mozilla Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2007
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Chris Double <chris.double@double.co.nz>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsMediaDecoder.h"
40 : #include "MediaResource.h"
41 :
42 : #include "nsHTMLMediaElement.h"
43 : #include "nsDOMError.h"
44 :
45 : using namespace mozilla;
46 :
47 : // Number of milliseconds between progress events as defined by spec
48 : static const PRUint32 PROGRESS_MS = 350;
49 :
50 : // Number of milliseconds of no data before a stall event is fired as defined by spec
51 : static const PRUint32 STALL_MS = 3000;
52 :
53 : // Number of estimated seconds worth of data we need to have buffered
54 : // ahead of the current playback position before we allow the media decoder
55 : // to report that it can play through the entire media without the decode
56 : // catching up with the download. Having this margin make the
57 : // nsMediaDecoder::CanPlayThrough() calculation more stable in the case of
58 : // fluctuating bitrates.
59 : static const PRInt64 CAN_PLAY_THROUGH_MARGIN = 10;
60 :
61 0 : nsMediaDecoder::nsMediaDecoder() :
62 : mElement(nsnull),
63 : mFrameBufferLength(0),
64 : mPinnedForSeek(false),
65 0 : mShuttingDown(false)
66 : {
67 0 : MOZ_COUNT_CTOR(nsMediaDecoder);
68 0 : MediaMemoryReporter::AddMediaDecoder(this);
69 0 : }
70 :
71 0 : nsMediaDecoder::~nsMediaDecoder()
72 : {
73 0 : MOZ_COUNT_DTOR(nsMediaDecoder);
74 0 : MediaMemoryReporter::RemoveMediaDecoder(this);
75 0 : }
76 :
77 0 : bool nsMediaDecoder::Init(nsHTMLMediaElement* aElement)
78 : {
79 0 : mElement = aElement;
80 0 : mVideoFrameContainer = aElement->GetVideoFrameContainer();
81 0 : return true;
82 : }
83 :
84 0 : void nsMediaDecoder::Shutdown()
85 : {
86 0 : StopProgress();
87 0 : mElement = nsnull;
88 0 : }
89 :
90 0 : nsHTMLMediaElement* nsMediaDecoder::GetMediaElement()
91 : {
92 0 : return mElement;
93 : }
94 :
95 0 : nsresult nsMediaDecoder::RequestFrameBufferLength(PRUint32 aLength)
96 : {
97 0 : if (aLength < FRAMEBUFFER_LENGTH_MIN || aLength > FRAMEBUFFER_LENGTH_MAX) {
98 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
99 : }
100 :
101 0 : mFrameBufferLength = aLength;
102 0 : return NS_OK;
103 : }
104 :
105 0 : static void ProgressCallback(nsITimer* aTimer, void* aClosure)
106 : {
107 0 : nsMediaDecoder* decoder = static_cast<nsMediaDecoder*>(aClosure);
108 0 : decoder->Progress(true);
109 0 : }
110 :
111 0 : void nsMediaDecoder::Progress(bool aTimer)
112 : {
113 0 : if (!mElement)
114 0 : return;
115 :
116 0 : TimeStamp now = TimeStamp::Now();
117 :
118 0 : if (!aTimer) {
119 0 : mDataTime = now;
120 : }
121 :
122 : // If PROGRESS_MS has passed since the last progress event fired and more
123 : // data has arrived since then, fire another progress event.
124 0 : if ((mProgressTime.IsNull() ||
125 0 : now - mProgressTime >= TimeDuration::FromMilliseconds(PROGRESS_MS)) &&
126 0 : !mDataTime.IsNull() &&
127 0 : now - mDataTime <= TimeDuration::FromMilliseconds(PROGRESS_MS)) {
128 0 : mElement->DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
129 0 : mProgressTime = now;
130 : }
131 :
132 0 : if (!mDataTime.IsNull() &&
133 0 : now - mDataTime >= TimeDuration::FromMilliseconds(STALL_MS)) {
134 0 : mElement->DownloadStalled();
135 : // Null it out
136 0 : mDataTime = TimeStamp();
137 : }
138 : }
139 :
140 0 : nsresult nsMediaDecoder::StartProgress()
141 : {
142 0 : if (mProgressTimer)
143 0 : return NS_OK;
144 :
145 0 : mProgressTimer = do_CreateInstance("@mozilla.org/timer;1");
146 0 : return mProgressTimer->InitWithFuncCallback(ProgressCallback,
147 : this,
148 : PROGRESS_MS,
149 0 : nsITimer::TYPE_REPEATING_SLACK);
150 : }
151 :
152 0 : nsresult nsMediaDecoder::StopProgress()
153 : {
154 0 : if (!mProgressTimer)
155 0 : return NS_OK;
156 :
157 0 : nsresult rv = mProgressTimer->Cancel();
158 0 : mProgressTimer = nsnull;
159 :
160 0 : return rv;
161 : }
162 :
163 0 : void nsMediaDecoder::FireTimeUpdate()
164 : {
165 0 : if (!mElement)
166 0 : return;
167 0 : mElement->FireTimeUpdate(true);
168 : }
169 :
170 0 : void nsMediaDecoder::PinForSeek()
171 : {
172 0 : MediaResource* resource = GetResource();
173 0 : if (!resource || mPinnedForSeek) {
174 0 : return;
175 : }
176 0 : mPinnedForSeek = true;
177 0 : resource->Pin();
178 : }
179 :
180 0 : void nsMediaDecoder::UnpinForSeek()
181 : {
182 0 : MediaResource* resource = GetResource();
183 0 : if (!resource || !mPinnedForSeek) {
184 0 : return;
185 : }
186 0 : mPinnedForSeek = false;
187 0 : resource->Unpin();
188 : }
189 :
190 0 : bool nsMediaDecoder::CanPlayThrough()
191 : {
192 0 : Statistics stats = GetStatistics();
193 0 : if (!stats.mDownloadRateReliable || !stats.mPlaybackRateReliable) {
194 0 : return false;
195 : }
196 0 : PRInt64 bytesToDownload = stats.mTotalBytes - stats.mDownloadPosition;
197 0 : PRInt64 bytesToPlayback = stats.mTotalBytes - stats.mPlaybackPosition;
198 0 : double timeToDownload = bytesToDownload / stats.mDownloadRate;
199 0 : double timeToPlay = bytesToPlayback / stats.mPlaybackRate;
200 :
201 0 : if (timeToDownload > timeToPlay) {
202 : // Estimated time to download is greater than the estimated time to play.
203 : // We probably can't play through without having to stop to buffer.
204 0 : return false;
205 : }
206 :
207 : // Estimated time to download is less than the estimated time to play.
208 : // We can probably play through without having to buffer, but ensure that
209 : // we've got a reasonable amount of data buffered after the current
210 : // playback position, so that if the bitrate of the media fluctuates, or if
211 : // our download rate or decode rate estimation is otherwise inaccurate,
212 : // we don't suddenly discover that we need to buffer. This is particularly
213 : // required near the start of the media, when not much data is downloaded.
214 : PRInt64 readAheadMargin =
215 0 : static_cast<PRInt64>(stats.mPlaybackRate * CAN_PLAY_THROUGH_MARGIN);
216 : return stats.mTotalBytes == stats.mDownloadPosition ||
217 0 : stats.mDownloadPosition > stats.mPlaybackPosition + readAheadMargin;
218 : }
219 :
220 : namespace mozilla {
221 :
222 : MediaMemoryReporter* MediaMemoryReporter::sUniqueInstance;
223 :
224 0 : NS_MEMORY_REPORTER_IMPLEMENT(MediaDecodedVideoMemory,
225 : "explicit/media/decoded-video",
226 : KIND_HEAP,
227 : UNITS_BYTES,
228 : MediaMemoryReporter::GetDecodedVideoMemory,
229 0 : "Memory used by decoded video frames.")
230 :
231 0 : NS_MEMORY_REPORTER_IMPLEMENT(MediaDecodedAudioMemory,
232 : "explicit/media/decoded-audio",
233 : KIND_HEAP,
234 : UNITS_BYTES,
235 : MediaMemoryReporter::GetDecodedAudioMemory,
236 0 : "Memory used by decoded audio chunks.")
237 :
238 0 : MediaMemoryReporter::MediaMemoryReporter()
239 0 : : mMediaDecodedVideoMemory(new NS_MEMORY_REPORTER_NAME(MediaDecodedVideoMemory))
240 0 : , mMediaDecodedAudioMemory(new NS_MEMORY_REPORTER_NAME(MediaDecodedAudioMemory))
241 : {
242 0 : NS_RegisterMemoryReporter(mMediaDecodedVideoMemory);
243 0 : NS_RegisterMemoryReporter(mMediaDecodedAudioMemory);
244 0 : }
245 :
246 0 : MediaMemoryReporter::~MediaMemoryReporter()
247 : {
248 0 : NS_UnregisterMemoryReporter(mMediaDecodedVideoMemory);
249 0 : NS_UnregisterMemoryReporter(mMediaDecodedAudioMemory);
250 0 : }
251 :
252 : } // namespace mozilla
|