/src/mozilla-central/dom/media/MediaResource.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "MediaResource.h" |
8 | | #include "mozilla/DebugOnly.h" |
9 | | #include "mozilla/Logging.h" |
10 | | #include "mozilla/MathAlgorithms.h" |
11 | | #include "mozilla/SystemGroup.h" |
12 | | #include "mozilla/ErrorNames.h" |
13 | | |
14 | | using mozilla::media::TimeUnit; |
15 | | |
16 | | #undef ILOG |
17 | | |
18 | | mozilla::LazyLogModule gMediaResourceIndexLog("MediaResourceIndex"); |
19 | | // Debug logging macro with object pointer and class name. |
20 | | #define ILOG(msg, ...) \ |
21 | 0 | DDMOZ_LOG( \ |
22 | 0 | gMediaResourceIndexLog, mozilla::LogLevel::Debug, msg, ##__VA_ARGS__) |
23 | | |
24 | | namespace mozilla { |
25 | | |
26 | | void |
27 | | MediaResource::Destroy() |
28 | 0 | { |
29 | 0 | // Ensures we only delete the MediaResource on the main thread. |
30 | 0 | if (NS_IsMainThread()) { |
31 | 0 | delete this; |
32 | 0 | return; |
33 | 0 | } |
34 | 0 | nsresult rv = SystemGroup::Dispatch( |
35 | 0 | TaskCategory::Other, |
36 | 0 | NewNonOwningRunnableMethod( |
37 | 0 | "MediaResource::Destroy", this, &MediaResource::Destroy)); |
38 | 0 | MOZ_ALWAYS_SUCCEEDS(rv); |
39 | 0 | } |
40 | | |
41 | | NS_IMPL_ADDREF(MediaResource) |
42 | | NS_IMPL_RELEASE_WITH_DESTROY(MediaResource, Destroy()) |
43 | | |
44 | | static const uint32_t kMediaResourceIndexCacheSize = 8192; |
45 | | static_assert(IsPowerOfTwo(kMediaResourceIndexCacheSize), |
46 | | "kMediaResourceIndexCacheSize cache size must be a power of 2"); |
47 | | |
48 | | MediaResourceIndex::MediaResourceIndex(MediaResource* aResource) |
49 | | : mResource(aResource) |
50 | | , mOffset(0) |
51 | | , mCacheBlockSize(aResource->ShouldCacheReads() |
52 | | ? kMediaResourceIndexCacheSize |
53 | | : 0) |
54 | | , mCachedOffset(0) |
55 | | , mCachedBytes(0) |
56 | | , mCachedBlock(MakeUnique<char[]>(mCacheBlockSize)) |
57 | 0 | { |
58 | 0 | DDLINKCHILD("resource", aResource); |
59 | 0 | } |
60 | | |
61 | | nsresult |
62 | | MediaResourceIndex::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) |
63 | 0 | { |
64 | 0 | NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); |
65 | 0 |
|
66 | 0 | // We purposefuly don't check that we may attempt to read past |
67 | 0 | // mResource->GetLength() as the resource's length may change over time. |
68 | 0 |
|
69 | 0 | nsresult rv = ReadAt(mOffset, aBuffer, aCount, aBytes); |
70 | 0 | if (NS_FAILED(rv)) { |
71 | 0 | return rv; |
72 | 0 | } |
73 | 0 | mOffset += *aBytes; |
74 | 0 | if (mOffset < 0) { |
75 | 0 | // Very unlikely overflow; just return to position 0. |
76 | 0 | mOffset = 0; |
77 | 0 | } |
78 | 0 | return NS_OK; |
79 | 0 | } |
80 | | |
81 | | static nsCString |
82 | | ResultName(nsresult aResult) |
83 | 0 | { |
84 | 0 | nsCString name; |
85 | 0 | GetErrorName(aResult, name); |
86 | 0 | return name; |
87 | 0 | } |
88 | | |
89 | | nsresult |
90 | | MediaResourceIndex::ReadAt(int64_t aOffset, |
91 | | char* aBuffer, |
92 | | uint32_t aCount, |
93 | | uint32_t* aBytes) |
94 | 0 | { |
95 | 0 | if (mCacheBlockSize == 0) { |
96 | 0 | return UncachedReadAt(aOffset, aBuffer, aCount, aBytes); |
97 | 0 | } |
98 | 0 | |
99 | 0 | *aBytes = 0; |
100 | 0 |
|
101 | 0 | if (aCount == 0) { |
102 | 0 | return NS_OK; |
103 | 0 | } |
104 | 0 | |
105 | 0 | const int64_t endOffset = aOffset + aCount; |
106 | 0 | if (aOffset < 0 || endOffset < aOffset) { |
107 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
108 | 0 | } |
109 | 0 | |
110 | 0 | const int64_t lastBlockOffset = CacheOffsetContaining(endOffset - 1); |
111 | 0 |
|
112 | 0 | if (mCachedBytes != 0 && mCachedOffset + mCachedBytes >= aOffset && |
113 | 0 | mCachedOffset < endOffset) { |
114 | 0 | // There is data in the cache that is not completely before aOffset and not |
115 | 0 | // completely after endOffset, so it could be usable (with potential top-up). |
116 | 0 | if (aOffset < mCachedOffset) { |
117 | 0 | // We need to read before the cached data. |
118 | 0 | const uint32_t toRead = uint32_t(mCachedOffset - aOffset); |
119 | 0 | MOZ_ASSERT(toRead > 0); |
120 | 0 | MOZ_ASSERT(toRead < aCount); |
121 | 0 | uint32_t read = 0; |
122 | 0 | nsresult rv = UncachedReadAt(aOffset, aBuffer, toRead, &read); |
123 | 0 | if (NS_FAILED(rv)) { |
124 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 |
125 | 0 | ") uncached read before cache -> %s, %" PRIu32, |
126 | 0 | aCount, |
127 | 0 | aOffset, |
128 | 0 | ResultName(rv).get(), |
129 | 0 | *aBytes); |
130 | 0 | return rv; |
131 | 0 | } |
132 | 0 | *aBytes = read; |
133 | 0 | if (read < toRead) { |
134 | 0 | // Could not read everything we wanted, we're done. |
135 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 |
136 | 0 | ") uncached read before cache, incomplete -> OK, %" PRIu32, |
137 | 0 | aCount, |
138 | 0 | aOffset, |
139 | 0 | *aBytes); |
140 | 0 | return NS_OK; |
141 | 0 | } |
142 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 |
143 | 0 | ") uncached read before cache: %" PRIu32 ", remaining: %" PRIu32 |
144 | 0 | "@%" PRId64 "...", |
145 | 0 | aCount, |
146 | 0 | aOffset, |
147 | 0 | read, |
148 | 0 | aCount - read, |
149 | 0 | aOffset + read); |
150 | 0 | aOffset += read; |
151 | 0 | aBuffer += read; |
152 | 0 | aCount -= read; |
153 | 0 | // We should have reached the cache. |
154 | 0 | MOZ_ASSERT(aOffset == mCachedOffset); |
155 | 0 | } |
156 | 0 | MOZ_ASSERT(aOffset >= mCachedOffset); |
157 | 0 |
|
158 | 0 | // We've reached our cache. |
159 | 0 | const uint32_t toCopy = |
160 | 0 | std::min(aCount, uint32_t(mCachedOffset + mCachedBytes - aOffset)); |
161 | 0 | // Note that we could in fact be just after the last byte of the cache, in |
162 | 0 | // which case we can't actually read from it! (But we will top-up next.) |
163 | 0 | if (toCopy != 0) { |
164 | 0 | memcpy(aBuffer, &mCachedBlock[IndexInCache(aOffset)], toCopy); |
165 | 0 | *aBytes += toCopy; |
166 | 0 | aCount -= toCopy; |
167 | 0 | if (aCount == 0) { |
168 | 0 | // All done! |
169 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") copied everything (%" PRIu32 |
170 | 0 | ") from cache(%" PRIu32 "@%" PRId64 ") :-D -> OK, %" PRIu32, |
171 | 0 | aCount, |
172 | 0 | aOffset, |
173 | 0 | toCopy, |
174 | 0 | mCachedBytes, |
175 | 0 | mCachedOffset, |
176 | 0 | *aBytes); |
177 | 0 | return NS_OK; |
178 | 0 | } |
179 | 0 | aOffset += toCopy; |
180 | 0 | aBuffer += toCopy; |
181 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") copied %" PRIu32 |
182 | 0 | " from cache(%" PRIu32 "@%" PRId64 ") :-), remaining: %" PRIu32 |
183 | 0 | "@%" PRId64 "...", |
184 | 0 | aCount + toCopy, |
185 | 0 | aOffset - toCopy, |
186 | 0 | toCopy, |
187 | 0 | mCachedBytes, |
188 | 0 | mCachedOffset, |
189 | 0 | aCount, |
190 | 0 | aOffset); |
191 | 0 | } |
192 | 0 |
|
193 | 0 | if (aOffset - 1 >= lastBlockOffset) { |
194 | 0 | // We were already reading cached data from the last block, we need more |
195 | 0 | // from it -> try to top-up, read what we can, and we'll be done. |
196 | 0 | MOZ_ASSERT(aOffset == mCachedOffset + mCachedBytes); |
197 | 0 | MOZ_ASSERT(endOffset <= lastBlockOffset + mCacheBlockSize); |
198 | 0 | return CacheOrReadAt(aOffset, aBuffer, aCount, aBytes); |
199 | 0 | } |
200 | 0 |
|
201 | 0 | // We were not in the last block (but we may just have crossed the line now) |
202 | 0 | MOZ_ASSERT(aOffset <= lastBlockOffset); |
203 | 0 | // Continue below... |
204 | 0 | } else if (aOffset >= lastBlockOffset) { |
205 | 0 | // There was nothing we could get from the cache. |
206 | 0 | // But we're already in the last block -> Cache or read what we can. |
207 | 0 | // Make sure to invalidate the cache first. |
208 | 0 | mCachedBytes = 0; |
209 | 0 | return CacheOrReadAt(aOffset, aBuffer, aCount, aBytes); |
210 | 0 | } |
211 | 0 | |
212 | 0 | // If we're here, either there was nothing usable in the cache, or we've just |
213 | 0 | // read what was in the cache but there's still more to read. |
214 | 0 | |
215 | 0 | if (aOffset < lastBlockOffset) { |
216 | 0 | // We need to read before the last block. |
217 | 0 | // Start with an uncached read up to the last block. |
218 | 0 | const uint32_t toRead = uint32_t(lastBlockOffset - aOffset); |
219 | 0 | MOZ_ASSERT(toRead > 0); |
220 | 0 | MOZ_ASSERT(toRead < aCount); |
221 | 0 | uint32_t read = 0; |
222 | 0 | nsresult rv = UncachedReadAt(aOffset, aBuffer, toRead, &read); |
223 | 0 | if (NS_FAILED(rv)) { |
224 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 |
225 | 0 | ") uncached read before last block failed -> %s, %" PRIu32, |
226 | 0 | aCount, |
227 | 0 | aOffset, |
228 | 0 | ResultName(rv).get(), |
229 | 0 | *aBytes); |
230 | 0 | return rv; |
231 | 0 | } |
232 | 0 | if (read == 0) { |
233 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 |
234 | 0 | ") uncached read 0 before last block -> OK, %" PRIu32, |
235 | 0 | aCount, |
236 | 0 | aOffset, |
237 | 0 | *aBytes); |
238 | 0 | return NS_OK; |
239 | 0 | } |
240 | 0 | *aBytes += read; |
241 | 0 | if (read < toRead) { |
242 | 0 | // Could not read everything we wanted, we're done. |
243 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 |
244 | 0 | ") uncached read before last block, incomplete -> OK, %" PRIu32, |
245 | 0 | aCount, |
246 | 0 | aOffset, |
247 | 0 | *aBytes); |
248 | 0 | return NS_OK; |
249 | 0 | } |
250 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") read %" PRIu32 |
251 | 0 | " before last block, remaining: %" PRIu32 "@%" PRId64 "...", |
252 | 0 | aCount, |
253 | 0 | aOffset, |
254 | 0 | read, |
255 | 0 | aCount - read, |
256 | 0 | aOffset + read); |
257 | 0 | aOffset += read; |
258 | 0 | aBuffer += read; |
259 | 0 | aCount -= read; |
260 | 0 | } |
261 | 0 |
|
262 | 0 | // We should just have reached the start of the last block. |
263 | 0 | MOZ_ASSERT(aOffset == lastBlockOffset); |
264 | 0 | MOZ_ASSERT(aCount <= mCacheBlockSize); |
265 | 0 | // Make sure to invalidate the cache first. |
266 | 0 | mCachedBytes = 0; |
267 | 0 | return CacheOrReadAt(aOffset, aBuffer, aCount, aBytes); |
268 | 0 | } |
269 | | |
270 | | nsresult |
271 | | MediaResourceIndex::CacheOrReadAt(int64_t aOffset, |
272 | | char* aBuffer, |
273 | | uint32_t aCount, |
274 | | uint32_t* aBytes) |
275 | 0 | { |
276 | 0 | // We should be here because there is more data to read. |
277 | 0 | MOZ_ASSERT(aCount > 0); |
278 | 0 | // We should be in the last block, so we shouldn't try to read past it. |
279 | 0 | MOZ_ASSERT(IndexInCache(aOffset) + aCount <= mCacheBlockSize); |
280 | 0 |
|
281 | 0 | const int64_t length = GetLength(); |
282 | 0 | // If length is unknown (-1), look at resource-cached data. |
283 | 0 | // If length is known and equal or greater than requested, also look at |
284 | 0 | // resource-cached data. |
285 | 0 | // Otherwise, if length is known but same, or less than(!?), requested, don't |
286 | 0 | // attempt to access resource-cached data, as we're not expecting it to ever |
287 | 0 | // be greater than the length. |
288 | 0 | if (length < 0 || length >= aOffset + aCount) { |
289 | 0 | // Is there cached data covering at least the requested range? |
290 | 0 | const int64_t cachedDataEnd = mResource->GetCachedDataEnd(aOffset); |
291 | 0 | if (cachedDataEnd >= aOffset + aCount) { |
292 | 0 | // Try to read as much resource-cached data as can fill our local cache. |
293 | 0 | // Assume we can read as much as is cached without blocking. |
294 | 0 | const uint32_t cacheIndex = IndexInCache(aOffset); |
295 | 0 | const uint32_t toRead = |
296 | 0 | uint32_t(std::min(cachedDataEnd - aOffset, |
297 | 0 | int64_t(mCacheBlockSize - cacheIndex))); |
298 | 0 | MOZ_ASSERT(toRead >= aCount); |
299 | 0 | uint32_t read = 0; |
300 | 0 | // We would like `toRead` if possible, but ok with at least `aCount`. |
301 | 0 | nsresult rv = UncachedRangedReadAt( |
302 | 0 | aOffset, &mCachedBlock[cacheIndex], aCount, toRead - aCount, &read); |
303 | 0 | if (NS_SUCCEEDED(rv)) { |
304 | 0 | if (read == 0) { |
305 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - UncachedRangedReadAt(%" PRIu32 |
306 | 0 | "..%" PRIu32 "@%" PRId64 |
307 | 0 | ") to top-up succeeded but read nothing -> OK anyway", |
308 | 0 | aCount, |
309 | 0 | aOffset, |
310 | 0 | aCount, |
311 | 0 | toRead, |
312 | 0 | aOffset); |
313 | 0 | // Couldn't actually read anything, but didn't error out, so count |
314 | 0 | // that as success. |
315 | 0 | return NS_OK; |
316 | 0 | } |
317 | 0 | if (mCachedOffset + mCachedBytes == aOffset) { |
318 | 0 | // We were topping-up the cache, just update its size. |
319 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - UncachedRangedReadAt(%" PRIu32 |
320 | 0 | "..%" PRIu32 "@%" PRId64 ") to top-up succeeded to read %" PRIu32 |
321 | 0 | "...", |
322 | 0 | aCount, |
323 | 0 | aOffset, |
324 | 0 | aCount, |
325 | 0 | toRead, |
326 | 0 | aOffset, |
327 | 0 | read); |
328 | 0 | mCachedBytes += read; |
329 | 0 | } else { |
330 | 0 | // We were filling the cache from scratch, save new cache information. |
331 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - UncachedRangedReadAt(%" PRIu32 |
332 | 0 | "..%" PRIu32 "@%" PRId64 |
333 | 0 | ") to fill cache succeeded to read %" PRIu32 "...", |
334 | 0 | aCount, |
335 | 0 | aOffset, |
336 | 0 | aCount, |
337 | 0 | toRead, |
338 | 0 | aOffset, |
339 | 0 | read); |
340 | 0 | mCachedOffset = aOffset; |
341 | 0 | mCachedBytes = read; |
342 | 0 | } |
343 | 0 | // Copy relevant part into output. |
344 | 0 | uint32_t toCopy = std::min(aCount, read); |
345 | 0 | memcpy(aBuffer, &mCachedBlock[cacheIndex], toCopy); |
346 | 0 | *aBytes += toCopy; |
347 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - copied %" PRIu32 "@%" PRId64 |
348 | 0 | " -> OK, %" PRIu32, |
349 | 0 | aCount, |
350 | 0 | aOffset, |
351 | 0 | toCopy, |
352 | 0 | aOffset, |
353 | 0 | *aBytes); |
354 | 0 | // We may not have read all that was requested, but we got everything |
355 | 0 | // we could get, so we're done. |
356 | 0 | return NS_OK; |
357 | 0 | } |
358 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - UncachedRangedReadAt(%" PRIu32 |
359 | 0 | "..%" PRIu32 "@%" PRId64 |
360 | 0 | ") failed: %s, will fallback to blocking read...", |
361 | 0 | aCount, |
362 | 0 | aOffset, |
363 | 0 | aCount, |
364 | 0 | toRead, |
365 | 0 | aOffset, |
366 | 0 | ResultName(rv).get()); |
367 | 0 | // Failure during reading. Note that this may be due to the cache |
368 | 0 | // changing between `GetCachedDataEnd` and `ReadAt`, so it's not |
369 | 0 | // totally unexpected, just hopefully rare; but we do need to handle it. |
370 | 0 |
|
371 | 0 | // Invalidate part of cache that may have been partially overridden. |
372 | 0 | if (mCachedOffset + mCachedBytes == aOffset) { |
373 | 0 | // We were topping-up the cache, just keep the old untouched data. |
374 | 0 | // (i.e., nothing to do here.) |
375 | 0 | } else { |
376 | 0 | // We were filling the cache from scratch, invalidate cache. |
377 | 0 | mCachedBytes = 0; |
378 | 0 | } |
379 | 0 | } else { |
380 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 |
381 | 0 | ") - no cached data, will fallback to blocking read...", |
382 | 0 | aCount, |
383 | 0 | aOffset); |
384 | 0 | } |
385 | 0 | } else { |
386 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - length is %" PRId64 |
387 | 0 | " (%s), will fallback to blocking read as the caller requested...", |
388 | 0 | aCount, |
389 | 0 | aOffset, |
390 | 0 | length, |
391 | 0 | length < 0 ? "unknown" : "too short!"); |
392 | 0 | } |
393 | 0 | uint32_t read = 0; |
394 | 0 | nsresult rv = UncachedReadAt(aOffset, aBuffer, aCount, &read); |
395 | 0 | if (NS_SUCCEEDED(rv)) { |
396 | 0 | *aBytes += read; |
397 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - fallback uncached read got %" PRIu32 |
398 | 0 | " bytes -> %s, %" PRIu32, |
399 | 0 | aCount, |
400 | 0 | aOffset, |
401 | 0 | read, |
402 | 0 | ResultName(rv).get(), |
403 | 0 | *aBytes); |
404 | 0 | } else { |
405 | 0 | ILOG("ReadAt(%" PRIu32 "@%" PRId64 |
406 | 0 | ") - fallback uncached read failed -> %s, %" PRIu32, |
407 | 0 | aCount, |
408 | 0 | aOffset, |
409 | 0 | ResultName(rv).get(), |
410 | 0 | *aBytes); |
411 | 0 | } |
412 | 0 | return rv; |
413 | 0 | } |
414 | | |
415 | | nsresult |
416 | | MediaResourceIndex::UncachedReadAt(int64_t aOffset, |
417 | | char* aBuffer, |
418 | | uint32_t aCount, |
419 | | uint32_t* aBytes) const |
420 | 0 | { |
421 | 0 | if (aOffset < 0) { |
422 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
423 | 0 | } |
424 | 0 | if (aCount == 0) { |
425 | 0 | *aBytes = 0; |
426 | 0 | return NS_OK; |
427 | 0 | } |
428 | 0 | return mResource->ReadAt(aOffset, aBuffer, aCount, aBytes); |
429 | 0 | } |
430 | | |
431 | | nsresult |
432 | | MediaResourceIndex::UncachedRangedReadAt(int64_t aOffset, |
433 | | char* aBuffer, |
434 | | uint32_t aRequestedCount, |
435 | | uint32_t aExtraCount, |
436 | | uint32_t* aBytes) const |
437 | 0 | { |
438 | 0 | uint32_t count = aRequestedCount + aExtraCount; |
439 | 0 | if (aOffset < 0 || count < aRequestedCount) { |
440 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
441 | 0 | } |
442 | 0 | if (count == 0) { |
443 | 0 | *aBytes = 0; |
444 | 0 | return NS_OK; |
445 | 0 | } |
446 | 0 | return mResource->ReadAt(aOffset, aBuffer, count, aBytes); |
447 | 0 | } |
448 | | |
449 | | nsresult |
450 | | MediaResourceIndex::Seek(int32_t aWhence, int64_t aOffset) |
451 | 0 | { |
452 | 0 | switch (aWhence) { |
453 | 0 | case SEEK_SET: |
454 | 0 | break; |
455 | 0 | case SEEK_CUR: |
456 | 0 | aOffset += mOffset; |
457 | 0 | break; |
458 | 0 | case SEEK_END: |
459 | 0 | { |
460 | 0 | int64_t length = mResource->GetLength(); |
461 | 0 | if (length == -1 || length - aOffset < 0) { |
462 | 0 | return NS_ERROR_FAILURE; |
463 | 0 | } |
464 | 0 | aOffset = mResource->GetLength() - aOffset; |
465 | 0 | } |
466 | 0 | break; |
467 | 0 | default: |
468 | 0 | return NS_ERROR_FAILURE; |
469 | 0 | } |
470 | 0 | |
471 | 0 | if (aOffset < 0) { |
472 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
473 | 0 | } |
474 | 0 | mOffset = aOffset; |
475 | 0 |
|
476 | 0 | return NS_OK; |
477 | 0 | } |
478 | | |
479 | | already_AddRefed<MediaByteBuffer> |
480 | | MediaResourceIndex::MediaReadAt(int64_t aOffset, uint32_t aCount) const |
481 | 0 | { |
482 | 0 | NS_ENSURE_TRUE(aOffset >= 0, nullptr); |
483 | 0 | RefPtr<MediaByteBuffer> bytes = new MediaByteBuffer(); |
484 | 0 | bool ok = bytes->SetLength(aCount, fallible); |
485 | 0 | NS_ENSURE_TRUE(ok, nullptr); |
486 | 0 |
|
487 | 0 | uint32_t bytesRead = 0; |
488 | 0 | nsresult rv = mResource->ReadAt( |
489 | 0 | aOffset, reinterpret_cast<char*>(bytes->Elements()), aCount, &bytesRead); |
490 | 0 | NS_ENSURE_SUCCESS(rv, nullptr); |
491 | 0 |
|
492 | 0 | bytes->SetLength(bytesRead); |
493 | 0 | return bytes.forget(); |
494 | 0 | } |
495 | | |
496 | | already_AddRefed<MediaByteBuffer> |
497 | | MediaResourceIndex::CachedMediaReadAt(int64_t aOffset, uint32_t aCount) const |
498 | 0 | { |
499 | 0 | RefPtr<MediaByteBuffer> bytes = new MediaByteBuffer(); |
500 | 0 | bool ok = bytes->SetLength(aCount, fallible); |
501 | 0 | NS_ENSURE_TRUE(ok, nullptr); |
502 | 0 | char* curr = reinterpret_cast<char*>(bytes->Elements()); |
503 | 0 | nsresult rv = mResource->ReadFromCache(curr, aOffset, aCount); |
504 | 0 | NS_ENSURE_SUCCESS(rv, nullptr); |
505 | 0 | return bytes.forget(); |
506 | 0 | } |
507 | | |
508 | | // Get the length of the stream in bytes. Returns -1 if not known. |
509 | | // This can change over time; after a seek operation, a misbehaving |
510 | | // server may give us a resource of a different length to what it had |
511 | | // reported previously --- or it may just lie in its Content-Length |
512 | | // header and give us more or less data than it reported. We will adjust |
513 | | // the result of GetLength to reflect the data that's actually arriving. |
514 | | int64_t |
515 | | MediaResourceIndex::GetLength() const |
516 | 0 | { |
517 | 0 | return mResource->GetLength(); |
518 | 0 | } |
519 | | |
520 | | uint32_t |
521 | | MediaResourceIndex::IndexInCache(int64_t aOffsetInFile) const |
522 | 0 | { |
523 | 0 | const uint32_t index = uint32_t(aOffsetInFile) & (mCacheBlockSize - 1); |
524 | 0 | MOZ_ASSERT(index == aOffsetInFile % mCacheBlockSize); |
525 | 0 | return index; |
526 | 0 | } |
527 | | |
528 | | int64_t |
529 | | MediaResourceIndex::CacheOffsetContaining(int64_t aOffsetInFile) const |
530 | 0 | { |
531 | 0 | const int64_t offset = aOffsetInFile & ~(int64_t(mCacheBlockSize) - 1); |
532 | 0 | MOZ_ASSERT(offset == aOffsetInFile - IndexInCache(aOffsetInFile)); |
533 | 0 | return offset; |
534 | 0 | } |
535 | | |
536 | | } // namespace mozilla |
537 | | |
538 | | // avoid redefined macro in unified build |
539 | | #undef ILOG |