Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp
Warning:line 2057, column 9
Value stored to 'mustBackoff' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_dom_media_ogg0.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dom/media/ogg -fcoverage-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dom/media/ogg -resource-dir /usr/lib/llvm-18/lib/clang/18 -include /var/lib/jenkins/workspace/firefox-scan-build/config/gcc_hidden.h -include /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D STATIC_EXPORTABLE_JS_API -I /var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dom/media/ogg -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/security/rlbox -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/x86_64-linux-gnu/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -fdeprecated-macro -ferror-limit 19 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2024-05-02-011441-20010-1 -x c++ Unified_cpp_dom_media_ogg0.cpp
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 "OggDemuxer.h"
8#include "OggRLBox.h"
9#include "MediaDataDemuxer.h"
10#include "OggCodecState.h"
11#include "TimeUnits.h"
12#include "XiphExtradata.h"
13#include "mozilla/AbstractThread.h"
14#include "mozilla/Atomics.h"
15#include "mozilla/PodOperations.h"
16#include "mozilla/ScopeExit.h"
17#include "mozilla/SchedulerGroup.h"
18#include "mozilla/SharedThreadPool.h"
19#include "mozilla/Telemetry.h"
20#include "mozilla/TimeStamp.h"
21#include "nsDebug.h"
22#include "nsAutoRef.h"
23#include "nsError.h"
24
25#include <algorithm>
26
27extern mozilla::LazyLogModule gMediaDemuxerLog;
28#define OGG_DEBUG(arg, ...) \
29 DDMOZ_LOG(gMediaDemuxerLog, mozilla::LogLevel::Debug, "::%s: " arg, \do { if (DecoderDoctorLogger::IsDDLoggingEnabled() || (__builtin_expect
(!!(mozilla::detail::log_test(gMediaDemuxerLog, mozilla::LogLevel
::Debug)), 0))) { DDLOGPRCheck("::%s: " arg, __func__, ##__VA_ARGS__
); DecoderDoctorLogger::MozLogPrintf(this, gMediaDemuxerLog, mozilla
::LogLevel::Debug, "::%s: " arg, __func__, ##__VA_ARGS__); } }
while (0)
30 __func__, ##__VA_ARGS__)do { if (DecoderDoctorLogger::IsDDLoggingEnabled() || (__builtin_expect
(!!(mozilla::detail::log_test(gMediaDemuxerLog, mozilla::LogLevel
::Debug)), 0))) { DDLOGPRCheck("::%s: " arg, __func__, ##__VA_ARGS__
); DecoderDoctorLogger::MozLogPrintf(this, gMediaDemuxerLog, mozilla
::LogLevel::Debug, "::%s: " arg, __func__, ##__VA_ARGS__); } }
while (0)
31
32// Un-comment to enable logging of seek bisections.
33// #define SEEK_LOGGING
34#ifdef SEEK_LOGGING
35# define SEEK_LOG(type, msg) MOZ_LOG(gMediaDemuxerLog, type, msg)do { const ::mozilla::LogModule* moz_real_module = gMediaDemuxerLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, type)), 0))) { mozilla::detail::log_print(moz_real_module, type
, MOZ_LOG_EXPAND_ARGS msg); } } while (0)
36#else
37# define SEEK_LOG(type, msg)
38#endif
39
40#define CopyAndVerifyOrFail(t, cond, failed) \
41 (t).copy_and_verify([&](auto val) { \
42 if (!(cond)) { \
43 *(failed) = true; \
44 } \
45 return val; \
46 })
47
48namespace mozilla {
49
50using media::TimeInterval;
51using media::TimeIntervals;
52using media::TimeUnit;
53
54// The number of microseconds of "fuzz" we use in a bisection search over
55// HTTP. When we're seeking with fuzz, we'll stop the search if a bisection
56// lands between the seek target and OGG_SEEK_FUZZ_USECS microseconds before the
57// seek target. This is becaue it's usually quicker to just keep downloading
58// from an exisiting connection than to do another bisection inside that
59// small range, which would open a new HTTP connetion.
60static const TimeUnit OGG_SEEK_FUZZ_USECS = TimeUnit::FromMicroseconds(500000);
61
62// The number of microseconds of "pre-roll" we use for Opus streams.
63// The specification recommends 80 ms.
64static const TimeUnit OGG_SEEK_OPUS_PREROLL = TimeUnit::FromMicroseconds(80000);
65
66static Atomic<uint32_t> sStreamSourceID(0u);
67
68OggDemuxer::nsAutoOggSyncState::nsAutoOggSyncState(rlbox_sandbox_ogg* aSandbox)
69 : mSandbox(aSandbox) {
70 if (mSandbox) {
71 tainted_ogg<ogg_sync_state*> state =
72 mSandbox->malloc_in_sandbox<ogg_sync_state>();
73 MOZ_RELEASE_ASSERT(state != nullptr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(state != nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(state != nullptr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("state != nullptr"
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 73); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "state != nullptr"
")"); do { *((volatile int*)__null) = 73; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
74 mState = state.to_opaque();
75 sandbox_invoke(*mSandbox, ogg_sync_init, mState)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_init)>( "ogg_sync_init", reinterpret_cast<void
*>(&ogg_sync_init), mState)
;
76 }
77}
78OggDemuxer::nsAutoOggSyncState::~nsAutoOggSyncState() {
79 if (mSandbox) {
80 sandbox_invoke(*mSandbox, ogg_sync_clear, mState)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_clear)>( "ogg_sync_clear", reinterpret_cast<void
*>(&ogg_sync_clear), mState)
;
81 mSandbox->free_in_sandbox(rlbox::from_opaque(mState));
82 tainted_ogg<ogg_sync_state*> null = nullptr;
83 mState = null.to_opaque();
84 }
85}
86
87/* static */
88rlbox_sandbox_ogg* OggDemuxer::CreateSandbox() {
89 rlbox_sandbox_ogg* sandbox = new rlbox_sandbox_ogg();
90#ifdef MOZ_WASM_SANDBOXING_OGG
91 bool success = sandbox->create_sandbox(false /* infallible */);
92#else
93 bool success = sandbox->create_sandbox();
94#endif
95 if (!success) {
96 delete sandbox;
97 sandbox = nullptr;
98 }
99 return sandbox;
100}
101
102void OggDemuxer::SandboxDestroy::operator()(rlbox_sandbox_ogg* sandbox) {
103 if (sandbox) {
104 sandbox->destroy_sandbox();
105 delete sandbox;
106 }
107}
108
109// Return the corresponding category in aKind based on the following specs.
110// (https://www.whatwg.org/specs/web-apps/current-
111// work/multipage/embedded-content.html#dom-audiotrack-kind) &
112// (http://wiki.xiph.org/SkeletonHeaders)
113nsString OggDemuxer::GetKind(const nsCString& aRole) {
114 if (aRole.Find("audio/main") != -1 || aRole.Find("video/main") != -1) {
115 return u"main"_ns;
116 }
117 if (aRole.Find("audio/alternate") != -1 ||
118 aRole.Find("video/alternate") != -1) {
119 return u"alternative"_ns;
120 }
121 if (aRole.Find("audio/audiodesc") != -1) {
122 return u"descriptions"_ns;
123 }
124 if (aRole.Find("audio/described") != -1) {
125 return u"main-desc"_ns;
126 }
127 if (aRole.Find("audio/dub") != -1) {
128 return u"translation"_ns;
129 }
130 if (aRole.Find("audio/commentary") != -1) {
131 return u"commentary"_ns;
132 }
133 if (aRole.Find("video/sign") != -1) {
134 return u"sign"_ns;
135 }
136 if (aRole.Find("video/captioned") != -1) {
137 return u"captions"_ns;
138 }
139 if (aRole.Find("video/subtitled") != -1) {
140 return u"subtitles"_ns;
141 }
142 return u""_ns;
143}
144
145void OggDemuxer::InitTrack(MessageField* aMsgInfo, TrackInfo* aInfo,
146 bool aEnable) {
147 MOZ_ASSERT(aMsgInfo)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aMsgInfo)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aMsgInfo))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aMsgInfo", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 147); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aMsgInfo" ")"
); do { *((volatile int*)__null) = 147; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
148 MOZ_ASSERT(aInfo)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aInfo)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(aInfo))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("aInfo", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 148); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aInfo" ")");
do { *((volatile int*)__null) = 148; __attribute__((nomerge)
) ::abort(); } while (false); } } while (false)
;
149
150 nsCString* sName = aMsgInfo->mValuesStore.Get(eName);
151 nsCString* sRole = aMsgInfo->mValuesStore.Get(eRole);
152 nsCString* sTitle = aMsgInfo->mValuesStore.Get(eTitle);
153 nsCString* sLanguage = aMsgInfo->mValuesStore.Get(eLanguage);
154 aInfo->Init(sName ? NS_ConvertUTF8toUTF16(*sName) : EmptyString(),
155 sRole ? GetKind(*sRole) : u""_ns,
156 sTitle ? NS_ConvertUTF8toUTF16(*sTitle) : EmptyString(),
157 sLanguage ? NS_ConvertUTF8toUTF16(*sLanguage) : EmptyString(),
158 aEnable);
159}
160
161OggDemuxer::OggDemuxer(MediaResource* aResource)
162 : mSandbox(CreateSandbox()),
163 mTheoraState(nullptr),
164 mVorbisState(nullptr),
165 mOpusState(nullptr),
166 mFlacState(nullptr),
167 mOpusEnabled(MediaDecoder::IsOpusEnabled()),
168 mSkeletonState(nullptr),
169 mAudioOggState(aResource, mSandbox.get()),
170 mVideoOggState(aResource, mSandbox.get()),
171 mIsChained(false),
172 mTimedMetadataEvent(nullptr),
173 mOnSeekableEvent(nullptr) {
174 MOZ_COUNT_CTOR(OggDemuxer)do { static_assert(std::is_class_v<OggDemuxer>, "Token '"
"OggDemuxer" "' is not a class type."); static_assert(!std::
is_base_of<nsISupports, OggDemuxer>::value, "nsISupports classes don't need to call MOZ_COUNT_CTOR or "
"MOZ_COUNT_DTOR");; NS_LogCtor((void*)this, "OggDemuxer", sizeof
(*this)); } while (0)
;
175 // aResource is referenced through inner m{Audio,Video}OffState members.
176 DDLINKCHILD("resource", aResource)do { if (DecoderDoctorLogger::IsDDLoggingEnabled()) { DecoderDoctorLogger
::LinkParentAndChild(this, "resource", aResource); } } while (
0)
;
177}
178
179OggDemuxer::~OggDemuxer() {
180 MOZ_COUNT_DTOR(OggDemuxer)do { static_assert(std::is_class_v<OggDemuxer>, "Token '"
"OggDemuxer" "' is not a class type."); static_assert(!std::
is_base_of<nsISupports, OggDemuxer>::value, "nsISupports classes don't need to call MOZ_COUNT_CTOR or "
"MOZ_COUNT_DTOR");; NS_LogDtor((void*)this, "OggDemuxer", sizeof
(*this)); } while (0)
;
181 Reset(TrackInfo::kAudioTrack);
182 Reset(TrackInfo::kVideoTrack);
183}
184
185void OggDemuxer::SetChainingEvents(TimedMetadataEventProducer* aMetadataEvent,
186 MediaEventProducer<void>* aOnSeekableEvent) {
187 mTimedMetadataEvent = aMetadataEvent;
188 mOnSeekableEvent = aOnSeekableEvent;
189}
190
191bool OggDemuxer::HasAudio() const {
192 return mVorbisState || mOpusState || mFlacState;
193}
194
195bool OggDemuxer::HasVideo() const { return mTheoraState; }
196
197bool OggDemuxer::HaveStartTime() const { return mStartTime.isSome(); }
198
199TimeUnit OggDemuxer::StartTime() const {
200 return mStartTime.refOr(TimeUnit::Zero());
201}
202
203bool OggDemuxer::HaveStartTime(TrackInfo::TrackType aType) {
204 return OggState(aType).mStartTime.isSome();
205}
206
207TimeUnit OggDemuxer::StartTime(TrackInfo::TrackType aType) {
208 return OggState(aType).mStartTime.refOr(TimeUnit::Zero());
209}
210
211RefPtr<OggDemuxer::InitPromise> OggDemuxer::Init() {
212 if (!mSandbox) {
213 return InitPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
214 }
215 const char RLBOX_OGG_RETURN_CODE_SAFE[] =
216 "Return codes only control whether to early exit. Incorrect return codes "
217 "will not lead to memory safety issues in the renderer.";
218
219 int ret = sandbox_invoke(*mSandbox, ogg_sync_init,(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_init)>( "ogg_sync_init", reinterpret_cast<void
*>(&ogg_sync_init), OggSyncState(TrackInfo::kAudioTrack
))
220 OggSyncState(TrackInfo::kAudioTrack))(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_init)>( "ogg_sync_init", reinterpret_cast<void
*>(&ogg_sync_init), OggSyncState(TrackInfo::kAudioTrack
))
221 .unverified_safe_because(RLBOX_OGG_RETURN_CODE_SAFE);
222 if (ret != 0) {
223 return InitPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
224 }
225 ret = sandbox_invoke(*mSandbox, ogg_sync_init,(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_init)>( "ogg_sync_init", reinterpret_cast<void
*>(&ogg_sync_init), OggSyncState(TrackInfo::kVideoTrack
))
226 OggSyncState(TrackInfo::kVideoTrack))(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_init)>( "ogg_sync_init", reinterpret_cast<void
*>(&ogg_sync_init), OggSyncState(TrackInfo::kVideoTrack
))
227 .unverified_safe_because(RLBOX_OGG_RETURN_CODE_SAFE);
228 if (ret != 0) {
229 return InitPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
230 }
231 if (ReadMetadata() != NS_OK) {
232 return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_METADATA_ERR,
233 __func__);
234 }
235
236 if (!GetNumberTracks(TrackInfo::kAudioTrack) &&
237 !GetNumberTracks(TrackInfo::kVideoTrack)) {
238 return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_METADATA_ERR,
239 __func__);
240 }
241
242 return InitPromise::CreateAndResolve(NS_OK, __func__);
243}
244
245OggCodecState* OggDemuxer::GetTrackCodecState(
246 TrackInfo::TrackType aType) const {
247 switch (aType) {
248 case TrackInfo::kAudioTrack:
249 if (mVorbisState) {
250 return mVorbisState;
251 } else if (mOpusState) {
252 return mOpusState;
253 } else {
254 return mFlacState;
255 }
256 case TrackInfo::kVideoTrack:
257 return mTheoraState;
258 default:
259 return nullptr;
260 }
261}
262
263TrackInfo::TrackType OggDemuxer::GetCodecStateType(
264 OggCodecState* aState) const {
265 switch (aState->GetType()) {
266 case OggCodecState::TYPE_THEORA:
267 return TrackInfo::kVideoTrack;
268 case OggCodecState::TYPE_OPUS:
269 case OggCodecState::TYPE_VORBIS:
270 case OggCodecState::TYPE_FLAC:
271 return TrackInfo::kAudioTrack;
272 default:
273 return TrackInfo::kUndefinedTrack;
274 }
275}
276
277uint32_t OggDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const {
278 switch (aType) {
279 case TrackInfo::kAudioTrack:
280 return HasAudio() ? 1 : 0;
281 case TrackInfo::kVideoTrack:
282 return HasVideo() ? 1 : 0;
283 default:
284 return 0;
285 }
286}
287
288UniquePtr<TrackInfo> OggDemuxer::GetTrackInfo(TrackInfo::TrackType aType,
289 size_t aTrackNumber) const {
290 switch (aType) {
291 case TrackInfo::kAudioTrack:
292 return mInfo.mAudio.Clone();
293 case TrackInfo::kVideoTrack:
294 return mInfo.mVideo.Clone();
295 default:
296 return nullptr;
297 }
298}
299
300already_AddRefed<MediaTrackDemuxer> OggDemuxer::GetTrackDemuxer(
301 TrackInfo::TrackType aType, uint32_t aTrackNumber) {
302 if (GetNumberTracks(aType) <= aTrackNumber) {
303 return nullptr;
304 }
305 RefPtr<OggTrackDemuxer> e = new OggTrackDemuxer(this, aType, aTrackNumber);
306 DDLINKCHILD("track demuxer", e.get())do { if (DecoderDoctorLogger::IsDDLoggingEnabled()) { DecoderDoctorLogger
::LinkParentAndChild(this, "track demuxer", e.get()); } } while
(0)
;
307 mDemuxers.AppendElement(e);
308
309 return e.forget();
310}
311
312nsresult OggDemuxer::Reset(TrackInfo::TrackType aType) {
313 // Discard any previously buffered packets/pages.
314 if (mSandbox) {
315 sandbox_invoke(*mSandbox, ogg_sync_reset, OggSyncState(aType))(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_reset)>( "ogg_sync_reset", reinterpret_cast<void
*>(&ogg_sync_reset), OggSyncState(aType))
;
316 }
317 OggCodecState* trackState = GetTrackCodecState(aType);
318 if (trackState) {
319 return trackState->Reset();
320 }
321 OggState(aType).mNeedKeyframe = true;
322 return NS_OK;
323}
324
325bool OggDemuxer::ReadHeaders(TrackInfo::TrackType aType,
326 OggCodecState* aState) {
327 while (!aState->DoneReadingHeaders()) {
328 DemuxUntilPacketAvailable(aType, aState);
329 OggPacketPtr packet = aState->PacketOut();
330 if (!packet) {
331 OGG_DEBUG("Ran out of header packets early; deactivating stream %" PRIu32"u",
332 aState->mSerial);
333 aState->Deactivate();
334 return false;
335 }
336
337 // Local OggCodecState needs to decode headers in order to process
338 // packet granulepos -> time mappings, etc.
339 if (!aState->DecodeHeader(std::move(packet))) {
340 OGG_DEBUG(
341 "Failed to decode ogg header packet; deactivating stream %" PRIu32"u",
342 aState->mSerial);
343 aState->Deactivate();
344 return false;
345 }
346 }
347
348 return aState->Init();
349}
350
351void OggDemuxer::BuildSerialList(nsTArray<uint32_t>& aTracks) {
352 // Obtaining seek index information for currently active bitstreams.
353 if (HasVideo()) {
354 aTracks.AppendElement(mTheoraState->mSerial);
355 }
356 if (HasAudio()) {
357 if (mVorbisState) {
358 aTracks.AppendElement(mVorbisState->mSerial);
359 } else if (mOpusState) {
360 aTracks.AppendElement(mOpusState->mSerial);
361 }
362 }
363}
364
365void OggDemuxer::SetupTarget(OggCodecState** aSavedState,
366 OggCodecState* aNewState) {
367 if (*aSavedState) {
368 (*aSavedState)->Reset();
369 }
370
371 if (aNewState->GetInfo()->GetAsAudioInfo()) {
372 mInfo.mAudio = *aNewState->GetInfo()->GetAsAudioInfo();
373 } else {
374 mInfo.mVideo = *aNewState->GetInfo()->GetAsVideoInfo();
375 }
376 *aSavedState = aNewState;
377}
378
379void OggDemuxer::SetupTargetSkeleton() {
380 // Setup skeleton related information after mVorbisState & mTheroState
381 // being set (if they exist).
382 if (mSkeletonState) {
383 if (!HasAudio() && !HasVideo()) {
384 // We have a skeleton track, but no audio or video, may as well disable
385 // the skeleton, we can't do anything useful with this media.
386 OGG_DEBUG("Deactivating skeleton stream %" PRIu32"u",
387 mSkeletonState->mSerial);
388 mSkeletonState->Deactivate();
389 } else if (ReadHeaders(TrackInfo::kAudioTrack, mSkeletonState) &&
390 mSkeletonState->HasIndex()) {
391 // We don't particularly care about which track we are currently using
392 // as both MediaResource points to the same content.
393 // Extract the duration info out of the index, so we don't need to seek to
394 // the end of resource to get it.
395 nsTArray<uint32_t> tracks;
396 BuildSerialList(tracks);
397 TimeUnit duration = TimeUnit::Zero();
398 if (NS_SUCCEEDED(mSkeletonState->GetDuration(tracks, duration))((bool)(__builtin_expect(!!(!NS_FAILED_impl(mSkeletonState->
GetDuration(tracks, duration))), 1)))
) {
399 OGG_DEBUG("Got duration from Skeleton index %s",
400 duration.ToString().get());
401 mInfo.mMetadataDuration.emplace(duration);
402 }
403 }
404 }
405}
406
407void OggDemuxer::SetupMediaTracksInfo(const nsTArray<uint32_t>& aSerials) {
408 // For each serial number
409 // 1. Retrieve a codecState from mCodecStore by this serial number.
410 // 2. Retrieve a message field from mMsgFieldStore by this serial number.
411 // 3. For now, skip if the serial number refers to a non-primary bitstream.
412 // 4. Setup track and other audio/video related information per different
413 // types.
414 for (size_t i = 0; i < aSerials.Length(); i++) {
415 uint32_t serial = aSerials[i];
416 OggCodecState* codecState = mCodecStore.Get(serial);
417
418 MessageField* msgInfo = nullptr;
419 if (mSkeletonState) {
420 mSkeletonState->mMsgFieldStore.Get(serial, &msgInfo);
421 }
422
423 OggCodecState* primeState = nullptr;
424 switch (codecState->GetType()) {
425 case OggCodecState::TYPE_THEORA:
426 primeState = mTheoraState;
427 break;
428 case OggCodecState::TYPE_VORBIS:
429 primeState = mVorbisState;
430 break;
431 case OggCodecState::TYPE_OPUS:
432 primeState = mOpusState;
433 break;
434 case OggCodecState::TYPE_FLAC:
435 primeState = mFlacState;
436 break;
437 default:
438 break;
439 }
440 if (primeState && primeState == codecState) {
441 bool isAudio = primeState->GetInfo()->GetAsAudioInfo();
442 if (msgInfo) {
443 InitTrack(
444 msgInfo,
445 isAudio ? static_cast<TrackInfo*>(&mInfo.mAudio) : &mInfo.mVideo,
446 true);
447 }
448 FillTags(isAudio ? static_cast<TrackInfo*>(&mInfo.mAudio) : &mInfo.mVideo,
449 primeState->GetTags());
450 }
451 }
452}
453
454void OggDemuxer::FillTags(TrackInfo* aInfo, UniquePtr<MetadataTags>&& aTags) {
455 if (!aTags) {
456 return;
457 }
458 UniquePtr<MetadataTags> tags(std::move(aTags));
459 for (const auto& entry : *tags) {
460 aInfo->mTags.AppendElement(MetadataTag(entry.GetKey(), entry.GetData()));
461 }
462}
463
464nsresult OggDemuxer::ReadMetadata() {
465 OGG_DEBUG("OggDemuxer::ReadMetadata called!");
466
467 // We read packets until all bitstreams have read all their header packets.
468 // We record the offset of the first non-header page so that we know
469 // what page to seek to when seeking to the media start.
470
471 // @FIXME we have to read all the header packets on all the streams
472 // and THEN we can run SetupTarget*
473 // @fixme fixme
474
475 TrackInfo::TrackType tracks[2] = {TrackInfo::kAudioTrack,
476 TrackInfo::kVideoTrack};
477
478 nsTArray<OggCodecState*> bitstreams;
479 nsTArray<uint32_t> serials;
480
481 for (auto& track : tracks) {
482 tainted_ogg<ogg_page*> page = mSandbox->malloc_in_sandbox<ogg_page>();
483 if (!page) {
484 return NS_ERROR_OUT_OF_MEMORY;
485 }
486 auto clean_page = MakeScopeExit([&] { mSandbox->free_in_sandbox(page); });
487
488 bool readAllBOS = false;
489 while (!readAllBOS) {
490 if (!ReadOggPage(track, page.to_opaque())) {
491 // Some kind of error...
492 OGG_DEBUG("OggDemuxer::ReadOggPage failed? leaving ReadMetadata...");
493 return NS_ERROR_FAILURE;
494 }
495
496 uint32_t serial = static_cast<uint32_t>(
497 sandbox_invoke(*mSandbox, ogg_page_serialno, page)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_page_serialno)>( "ogg_page_serialno", reinterpret_cast
<void*>(&ogg_page_serialno), page)
498 .unverified_safe_because(RLBOX_OGG_PAGE_SERIAL_REASON));
499
500 if (!sandbox_invoke(*mSandbox, ogg_page_bos, page)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_page_bos)>( "ogg_page_bos", reinterpret_cast<void*
>(&ogg_page_bos), page)
501 .unverified_safe_because(
502 "If this value is incorrect, it would mean not all "
503 "bitstreams are read. This does not affect the memory "
504 "safety of the renderer.")) {
505 // We've encountered a non Beginning Of Stream page. No more BOS pages
506 // can follow in this Ogg segment, so there will be no other bitstreams
507 // in the Ogg (unless it's invalid).
508 readAllBOS = true;
509 } else if (!mCodecStore.Contains(serial)) {
510 // We've not encountered a stream with this serial number before. Create
511 // an OggCodecState to demux it, and map that to the OggCodecState
512 // in mCodecStates.
513 OggCodecState* const codecState = mCodecStore.Add(
514 serial,
515 OggCodecState::Create(mSandbox.get(), page.to_opaque(), serial));
516 bitstreams.AppendElement(codecState);
517 serials.AppendElement(serial);
518 }
519 if (NS_FAILED(DemuxOggPage(track, page.to_opaque()))((bool)(__builtin_expect(!!(NS_FAILED_impl(DemuxOggPage(track
, page.to_opaque()))), 0)))
) {
520 return NS_ERROR_FAILURE;
521 }
522 }
523 }
524
525 // We've read all BOS pages, so we know the streams contained in the media.
526 // 1. Find the first encountered Theora/Vorbis/Opus bitstream, and configure
527 // it as the target A/V bitstream.
528 // 2. Deactivate the rest of bitstreams for now, until we have MediaInfo
529 // support multiple track infos.
530 for (uint32_t i = 0; i < bitstreams.Length(); ++i) {
531 OggCodecState* s = bitstreams[i];
532 if (s) {
533 if (s->GetType() == OggCodecState::TYPE_THEORA &&
534 ReadHeaders(TrackInfo::kVideoTrack, s)) {
535 if (!mTheoraState) {
536 SetupTarget(&mTheoraState, s);
537 } else {
538 s->Deactivate();
539 }
540 } else if (s->GetType() == OggCodecState::TYPE_VORBIS &&
541 ReadHeaders(TrackInfo::kAudioTrack, s)) {
542 if (!mVorbisState) {
543 SetupTarget(&mVorbisState, s);
544 } else {
545 s->Deactivate();
546 }
547 } else if (s->GetType() == OggCodecState::TYPE_OPUS &&
548 ReadHeaders(TrackInfo::kAudioTrack, s)) {
549 if (mOpusEnabled) {
550 if (!mOpusState) {
551 SetupTarget(&mOpusState, s);
552 } else {
553 s->Deactivate();
554 }
555 } else {
556 NS_WARNING(NS_DebugBreak(NS_DEBUG_WARNING, "Opus decoding disabled." " See media.opus.enabled in about:config"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 558)
557 "Opus decoding disabled."NS_DebugBreak(NS_DEBUG_WARNING, "Opus decoding disabled." " See media.opus.enabled in about:config"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 558)
558 " See media.opus.enabled in about:config")NS_DebugBreak(NS_DEBUG_WARNING, "Opus decoding disabled." " See media.opus.enabled in about:config"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 558)
;
559 }
560 } else if (s->GetType() == OggCodecState::TYPE_FLAC &&
561 ReadHeaders(TrackInfo::kAudioTrack, s)) {
562 if (!mFlacState) {
563 SetupTarget(&mFlacState, s);
564 } else {
565 s->Deactivate();
566 }
567 } else if (s->GetType() == OggCodecState::TYPE_SKELETON &&
568 !mSkeletonState) {
569 mSkeletonState = static_cast<SkeletonState*>(s);
570 } else {
571 // Deactivate any non-primary bitstreams.
572 s->Deactivate();
573 }
574 }
575 }
576
577 SetupTargetSkeleton();
578 SetupMediaTracksInfo(serials);
579
580 if (HasAudio() || HasVideo()) {
581 TimeUnit startTime = TimeUnit::Invalid();
582 FindStartTime(startTime);
583 if (startTime.IsValid()) {
584 OGG_DEBUG("Detected stream start time %s", startTime.ToString().get());
585 mStartTime.emplace(startTime);
586 }
587
588 if (mInfo.mMetadataDuration.isNothing() &&
589 Resource(TrackInfo::kAudioTrack)->GetLength() >= 0) {
590 // We didn't get a duration from the index or a Content-Duration header.
591 // Seek to the end of file to find the end time.
592 int64_t length = Resource(TrackInfo::kAudioTrack)->GetLength();
593
594 MOZ_ASSERT(length > 0, "Must have a content length to get end time")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(length > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(length > 0))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("length > 0" " ("
"Must have a content length to get end time" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 594); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length > 0"
") (" "Must have a content length to get end time" ")"); do {
*((volatile int*)__null) = 594; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
595
596 TimeUnit endTime = RangeEndTime(TrackInfo::kAudioTrack, length);
597
598 if (endTime.IsValid() && endTime.IsPositive()) {
599 mInfo.mUnadjustedMetadataEndTime.emplace(endTime);
600 TimeUnit computedDuration =
601 endTime - mStartTime.refOr(TimeUnit::Zero());
602 if (computedDuration.IsPositive()) {
603 mInfo.mMetadataDuration.emplace(computedDuration);
604 OGG_DEBUG("Got Ogg duration from seeking to end %s",
605 computedDuration.ToString().get());
606 } else {
607 OGG_DEBUG("Ignoring incorect start time in metadata");
608 mStartTime.reset();
609 }
610 }
611 }
612 if (mInfo.mMetadataDuration.isNothing()) {
613 OGG_DEBUG("Couldn't determine OGG file duration.");
614 mInfo.mMetadataDuration.emplace(TimeUnit::FromInfinity());
615 }
616 if (HasAudio()) {
617 mInfo.mAudio.mDuration = mInfo.mMetadataDuration.ref();
618 }
619 if (HasVideo()) {
620 mInfo.mVideo.mDuration = mInfo.mMetadataDuration.ref();
621 }
622 } else {
623 OGG_DEBUG("no audio or video tracks");
624 return NS_ERROR_FAILURE;
625 }
626
627 OGG_DEBUG("success?!");
628 return NS_OK;
629}
630
631void OggDemuxer::SetChained() {
632 {
633 if (mIsChained) {
634 return;
635 }
636 mIsChained = true;
637 }
638 if (mOnSeekableEvent) {
639 mOnSeekableEvent->Notify();
640 }
641}
642
643bool OggDemuxer::ReadOggChain(const media::TimeUnit& aLastEndTime) {
644 bool chained = false;
645 OpusState* newOpusState = nullptr;
646 VorbisState* newVorbisState = nullptr;
647 FlacState* newFlacState = nullptr;
648 UniquePtr<MetadataTags> tags;
649
650 if (HasVideo() || HasSkeleton() || !HasAudio()) {
651 return false;
652 }
653
654 tainted_ogg<ogg_page*> page = mSandbox->malloc_in_sandbox<ogg_page>();
655 if (!page) {
656 return false;
657 }
658 auto clean_page = MakeScopeExit([&] { mSandbox->free_in_sandbox(page); });
659 if (!ReadOggPage(TrackInfo::kAudioTrack, page.to_opaque()) ||
660 !sandbox_invoke(*mSandbox, ogg_page_bos, page)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_page_bos)>( "ogg_page_bos", reinterpret_cast<void*
>(&ogg_page_bos), page)
661 .unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON)) {
662 // Chaining is only supported for audio only ogg files.
663 return false;
664 }
665
666 uint32_t serial = static_cast<uint32_t>(
667 sandbox_invoke(*mSandbox, ogg_page_serialno, page)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_page_serialno)>( "ogg_page_serialno", reinterpret_cast
<void*>(&ogg_page_serialno), page)
668 .unverified_safe_because(
669 "We are reading a new page with a serial number for the first "
670 "time and will check if we have seen it before prior to use."));
671 if (mCodecStore.Contains(serial)) {
672 return false;
673 }
674
675 UniquePtr<OggCodecState> codecState(
676 OggCodecState::Create(mSandbox.get(), page.to_opaque(), serial));
677 if (!codecState) {
678 return false;
679 }
680
681 if (mVorbisState && (codecState->GetType() == OggCodecState::TYPE_VORBIS)) {
682 newVorbisState = static_cast<VorbisState*>(codecState.get());
683 } else if (mOpusState &&
684 (codecState->GetType() == OggCodecState::TYPE_OPUS)) {
685 newOpusState = static_cast<OpusState*>(codecState.get());
686 } else if (mFlacState &&
687 (codecState->GetType() == OggCodecState::TYPE_FLAC)) {
688 newFlacState = static_cast<FlacState*>(codecState.get());
689 } else {
690 return false;
691 }
692
693 OggCodecState* state;
694
695 mCodecStore.Add(serial, std::move(codecState));
696 state = mCodecStore.Get(serial);
697
698 NS_ENSURE_TRUE(state != nullptr, false)do { if ((__builtin_expect(!!(!(state != nullptr)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "state != nullptr" ") failed"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 698); return false; } } while (false)
;
699
700 if (NS_FAILED(state->PageIn(page.to_opaque()))((bool)(__builtin_expect(!!(NS_FAILED_impl(state->PageIn(page
.to_opaque()))), 0)))
) {
701 return false;
702 }
703
704 MessageField* msgInfo = nullptr;
705 if (mSkeletonState) {
706 mSkeletonState->mMsgFieldStore.Get(serial, &msgInfo);
707 }
708
709 if ((newVorbisState && ReadHeaders(TrackInfo::kAudioTrack, newVorbisState)) &&
710 (mVorbisState->GetInfo()->GetAsAudioInfo()->mRate ==
711 newVorbisState->GetInfo()->GetAsAudioInfo()->mRate) &&
712 (mVorbisState->GetInfo()->GetAsAudioInfo()->mChannels ==
713 newVorbisState->GetInfo()->GetAsAudioInfo()->mChannels)) {
714 SetupTarget(&mVorbisState, newVorbisState);
715 OGG_DEBUG("New vorbis ogg link, serial=%d\n", mVorbisState->mSerial);
716
717 if (msgInfo) {
718 InitTrack(msgInfo, &mInfo.mAudio, true);
719 }
720
721 chained = true;
722 tags = newVorbisState->GetTags();
723 }
724
725 if ((newOpusState && ReadHeaders(TrackInfo::kAudioTrack, newOpusState)) &&
726 (mOpusState->GetInfo()->GetAsAudioInfo()->mRate ==
727 newOpusState->GetInfo()->GetAsAudioInfo()->mRate) &&
728 (mOpusState->GetInfo()->GetAsAudioInfo()->mChannels ==
729 newOpusState->GetInfo()->GetAsAudioInfo()->mChannels)) {
730 SetupTarget(&mOpusState, newOpusState);
731
732 if (msgInfo) {
733 InitTrack(msgInfo, &mInfo.mAudio, true);
734 }
735
736 chained = true;
737 tags = newOpusState->GetTags();
738 }
739
740 if ((newFlacState && ReadHeaders(TrackInfo::kAudioTrack, newFlacState)) &&
741 (mFlacState->GetInfo()->GetAsAudioInfo()->mRate ==
742 newFlacState->GetInfo()->GetAsAudioInfo()->mRate) &&
743 (mFlacState->GetInfo()->GetAsAudioInfo()->mChannels ==
744 newFlacState->GetInfo()->GetAsAudioInfo()->mChannels)) {
745 SetupTarget(&mFlacState, newFlacState);
746 OGG_DEBUG("New flac ogg link, serial=%d\n", mFlacState->mSerial);
747
748 if (msgInfo) {
749 InitTrack(msgInfo, &mInfo.mAudio, true);
750 }
751
752 chained = true;
753 tags = newFlacState->GetTags();
754 }
755
756 if (chained) {
757 SetChained();
758 mInfo.mMediaSeekable = false;
759 mDecodedAudioDuration += aLastEndTime;
760 if (mTimedMetadataEvent) {
761 mTimedMetadataEvent->Notify(
762 TimedMetadata(mDecodedAudioDuration, std::move(tags),
763 UniquePtr<MediaInfo>(new MediaInfo(mInfo))));
764 }
765 // Setup a new TrackInfo so that the MediaFormatReader will flush the
766 // current decoder.
767 mSharedAudioTrackInfo =
768 new TrackInfoSharedPtr(mInfo.mAudio, ++sStreamSourceID);
769 return true;
770 }
771
772 return false;
773}
774
775OggDemuxer::OggStateContext& OggDemuxer::OggState(TrackInfo::TrackType aType) {
776 if (aType == TrackInfo::kVideoTrack) {
777 return mVideoOggState;
778 }
779 return mAudioOggState;
780}
781
782tainted_opaque_ogg<ogg_sync_state*> OggDemuxer::OggSyncState(
783 TrackInfo::TrackType aType) {
784 return OggState(aType).mOggState.mState;
785}
786
787MediaResourceIndex* OggDemuxer::Resource(TrackInfo::TrackType aType) {
788 return &OggState(aType).mResource;
789}
790
791MediaResourceIndex* OggDemuxer::CommonResource() {
792 return &mAudioOggState.mResource;
793}
794
795bool OggDemuxer::ReadOggPage(TrackInfo::TrackType aType,
796 tainted_opaque_ogg<ogg_page*> aPage) {
797 int ret = 0;
798 while ((ret = sandbox_invoke(*mSandbox, ogg_sync_pageseek,(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_pageseek)>( "ogg_sync_pageseek", reinterpret_cast
<void*>(&ogg_sync_pageseek), OggSyncState(aType), aPage
)
799 OggSyncState(aType), aPage)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_pageseek)>( "ogg_sync_pageseek", reinterpret_cast
<void*>(&ogg_sync_pageseek), OggSyncState(aType), aPage
)
800 .unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON)) <=
801 0) {
802 if (ret < 0) {
803 // Lost page sync, have to skip up to next page.
804 continue;
805 }
806 // Returns a buffer that can be written too
807 // with the given size. This buffer is stored
808 // in the ogg synchronisation structure.
809 const uint32_t MIN_BUFFER_SIZE = 4096;
810 tainted_ogg<char*> buffer_tainted = sandbox_invoke((*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_buffer)>( "ogg_sync_buffer", reinterpret_cast<
void*>(&ogg_sync_buffer), OggSyncState(aType), MIN_BUFFER_SIZE
)
811 *mSandbox, ogg_sync_buffer, OggSyncState(aType), MIN_BUFFER_SIZE)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_buffer)>( "ogg_sync_buffer", reinterpret_cast<
void*>(&ogg_sync_buffer), OggSyncState(aType), MIN_BUFFER_SIZE
)
;
812 MOZ_ASSERT(buffer_tainted != nullptr, "ogg_sync_buffer failed")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(buffer_tainted != nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(buffer_tainted != nullptr)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("buffer_tainted != nullptr"
" (" "ogg_sync_buffer failed" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 812); AnnotateMozCrashReason("MOZ_ASSERT" "(" "buffer_tainted != nullptr"
") (" "ogg_sync_buffer failed" ")"); do { *((volatile int*)__null
) = 812; __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
813
814 // Read from the resource into the buffer
815 uint32_t bytesRead = 0;
816
817 char* buffer = buffer_tainted.copy_and_verify_buffer_address(
818 [](uintptr_t val) { return reinterpret_cast<char*>(val); },
819 MIN_BUFFER_SIZE);
820
821 nsresult rv = Resource(aType)->Read(buffer, MIN_BUFFER_SIZE, &bytesRead);
822 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) || !bytesRead) {
823 // End of file or error.
824 return false;
825 }
826
827 // Update the synchronisation layer with the number
828 // of bytes written to the buffer
829 ret = sandbox_invoke(*mSandbox, ogg_sync_wrote, OggSyncState(aType),(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_wrote)>( "ogg_sync_wrote", reinterpret_cast<void
*>(&ogg_sync_wrote), OggSyncState(aType), bytesRead)
830 bytesRead)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_wrote)>( "ogg_sync_wrote", reinterpret_cast<void
*>(&ogg_sync_wrote), OggSyncState(aType), bytesRead)
831 .unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON);
832 NS_ENSURE_TRUE(ret == 0, false)do { if ((__builtin_expect(!!(!(ret == 0)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "ret == 0" ") failed", nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 832); return false; } } while (false)
;
833 }
834
835 return true;
836}
837
838nsresult OggDemuxer::DemuxOggPage(TrackInfo::TrackType aType,
839 tainted_opaque_ogg<ogg_page*> aPage) {
840 tainted_ogg<int> serial = sandbox_invoke(*mSandbox, ogg_page_serialno, aPage)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_page_serialno)>( "ogg_page_serialno", reinterpret_cast
<void*>(&ogg_page_serialno), aPage)
;
841 OggCodecState* codecState = mCodecStore.Get(static_cast<uint32_t>(
842 serial.unverified_safe_because(RLBOX_OGG_PAGE_SERIAL_REASON)));
843 if (codecState == nullptr) {
844 OGG_DEBUG("encountered packet for unrecognized codecState");
845 return NS_ERROR_FAILURE;
846 }
847 if (GetCodecStateType(codecState) != aType &&
848 codecState->GetType() != OggCodecState::TYPE_SKELETON) {
849 // Not a page we're interested in.
850 return NS_OK;
851 }
852 if (NS_FAILED(codecState->PageIn(aPage))((bool)(__builtin_expect(!!(NS_FAILED_impl(codecState->PageIn
(aPage))), 0)))
) {
853 OGG_DEBUG("codecState->PageIn failed");
854 return NS_ERROR_FAILURE;
855 }
856 return NS_OK;
857}
858
859bool OggDemuxer::IsSeekable() const { return !mIsChained; }
860
861UniquePtr<EncryptionInfo> OggDemuxer::GetCrypto() { return nullptr; }
862
863ogg_packet* OggDemuxer::GetNextPacket(TrackInfo::TrackType aType) {
864 OggCodecState* state = GetTrackCodecState(aType);
865 ogg_packet* packet = nullptr;
866 OggStateContext& context = OggState(aType);
867
868 while (true) {
869 if (packet) {
870 Unused << state->PacketOut();
871 }
872 DemuxUntilPacketAvailable(aType, state);
873
874 packet = state->PacketPeek();
875 if (!packet) {
876 break;
877 }
878 if (state->IsHeader(packet)) {
879 continue;
880 }
881 if (context.mNeedKeyframe && !state->IsKeyframe(packet)) {
882 continue;
883 }
884 context.mNeedKeyframe = false;
885 break;
886 }
887
888 return packet;
889}
890
891void OggDemuxer::DemuxUntilPacketAvailable(TrackInfo::TrackType aType,
892 OggCodecState* aState) {
893 while (!aState->IsPacketReady()) {
894 OGG_DEBUG("no packet yet, reading some more");
895 tainted_ogg<ogg_page*> page = mSandbox->malloc_in_sandbox<ogg_page>();
896 MOZ_RELEASE_ASSERT(page != nullptr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(page != nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(page != nullptr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("page != nullptr"
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 896); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "page != nullptr"
")"); do { *((volatile int*)__null) = 896; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
897 auto clean_page = MakeScopeExit([&] { mSandbox->free_in_sandbox(page); });
898 if (!ReadOggPage(aType, page.to_opaque())) {
899 OGG_DEBUG("no more pages to read in resource?");
900 return;
901 }
902 DemuxOggPage(aType, page.to_opaque());
903 }
904}
905
906TimeIntervals OggDemuxer::GetBuffered(TrackInfo::TrackType aType) {
907 if (!HaveStartTime(aType)) {
908 return TimeIntervals();
909 }
910 if (mIsChained) {
911 return TimeIntervals::Invalid();
912 }
913 TimeIntervals buffered;
914 // HasAudio and HasVideo are not used here as they take a lock and cause
915 // a deadlock. Accessing mInfo doesn't require a lock - it doesn't change
916 // after metadata is read.
917 if (!mInfo.HasValidMedia()) {
918 // No need to search through the file if there are no audio or video tracks
919 return buffered;
920 }
921
922 AutoPinned<MediaResource> resource(Resource(aType)->GetResource());
923 MediaByteRangeSet ranges;
924 nsresult res = resource->GetCachedRanges(ranges);
925 NS_ENSURE_SUCCESS(res, TimeIntervals::Invalid())do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "TimeIntervals::Invalid()"
, static_cast<uint32_t>(__rv), name ? " (" : "", name ?
name : "", name ? ")" : ""); NS_DebugBreak(NS_DEBUG_WARNING,
msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 925); return TimeIntervals::Invalid(); } } while (false)
;
926
927 const char time_interval_reason[] =
928 "Even if this computation is incorrect due to the reliance on tainted "
929 "values, only the search for the time interval or the time interval "
930 "returned will be affected. However this will not result in a memory "
931 "safety vulnerabilty in the Firefox renderer.";
932
933 // Traverse across the buffered byte ranges, determining the time ranges
934 // they contain. MediaResource::GetNextCachedData(offset) returns -1 when
935 // offset is after the end of the media resource, or there's no more cached
936 // data after the offset. This loop will run until we've checked every
937 // buffered range in the media, in increasing order of offset.
938 nsAutoOggSyncState sync(mSandbox.get());
939 for (uint32_t index = 0; index < ranges.Length(); index++) {
940 // Ensure the offsets are after the header pages.
941 int64_t startOffset = ranges[index].mStart;
942 int64_t endOffset = ranges[index].mEnd;
943
944 // Because the granulepos time is actually the end time of the page,
945 // we special-case (startOffset == 0) so that the first
946 // buffered range always appears to be buffered from the media start
947 // time, rather than from the end-time of the first page.
948 TimeUnit startTime = (startOffset == 0) ? StartTime() : TimeUnit::Invalid();
949
950 // Find the start time of the range. Read pages until we find one with a
951 // granulepos which we can convert into a timestamp to use as the time of
952 // the start of the buffered range.
953 sandbox_invoke(*mSandbox, ogg_sync_reset, sync.mState)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_reset)>( "ogg_sync_reset", reinterpret_cast<void
*>(&ogg_sync_reset), sync.mState)
;
954 tainted_ogg<ogg_page*> page = mSandbox->malloc_in_sandbox<ogg_page>();
955 if (!page) {
956 return TimeIntervals::Invalid();
957 }
958 auto clean_page = MakeScopeExit([&] { mSandbox->free_in_sandbox(page); });
959
960 while (!startTime.IsValid()) {
961 int32_t discard;
962 PageSyncResult pageSyncResult =
963 PageSync(mSandbox.get(), Resource(aType), sync.mState, true,
964 startOffset, endOffset, page, discard);
965 if (pageSyncResult == PAGE_SYNC_ERROR) {
966 return TimeIntervals::Invalid();
967 }
968 if (pageSyncResult == PAGE_SYNC_END_OF_RANGE) {
969 // Hit the end of range without reading a page, give up trying to
970 // find a start time for this buffered range, skip onto the next one.
971 break;
972 }
973
974 int64_t granulepos = sandbox_invoke(*mSandbox, ogg_page_granulepos, page)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_page_granulepos)>( "ogg_page_granulepos", reinterpret_cast
<void*>(&ogg_page_granulepos), page)
975 .unverified_safe_because(time_interval_reason);
976 if (granulepos == -1) {
977 // Page doesn't have an end time, advance to the next page
978 // until we find one.
979
980 bool failedPageLenVerify = false;
981 // Page length should be under 64Kb according to
982 // https://xiph.org/ogg/doc/libogg/ogg_page.html
983 long pageLength =
984 CopyAndVerifyOrFail(page->header_len + page->body_len,
985 val <= 64 * 1024, &failedPageLenVerify);
986 if (failedPageLenVerify) {
987 return TimeIntervals::Invalid();
988 }
989
990 startOffset += pageLength;
991 continue;
992 }
993
994 tainted_ogg<uint32_t> serial = rlbox::sandbox_static_cast<uint32_t>(
995 sandbox_invoke(*mSandbox, ogg_page_serialno, page)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_page_serialno)>( "ogg_page_serialno", reinterpret_cast
<void*>(&ogg_page_serialno), page)
);
996 if (aType == TrackInfo::kAudioTrack && mVorbisState &&
997 (serial == mVorbisState->mSerial)
998 .unverified_safe_because(time_interval_reason)) {
999 startTime = mVorbisState->Time(granulepos);
1000 MOZ_ASSERT(startTime.IsPositive(), "Must have positive start time")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(startTime.IsPositive())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(startTime.IsPositive()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("startTime.IsPositive()"
" (" "Must have positive start time" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1000); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startTime.IsPositive()"
") (" "Must have positive start time" ")"); do { *((volatile
int*)__null) = 1000; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
1001 } else if (aType == TrackInfo::kAudioTrack && mOpusState &&
1002 (serial == mOpusState->mSerial)
1003 .unverified_safe_because(time_interval_reason)) {
1004 startTime = mOpusState->Time(granulepos);
1005 MOZ_ASSERT(startTime.IsPositive(), "Must have positive start time")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(startTime.IsPositive())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(startTime.IsPositive()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("startTime.IsPositive()"
" (" "Must have positive start time" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1005); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startTime.IsPositive()"
") (" "Must have positive start time" ")"); do { *((volatile
int*)__null) = 1005; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
1006 } else if (aType == TrackInfo::kAudioTrack && mFlacState &&
1007 (serial == mFlacState->mSerial)
1008 .unverified_safe_because(time_interval_reason)) {
1009 startTime = mFlacState->Time(granulepos);
1010 MOZ_ASSERT(startTime.IsPositive(), "Must have positive start time")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(startTime.IsPositive())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(startTime.IsPositive()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("startTime.IsPositive()"
" (" "Must have positive start time" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1010); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startTime.IsPositive()"
") (" "Must have positive start time" ")"); do { *((volatile
int*)__null) = 1010; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
1011 } else if (aType == TrackInfo::kVideoTrack && mTheoraState &&
1012 (serial == mTheoraState->mSerial)
1013 .unverified_safe_because(time_interval_reason)) {
1014 startTime = mTheoraState->Time(granulepos);
1015 MOZ_ASSERT(startTime.IsPositive(), "Must have positive start time")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(startTime.IsPositive())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(startTime.IsPositive()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("startTime.IsPositive()"
" (" "Must have positive start time" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1015); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startTime.IsPositive()"
") (" "Must have positive start time" ")"); do { *((volatile
int*)__null) = 1015; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
1016 } else if (mCodecStore.Contains(
1017 serial.unverified_safe_because(time_interval_reason))) {
1018 // Stream is not the theora or vorbis stream we're playing,
1019 // but is one that we have header data for.
1020
1021 bool failedPageLenVerify = false;
1022 // Page length should be under 64Kb according to
1023 // https://xiph.org/ogg/doc/libogg/ogg_page.html
1024 long pageLength =
1025 CopyAndVerifyOrFail(page->header_len + page->body_len,
1026 val <= 64 * 1024, &failedPageLenVerify);
1027 if (failedPageLenVerify) {
1028 return TimeIntervals::Invalid();
1029 }
1030
1031 startOffset += pageLength;
1032 continue;
1033 } else {
1034 // Page is for a stream we don't know about (possibly a chained
1035 // ogg), return OK to abort the finding any further ranges. This
1036 // prevents us searching through the rest of the media when we
1037 // may not be able to extract timestamps from it.
1038 SetChained();
1039 return buffered;
1040 }
1041 }
1042
1043 if (startTime.IsValid()) {
1044 // We were able to find a start time for that range, see if we can
1045 // find an end time.
1046 TimeUnit endTime = RangeEndTime(aType, startOffset, endOffset, true);
1047 if (endTime.IsValid() && endTime > startTime) {
1048 buffered +=
1049 TimeInterval(startTime - StartTime(), endTime - StartTime());
1050 }
1051 }
1052 }
1053
1054 return buffered;
1055}
1056
1057void OggDemuxer::FindStartTime(TimeUnit& aOutStartTime) {
1058 // Extract the start times of the bitstreams in order to calculate
1059 // the duration.
1060 TimeUnit videoStartTime = TimeUnit::FromInfinity();
1061 TimeUnit audioStartTime = TimeUnit::FromInfinity();
1062
1063 if (HasVideo()) {
1064 FindStartTime(TrackInfo::kVideoTrack, videoStartTime);
1065 if (!videoStartTime.IsPosInf() && videoStartTime.IsValid()) {
1066 OGG_DEBUG("OggDemuxer::FindStartTime() video=%s",
1067 videoStartTime.ToString().get());
1068 mVideoOggState.mStartTime = Some(videoStartTime);
1069 }
1070 }
1071 if (HasAudio()) {
1072 FindStartTime(TrackInfo::kAudioTrack, audioStartTime);
1073 if (!audioStartTime.IsPosInf() && audioStartTime.IsValid()) {
1074 OGG_DEBUG("OggDemuxer::FindStartTime() audio=%s",
1075 audioStartTime.ToString().get());
1076 mAudioOggState.mStartTime = Some(audioStartTime);
1077 }
1078 }
1079
1080 TimeUnit minStartTime;
1081 if (videoStartTime.IsValid() && audioStartTime.IsValid()) {
1082 minStartTime = std::min(videoStartTime, audioStartTime);
1083 } else if (videoStartTime.IsValid()) {
1084 minStartTime = videoStartTime;
1085 } else if (audioStartTime.IsValid()) {
1086 minStartTime = audioStartTime;
1087 }
1088
1089 if (!minStartTime.IsPosInf()) {
1090 aOutStartTime = minStartTime;
1091 }
1092}
1093
1094void OggDemuxer::FindStartTime(TrackInfo::TrackType aType,
1095 TimeUnit& aOutStartTime) {
1096 TimeUnit startTime = TimeUnit::FromInfinity();
1097
1098 OggCodecState* state = GetTrackCodecState(aType);
1099 ogg_packet* pkt = GetNextPacket(aType);
1100 if (pkt) {
1101 startTime = state->PacketStartTime(pkt);
1102 }
1103
1104 if (!startTime.IsInfinite()) {
1105 aOutStartTime = startTime;
1106 }
1107}
1108
1109nsresult OggDemuxer::SeekInternal(TrackInfo::TrackType aType,
1110 const TimeUnit& aTarget) {
1111 OGG_DEBUG("About to seek to %s", aTarget.ToString().get());
1112 nsresult res;
1113 TimeUnit adjustedTarget = aTarget;
1114 TimeUnit startTime = StartTime(aType);
1115 TimeUnit endTime =
1116 mInfo.mMetadataDuration.valueOr(TimeUnit::Zero()) + startTime;
1117 if (aType == TrackInfo::kAudioTrack && mOpusState) {
1118 adjustedTarget = std::max(startTime, aTarget - OGG_SEEK_OPUS_PREROLL);
1119 }
1120
1121 if (!HaveStartTime(aType) || adjustedTarget == startTime) {
1122 // We've seeked to the media start or we can't seek.
1123 // Just seek to the offset of the first content page.
1124 res = Resource(aType)->Seek(nsISeekableStream::NS_SEEK_SET, 0);
1125 NS_ENSURE_SUCCESS(res, res)do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "res", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1125); return res; } } while (false)
;
1126
1127 res = Reset(aType);
1128 NS_ENSURE_SUCCESS(res, res)do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "res", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1128); return res; } } while (false)
;
1129 } else {
1130 // TODO: This may seek back unnecessarily far in the video, but we don't
1131 // have a way of asking Skeleton to seek to a different target for each
1132 // stream yet. Using adjustedTarget here is at least correct, if slow.
1133 IndexedSeekResult sres = SeekToKeyframeUsingIndex(aType, adjustedTarget);
1134 NS_ENSURE_TRUE(sres != SEEK_FATAL_ERROR, NS_ERROR_FAILURE)do { if ((__builtin_expect(!!(!(sres != SEEK_FATAL_ERROR)), 0
))) { NS_DebugBreak(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "sres != SEEK_FATAL_ERROR"
") failed", nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1134); return NS_ERROR_FAILURE; } } while (false)
;
1135 if (sres == SEEK_INDEX_FAIL) {
1136 // No index or other non-fatal index-related failure. Try to seek
1137 // using a bisection search. Determine the already downloaded data
1138 // in the media cache, so we can try to seek in the cached data first.
1139 AutoTArray<SeekRange, 16> ranges;
1140 res = GetSeekRanges(aType, ranges);
1141 NS_ENSURE_SUCCESS(res, res)do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "res", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1141); return res; } } while (false)
;
1142
1143 // Figure out if the seek target lies in a buffered range.
1144 SeekRange r =
1145 SelectSeekRange(aType, ranges, aTarget, startTime, endTime, true);
1146
1147 if (!r.IsNull()) {
1148 // We know the buffered range in which the seek target lies, do a
1149 // bisection search in that buffered range.
1150 res = SeekInBufferedRange(aType, aTarget, adjustedTarget, startTime,
1151 endTime, ranges, r);
1152 NS_ENSURE_SUCCESS(res, res)do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "res", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1152); return res; } } while (false)
;
1153 } else {
1154 // The target doesn't lie in a buffered range. Perform a bisection
1155 // search over the whole media, using the known buffered ranges to
1156 // reduce the search space.
1157 res = SeekInUnbuffered(aType, aTarget, startTime, endTime, ranges);
1158 NS_ENSURE_SUCCESS(res, res)do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "res", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1158); return res; } } while (false)
;
1159 }
1160 }
1161 }
1162
1163 // Demux forwards until we find the first keyframe prior the target.
1164 // there may be non-keyframes in the page before the keyframe.
1165 // Additionally, we may have seeked to the first page referenced by the
1166 // page index which may be quite far off the target.
1167 // When doing fastSeek we display the first frame after the seek, so
1168 // we need to advance the decode to the keyframe otherwise we'll get
1169 // visual artifacts in the first frame output after the seek.
1170 OggCodecState* state = GetTrackCodecState(aType);
1171 OggPacketQueue tempPackets;
1172 bool foundKeyframe = false;
1173 while (true) {
1174 DemuxUntilPacketAvailable(aType, state);
1175 ogg_packet* packet = state->PacketPeek();
1176 if (packet == nullptr) {
1177 OGG_DEBUG("End of stream reached before keyframe found in indexed seek");
1178 break;
1179 }
1180 // Skip any header packet, this can be the case when looping and not parsing
1181 // the headers again.
1182 if (state->IsHeader(packet)) {
1183 OggPacketPtr drop(state->PacketOut());
1184 continue;
1185 }
1186 TimeUnit startTstamp = state->PacketStartTime(packet);
1187 if (!startTstamp.IsValid()) {
1188 OGG_DEBUG("Invalid tstamp on packet %p (granulepos: %" PRId64"l" "d" ")", packet,
1189 packet->granulepos);
1190 }
1191 if (foundKeyframe && startTstamp.IsValid() &&
1192 startTstamp > adjustedTarget) {
1193 break;
1194 }
1195 if (state->IsKeyframe(packet)) {
1196 OGG_DEBUG("keyframe found after seeking at %s",
1197 startTstamp.ToString().get());
1198 tempPackets.Erase();
1199 foundKeyframe = true;
1200 }
1201 if (foundKeyframe && startTstamp.IsValid() &&
1202 startTstamp == adjustedTarget) {
1203 break;
1204 }
1205 if (foundKeyframe) {
1206 tempPackets.Append(state->PacketOut());
1207 } else {
1208 // Discard video packets before the first keyframe.
1209 Unused << state->PacketOut();
1210 }
1211 }
1212 // Re-add all packet into the codec state in order.
1213 state->PushFront(std::move(tempPackets));
1214
1215 return NS_OK;
1216}
1217
1218OggDemuxer::IndexedSeekResult OggDemuxer::RollbackIndexedSeek(
1219 TrackInfo::TrackType aType, int64_t aOffset) {
1220 if (mSkeletonState) {
1221 mSkeletonState->Deactivate();
1222 }
1223 nsresult res = Resource(aType)->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
1224 NS_ENSURE_SUCCESS(res, SEEK_FATAL_ERROR)do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "SEEK_FATAL_ERROR", static_cast
<uint32_t>(__rv), name ? " (" : "", name ? name : "", name
? ")" : ""); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1224); return SEEK_FATAL_ERROR; } } while (false)
;
1225 return SEEK_INDEX_FAIL;
1226}
1227
1228OggDemuxer::IndexedSeekResult OggDemuxer::SeekToKeyframeUsingIndex(
1229 TrackInfo::TrackType aType, const TimeUnit& aTarget) {
1230 if (!HasSkeleton() || !mSkeletonState->HasIndex()) {
1231 return SEEK_INDEX_FAIL;
1232 }
1233 // We have an index from the Skeleton track, try to use it to seek.
1234 AutoTArray<uint32_t, 2> tracks;
1235 BuildSerialList(tracks);
1236 SkeletonState::nsSeekTarget keyframe;
1237 if (NS_FAILED(mSkeletonState->IndexedSeekTarget(aTarget, tracks, keyframe))((bool)(__builtin_expect(!!(NS_FAILED_impl(mSkeletonState->
IndexedSeekTarget(aTarget, tracks, keyframe))), 0)))
) {
1238 // Could not locate a keypoint for the target in the index.
1239 return SEEK_INDEX_FAIL;
1240 }
1241
1242 // Remember original resource read cursor position so we can rollback on
1243 // failure.
1244 int64_t tell = Resource(aType)->Tell();
1245
1246 // Seek to the keypoint returned by the index.
1247 if (keyframe.mKeyPoint.mOffset > Resource(aType)->GetLength() ||
1248 keyframe.mKeyPoint.mOffset < 0) {
1249 // Index must be invalid.
1250 return RollbackIndexedSeek(aType, tell);
1251 }
1252 OGG_DEBUG("Seeking using index to keyframe at offset %" PRId64"l" "d" "\n",
1253 keyframe.mKeyPoint.mOffset);
1254 nsresult res = Resource(aType)->Seek(nsISeekableStream::NS_SEEK_SET,
1255 keyframe.mKeyPoint.mOffset);
1256 NS_ENSURE_SUCCESS(res, SEEK_FATAL_ERROR)do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "SEEK_FATAL_ERROR", static_cast
<uint32_t>(__rv), name ? " (" : "", name ? name : "", name
? ")" : ""); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1256); return SEEK_FATAL_ERROR; } } while (false)
;
1257
1258 // We've moved the read set, so reset decode.
1259 res = Reset(aType);
1260 NS_ENSURE_SUCCESS(res, SEEK_FATAL_ERROR)do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "SEEK_FATAL_ERROR", static_cast
<uint32_t>(__rv), name ? " (" : "", name ? name : "", name
? ")" : ""); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1260); return SEEK_FATAL_ERROR; } } while (false)
;
1261
1262 // Check that the page the index thinks is exactly here is actually exactly
1263 // here. If not, the index is invalid.
1264 tainted_ogg<ogg_page*> page = mSandbox->malloc_in_sandbox<ogg_page>();
1265 if (!page) {
1266 return SEEK_INDEX_FAIL;
1267 }
1268 auto clean_page = MakeScopeExit([&] { mSandbox->free_in_sandbox(page); });
1269 int skippedBytes = 0;
1270 PageSyncResult syncres =
1271 PageSync(mSandbox.get(), Resource(aType), OggSyncState(aType), false,
1272 keyframe.mKeyPoint.mOffset, Resource(aType)->GetLength(), page,
1273 skippedBytes);
1274 NS_ENSURE_TRUE(syncres != PAGE_SYNC_ERROR, SEEK_FATAL_ERROR)do { if ((__builtin_expect(!!(!(syncres != PAGE_SYNC_ERROR)),
0))) { NS_DebugBreak(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "syncres != PAGE_SYNC_ERROR"
") failed", nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1274); return SEEK_FATAL_ERROR; } } while (false)
;
1275 if (syncres != PAGE_SYNC_OK || skippedBytes != 0) {
1276 OGG_DEBUG(
1277 "Indexed-seek failure: Ogg Skeleton Index is invalid "
1278 "or sync error after seek");
1279 return RollbackIndexedSeek(aType, tell);
1280 }
1281 uint32_t serial = static_cast<uint32_t>(
1282 sandbox_invoke(*mSandbox, ogg_page_serialno, page)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_page_serialno)>( "ogg_page_serialno", reinterpret_cast
<void*>(&ogg_page_serialno), page)
1283 .unverified_safe_because(
1284 "Serial is only used to locate the correct page. If the serial "
1285 "is incorrect the the renderer would just fail to seek with an "
1286 "error code. This would not lead to any memory safety bugs."));
1287 if (serial != keyframe.mSerial) {
1288 // Serialno of page at offset isn't what the index told us to expect.
1289 // Assume the index is invalid.
1290 return RollbackIndexedSeek(aType, tell);
1291 }
1292 OggCodecState* codecState = mCodecStore.Get(serial);
1293 if (codecState && codecState->mActive &&
1294 sandbox_invoke(*mSandbox, ogg_stream_pagein, codecState->mState, page)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_stream_pagein)>( "ogg_stream_pagein", reinterpret_cast
<void*>(&ogg_stream_pagein), codecState->mState,
page)
1295 .unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON) != 0) {
1296 // Couldn't insert page into the ogg resource, or somehow the resource
1297 // is no longer active.
1298 return RollbackIndexedSeek(aType, tell);
1299 }
1300 return SEEK_OK;
1301}
1302
1303// Reads a page from the media resource.
1304OggDemuxer::PageSyncResult OggDemuxer::PageSync(
1305 rlbox_sandbox_ogg* aSandbox, MediaResourceIndex* aResource,
1306 tainted_opaque_ogg<ogg_sync_state*> aState, bool aCachedDataOnly,
1307 int64_t aOffset, int64_t aEndOffset, tainted_ogg<ogg_page*> aPage,
1308 int& aSkippedBytes) {
1309 aSkippedBytes = 0;
1310 // Sync to the next page.
1311 tainted_ogg<int> ret = 0;
1312 uint32_t bytesRead = 0;
1313 int64_t readHead = aOffset;
1314 while (ret.unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON) <= 0) {
1315 tainted_ogg<long> seek_ret =
1316 sandbox_invoke(*aSandbox, ogg_sync_pageseek, aState, aPage)(*aSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_pageseek)>( "ogg_sync_pageseek", reinterpret_cast
<void*>(&ogg_sync_pageseek), aState, aPage)
;
1317
1318 // We aren't really verifying the value of seek_ret below.
1319 // We are merely ensuring that it won't overflow an integer.
1320 // However we are assigning the value to ret which is marked tainted, so
1321 // this is fine.
1322 bool failedVerify = false;
1323 CheckedInt<int> checker;
1324 ret = CopyAndVerifyOrFail(
1325 seek_ret, (static_cast<void>(checker = val), checker.isValid()),
1326 &failedVerify);
1327 if (failedVerify) {
1328 return PAGE_SYNC_ERROR;
1329 }
1330
1331 if (ret.unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON) == 0) {
1332 const int page_step_val = PAGE_STEP;
1333 tainted_ogg<char*> buffer_tainted =
1334 sandbox_invoke(*aSandbox, ogg_sync_buffer, aState, page_step_val)(*aSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_buffer)>( "ogg_sync_buffer", reinterpret_cast<
void*>(&ogg_sync_buffer), aState, page_step_val)
;
1335 MOZ_ASSERT(buffer_tainted != nullptr, "Must have a buffer")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(buffer_tainted != nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(buffer_tainted != nullptr)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("buffer_tainted != nullptr"
" (" "Must have a buffer" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1335); AnnotateMozCrashReason("MOZ_ASSERT" "(" "buffer_tainted != nullptr"
") (" "Must have a buffer" ")"); do { *((volatile int*)__null
) = 1335; __attribute__((nomerge)) ::abort(); } while (false)
; } } while (false)
;
1336
1337 // Read from the file into the buffer
1338 int64_t bytesToRead =
1339 std::min(static_cast<int64_t>(PAGE_STEP), aEndOffset - readHead);
1340 MOZ_ASSERT(bytesToRead <= UINT32_MAX, "bytesToRead range check")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(bytesToRead <= (4294967295U))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(bytesToRead <= (4294967295U
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("bytesToRead <= (4294967295U)" " (" "bytesToRead range check"
")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1340); AnnotateMozCrashReason("MOZ_ASSERT" "(" "bytesToRead <= (4294967295U)"
") (" "bytesToRead range check" ")"); do { *((volatile int*)
__null) = 1340; __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1341 if (bytesToRead <= 0) {
1342 return PAGE_SYNC_END_OF_RANGE;
1343 }
1344 char* buffer = buffer_tainted.copy_and_verify_buffer_address(
1345 [](uintptr_t val) { return reinterpret_cast<char*>(val); },
1346 static_cast<size_t>(bytesToRead));
1347
1348 nsresult rv = NS_OK;
1349 if (aCachedDataOnly) {
1350 rv = aResource->GetResource()->ReadFromCache(
1351 buffer, readHead, static_cast<uint32_t>(bytesToRead));
1352 NS_ENSURE_SUCCESS(rv, PAGE_SYNC_ERROR)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "rv", "PAGE_SYNC_ERROR", static_cast
<uint32_t>(__rv), name ? " (" : "", name ? name : "", name
? ")" : ""); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1352); return PAGE_SYNC_ERROR; } } while (false)
;
1353 bytesRead = static_cast<uint32_t>(bytesToRead);
1354 } else {
1355 rv = aResource->Seek(nsISeekableStream::NS_SEEK_SET, readHead);
1356 NS_ENSURE_SUCCESS(rv, PAGE_SYNC_ERROR)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "rv", "PAGE_SYNC_ERROR", static_cast
<uint32_t>(__rv), name ? " (" : "", name ? name : "", name
? ")" : ""); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1356); return PAGE_SYNC_ERROR; } } while (false)
;
1357 rv = aResource->Read(buffer, static_cast<uint32_t>(bytesToRead),
1358 &bytesRead);
1359 NS_ENSURE_SUCCESS(rv, PAGE_SYNC_ERROR)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "rv", "PAGE_SYNC_ERROR", static_cast
<uint32_t>(__rv), name ? " (" : "", name ? name : "", name
? ")" : ""); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1359); return PAGE_SYNC_ERROR; } } while (false)
;
1360 }
1361 if (bytesRead == 0 && NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) {
1362 // End of file.
1363 return PAGE_SYNC_END_OF_RANGE;
1364 }
1365 readHead += bytesRead;
1366
1367 // Update the synchronisation layer with the number
1368 // of bytes written to the buffer
1369 ret = sandbox_invoke(*aSandbox, ogg_sync_wrote, aState, bytesRead)(*aSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_wrote)>( "ogg_sync_wrote", reinterpret_cast<void
*>(&ogg_sync_wrote), aState, bytesRead)
;
1370 NS_ENSURE_TRUE(do { if ((__builtin_expect(!!(!(ret.unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON
) == 0)), 0))) { NS_DebugBreak(NS_DEBUG_WARNING, "NS_ENSURE_TRUE("
"ret.unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON) == 0"
") failed", nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1372); return PAGE_SYNC_ERROR; } } while (false)
1371 ret.unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON) == 0,do { if ((__builtin_expect(!!(!(ret.unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON
) == 0)), 0))) { NS_DebugBreak(NS_DEBUG_WARNING, "NS_ENSURE_TRUE("
"ret.unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON) == 0"
") failed", nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1372); return PAGE_SYNC_ERROR; } } while (false)
1372 PAGE_SYNC_ERROR)do { if ((__builtin_expect(!!(!(ret.unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON
) == 0)), 0))) { NS_DebugBreak(NS_DEBUG_WARNING, "NS_ENSURE_TRUE("
"ret.unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON) == 0"
") failed", nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1372); return PAGE_SYNC_ERROR; } } while (false)
;
1373 continue;
1374 }
1375
1376 if (ret.unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON) < 0) {
1377 MOZ_ASSERT(aSkippedBytes >= 0, "Offset >= 0")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSkippedBytes >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aSkippedBytes >= 0))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("aSkippedBytes >= 0"
" (" "Offset >= 0" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1377); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aSkippedBytes >= 0"
") (" "Offset >= 0" ")"); do { *((volatile int*)__null) =
1377; __attribute__((nomerge)) ::abort(); } while (false); }
} while (false)
;
1378 bool failedSkippedBytesVerify = false;
1379 ret.copy_and_verify([&](int val) {
1380 int64_t result = static_cast<int64_t>(aSkippedBytes) - val;
1381 if (result > std::numeric_limits<int>::max() ||
1382 result > (aEndOffset - aOffset) || result < 0) {
1383 failedSkippedBytesVerify = true;
1384 } else {
1385 aSkippedBytes = AssertedCast<int>(result);
1386 }
1387 });
1388 if (failedSkippedBytesVerify) {
1389 return PAGE_SYNC_ERROR;
1390 }
1391 continue;
1392 }
1393 }
1394
1395 return PAGE_SYNC_OK;
1396}
1397
1398// OggTrackDemuxer
1399OggTrackDemuxer::OggTrackDemuxer(OggDemuxer* aParent,
1400 TrackInfo::TrackType aType,
1401 uint32_t aTrackNumber)
1402 : mParent(aParent), mType(aType) {
1403 mInfo = mParent->GetTrackInfo(aType, aTrackNumber);
1404 MOZ_ASSERT(mInfo)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mInfo)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(mInfo))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("mInfo", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1404); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mInfo" ")")
; do { *((volatile int*)__null) = 1404; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1405}
1406
1407OggTrackDemuxer::~OggTrackDemuxer() = default;
1408
1409UniquePtr<TrackInfo> OggTrackDemuxer::GetInfo() const { return mInfo->Clone(); }
1410
1411RefPtr<OggTrackDemuxer::SeekPromise> OggTrackDemuxer::Seek(
1412 const TimeUnit& aTime) {
1413 // Seeks to aTime. Upon success, SeekPromise will be resolved with the
1414 // actual time seeked to. Typically the random access point time
1415 mQueuedSample = nullptr;
1416 TimeUnit seekTime = aTime;
1417 if (mParent->SeekInternal(mType, aTime) == NS_OK) {
1418 RefPtr<MediaRawData> sample(NextSample());
1419
1420 // Check what time we actually seeked to.
1421 if (sample != nullptr) {
1422 seekTime = sample->mTime;
1423 OGG_DEBUG("%p seeked to time %" PRId64"l" "d", this, seekTime.ToMicroseconds());
1424 }
1425 mQueuedSample = sample;
1426
1427 return SeekPromise::CreateAndResolve(seekTime, __func__);
1428 }
1429 return SeekPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, __func__);
1430}
1431
1432RefPtr<MediaRawData> OggTrackDemuxer::NextSample() {
1433 OGG_DEBUG("OggTrackDemuxer::NextSample");
1434 if (mQueuedSample) {
1435 RefPtr<MediaRawData> nextSample = mQueuedSample;
1436 mQueuedSample = nullptr;
1437 if (mType == TrackInfo::kAudioTrack) {
1438 nextSample->mTrackInfo = mParent->mSharedAudioTrackInfo;
1439 }
1440 OGG_DEBUG("OggTrackDemuxer::NextSample (queued)");
1441 return nextSample;
1442 }
1443 ogg_packet* packet = mParent->GetNextPacket(mType);
1444 if (!packet) {
1445 return nullptr;
1446 }
1447 // Check the eos state in case we need to look for chained streams.
1448 bool eos = packet->e_o_s;
1449 OggCodecState* state = mParent->GetTrackCodecState(mType);
1450 RefPtr<MediaRawData> data = state->PacketOutAsMediaRawData();
1451 // ogg allows 'nil' packets, that are EOS and of size 0.
1452 if (!data || (data->mEOS && data->Size() == 0)) {
1453 return nullptr;
1454 }
1455 if (mType == TrackInfo::kAudioTrack) {
1456 data->mTrackInfo = mParent->mSharedAudioTrackInfo;
1457 }
1458 // mDecodedAudioDuration gets adjusted during ReadOggChain().
1459 TimeUnit totalDuration = mParent->mDecodedAudioDuration;
1460 if (eos) {
1461 // We've encountered an end of bitstream packet; check for a chained
1462 // bitstream following this one.
1463 // This will also update mSharedAudioTrackInfo.
1464 mParent->ReadOggChain(data->GetEndTime());
1465 }
1466 data->mOffset = mParent->Resource(mType)->Tell();
1467 // We adjust the start time of the sample to account for the potential ogg
1468 // chaining.
1469 data->mTime += totalDuration;
1470 if (!data->mTime.IsValid()) {
1471 return nullptr;
1472 }
1473 TimeUnit mediaStartTime = mParent->mStartTime.valueOr(TimeUnit::Zero());
1474 TimeUnit mediaEndTime =
1475 mediaStartTime +
1476 mParent->mInfo.mMetadataDuration.valueOr(TimeUnit::FromInfinity());
1477 // Trim packets that end after the media duration.
1478 if (mType == TrackInfo::kAudioTrack) {
1479 OGG_DEBUG("Check trimming %s > %s", data->GetEndTime().ToString().get(),
1480 mediaEndTime.ToString().get());
1481 // Because of a quirk of this demuxer, this needs to be >=. It looks
1482 // useless, because `toTrim` is going to be 0, but it allows setting
1483 // `mOriginalPresentationWindow`, so that the trimming logic will later
1484 // remove extraneous frames.
1485 // This demuxer sets the end time of a packet to be the end time that
1486 // should be played, not the end time that corresponds to the number of
1487 // decoded frames, that we can only have after decoding.
1488 // >= allows detecting the last packet, and trimming it appropriately,
1489 // after decoding has happened, with the AudioTrimmer.
1490 if (data->GetEndTime() >= mediaEndTime) {
1491 TimeUnit toTrim = data->GetEndTime() - mediaEndTime;
1492 TimeUnit originalDuration = data->mDuration;
1493 OGG_DEBUG(
1494 "Demuxed past media end time, trimming: packet [%s,%s] to [%s,%s]",
1495 data->mTime.ToString().get(), data->GetEndTime().ToString().get(),
1496 data->mTime.ToString().get(),
1497 (data->mTime + originalDuration).ToString().get());
1498 data->mOriginalPresentationWindow =
1499 Some(TimeInterval{data->mTime, data->GetEndTime()});
1500 data->mDuration -= toTrim;
1501 if (data->mDuration.IsNegative()) {
1502 data->mDuration = TimeUnit::Zero(data->mTime);
1503 }
1504 }
1505 }
1506
1507 OGG_DEBUG("OGG packet demuxed: [%s,%s] (duration: %s, type: %s)",
1508 data->mTime.ToString().get(), data->GetEndTime().ToString().get(),
1509 data->mDuration.ToString().get(),
1510 mType == TrackInfo::kAudioTrack ? "audio" : "video");
1511
1512 return data;
1513}
1514
1515RefPtr<OggTrackDemuxer::SamplesPromise> OggTrackDemuxer::GetSamples(
1516 int32_t aNumSamples) {
1517 RefPtr<SamplesHolder> samples = new SamplesHolder;
1518 if (!aNumSamples) {
1519 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
1520 __func__);
1521 }
1522
1523 while (aNumSamples) {
1524 RefPtr<MediaRawData> sample(NextSample());
1525 if (!sample) {
1526 break;
1527 }
1528 if (!sample->HasValidTime()) {
1529 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
1530 __func__);
1531 }
1532 samples->AppendSample(sample);
1533 aNumSamples--;
1534 }
1535
1536 if (samples->GetSamples().IsEmpty()) {
1537 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM,
1538 __func__);
1539 }
1540 return SamplesPromise::CreateAndResolve(samples, __func__);
1541}
1542
1543void OggTrackDemuxer::Reset() {
1544 mParent->Reset(mType);
1545 mQueuedSample = nullptr;
1546}
1547
1548RefPtr<OggTrackDemuxer::SkipAccessPointPromise>
1549OggTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) {
1550 uint32_t parsed = 0;
1551 bool found = false;
1552 RefPtr<MediaRawData> sample;
1553
1554 OGG_DEBUG("TimeThreshold: %f", aTimeThreshold.ToSeconds());
1555 while (!found && (sample = NextSample())) {
1556 parsed++;
1557 if (sample->mKeyframe && sample->mTime >= aTimeThreshold) {
1558 found = true;
1559 mQueuedSample = sample;
1560 }
1561 }
1562 if (found) {
1563 OGG_DEBUG("next sample: %f (parsed: %d)", sample->mTime.ToSeconds(),
1564 parsed);
1565 return SkipAccessPointPromise::CreateAndResolve(parsed, __func__);
1566 }
1567 SkipFailureHolder failure(NS_ERROR_DOM_MEDIA_END_OF_STREAM, parsed);
1568 return SkipAccessPointPromise::CreateAndReject(std::move(failure), __func__);
1569}
1570
1571TimeIntervals OggTrackDemuxer::GetBuffered() {
1572 return mParent->GetBuffered(mType);
1573}
1574
1575void OggTrackDemuxer::BreakCycles() { mParent = nullptr; }
1576
1577// Returns an ogg page's checksum.
1578tainted_opaque_ogg<ogg_uint32_t> OggDemuxer::GetPageChecksum(
1579 tainted_opaque_ogg<ogg_page*> aPage) {
1580 tainted_ogg<ogg_page*> page = rlbox::from_opaque(aPage);
1581
1582 const char hint_reason[] =
1583 "Early bail out of checksum. Even if this is wrong, the renderer's "
1584 "security is not compromised.";
1585 if (page == nullptr ||
1586 (page->header == nullptr).unverified_safe_because(hint_reason) ||
1587 (page->header_len < 25).unverified_safe_because(hint_reason)) {
1588 tainted_ogg<ogg_uint32_t> ret = 0;
1589 return ret.to_opaque();
1590 }
1591
1592 const int CHECKSUM_BYTES_LENGTH = 4;
1593 const unsigned char* p =
1594 (page->header + 22u)
1595 .copy_and_verify_buffer_address(
1596 [](uintptr_t val) {
1597 return reinterpret_cast<const unsigned char*>(val);
1598 },
1599 CHECKSUM_BYTES_LENGTH);
1600 uint32_t c =
1601 static_cast<uint32_t>(p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24));
1602 tainted_ogg<uint32_t> ret = c;
1603 return ret.to_opaque();
1604}
1605
1606TimeUnit OggDemuxer::RangeStartTime(TrackInfo::TrackType aType,
1607 int64_t aOffset) {
1608 int64_t position = Resource(aType)->Tell();
1609 nsresult res = Resource(aType)->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
1610 NS_ENSURE_SUCCESS(res, TimeUnit::Zero())do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "TimeUnit::Zero()", static_cast
<uint32_t>(__rv), name ? " (" : "", name ? name : "", name
? ")" : ""); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1610); return TimeUnit::Zero(); } } while (false)
;
1611 TimeUnit startTime = TimeUnit::Zero();
1612 FindStartTime(aType, startTime);
1613 res = Resource(aType)->Seek(nsISeekableStream::NS_SEEK_SET, position);
1614 NS_ENSURE_SUCCESS(res, TimeUnit::Invalid())do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "TimeUnit::Invalid()", static_cast
<uint32_t>(__rv), name ? " (" : "", name ? name : "", name
? ")" : ""); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1614); return TimeUnit::Invalid(); } } while (false)
;
1615 return startTime;
1616}
1617
1618struct nsDemuxerAutoOggSyncState {
1619 explicit nsDemuxerAutoOggSyncState(rlbox_sandbox_ogg& aSandbox)
1620 : mSandbox(aSandbox) {
1621 mState = mSandbox.malloc_in_sandbox<ogg_sync_state>();
1622 MOZ_RELEASE_ASSERT(mState != nullptr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mState != nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mState != nullptr))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("mState != nullptr"
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1622); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "mState != nullptr"
")"); do { *((volatile int*)__null) = 1622; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1623 sandbox_invoke(mSandbox, ogg_sync_init, mState)(mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_init)>( "ogg_sync_init", reinterpret_cast<void
*>(&ogg_sync_init), mState)
;
1624 }
1625 ~nsDemuxerAutoOggSyncState() {
1626 sandbox_invoke(mSandbox, ogg_sync_clear, mState)(mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_clear)>( "ogg_sync_clear", reinterpret_cast<void
*>(&ogg_sync_clear), mState)
;
1627 mSandbox.free_in_sandbox(mState);
1628 }
1629 rlbox_sandbox_ogg& mSandbox;
1630 tainted_ogg<ogg_sync_state*> mState{};
1631};
1632
1633TimeUnit OggDemuxer::RangeEndTime(TrackInfo::TrackType aType,
1634 int64_t aEndOffset) {
1635 int64_t position = Resource(aType)->Tell();
1636 TimeUnit endTime = RangeEndTime(aType, 0, aEndOffset, false);
1637 nsresult res =
1638 Resource(aType)->Seek(nsISeekableStream::NS_SEEK_SET, position);
1639 NS_ENSURE_SUCCESS(res, TimeUnit::Invalid())do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "TimeUnit::Invalid()", static_cast
<uint32_t>(__rv), name ? " (" : "", name ? name : "", name
? ")" : ""); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1639); return TimeUnit::Invalid(); } } while (false)
;
1640 return endTime;
1641}
1642
1643TimeUnit OggDemuxer::RangeEndTime(TrackInfo::TrackType aType,
1644 int64_t aStartOffset, int64_t aEndOffset,
1645 bool aCachedDataOnly) {
1646 nsDemuxerAutoOggSyncState sync(*mSandbox);
1647
1648 // We need to find the last page which ends before aEndOffset that
1649 // has a granulepos that we can convert to a timestamp. We do this by
1650 // backing off from aEndOffset until we encounter a page on which we can
1651 // interpret the granulepos. If while backing off we encounter a page which
1652 // we've previously encountered before, we'll either backoff again if we
1653 // haven't found an end time yet, or return the last end time found.
1654 const int step = 5000;
1655 const int maxOggPageSize = 65306;
1656 int64_t readStartOffset = aEndOffset;
1657 int64_t readLimitOffset = aEndOffset;
1658 int64_t readHead = aEndOffset;
1659 TimeUnit endTime = TimeUnit::Invalid();
1660 uint32_t checksumAfterSeek = 0;
1661 uint32_t prevChecksumAfterSeek = 0;
1662 bool mustBackOff = false;
1663 tainted_ogg<ogg_page*> page = mSandbox->malloc_in_sandbox<ogg_page>();
1664 if (!page) {
1665 return TimeUnit::Invalid();
1666 }
1667 auto clean_page = MakeScopeExit([&] { mSandbox->free_in_sandbox(page); });
1668 while (true) {
1669 tainted_ogg<long> seek_ret =
1670 sandbox_invoke(*mSandbox, ogg_sync_pageseek, sync.mState, page)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_pageseek)>( "ogg_sync_pageseek", reinterpret_cast
<void*>(&ogg_sync_pageseek), sync.mState, page)
;
1671
1672 // We aren't really verifying the value of seek_ret below.
1673 // We are merely ensuring that it won't overflow an integer.
1674 // However we are assigning the value to ret which is marked tainted, so
1675 // this is fine.
1676 bool failedVerify = false;
1677 CheckedInt<int> checker;
1678 tainted_ogg<int> ret = CopyAndVerifyOrFail(
1679 seek_ret, (static_cast<void>(checker = val), checker.isValid()),
1680 &failedVerify);
1681 if (failedVerify) {
1682 return TimeUnit::Invalid();
1683 }
1684
1685 if (ret.unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON) == 0) {
1686 // We need more data if we've not encountered a page we've seen before,
1687 // or we've read to the end of file.
1688 if (mustBackOff || readHead == aEndOffset || readHead == aStartOffset) {
1689 if (endTime.IsValid() || readStartOffset == 0) {
1690 // We have encountered a page before, or we're at the end of file.
1691 break;
1692 }
1693 mustBackOff = false;
1694 prevChecksumAfterSeek = checksumAfterSeek;
1695 checksumAfterSeek = 0;
1696 sandbox_invoke(*mSandbox, ogg_sync_reset, sync.mState)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_reset)>( "ogg_sync_reset", reinterpret_cast<void
*>(&ogg_sync_reset), sync.mState)
;
1697 readStartOffset =
1698 std::max(static_cast<int64_t>(0), readStartOffset - step);
1699 // There's no point reading more than the maximum size of
1700 // an Ogg page into data we've previously scanned. Any data
1701 // between readLimitOffset and aEndOffset must be garbage
1702 // and we can ignore it thereafter.
1703 readLimitOffset =
1704 std::min(readLimitOffset, readStartOffset + maxOggPageSize);
1705 readHead = std::max(aStartOffset, readStartOffset);
1706 }
1707
1708 int64_t limit =
1709 std::min(static_cast<int64_t>(UINT32_MAX(4294967295U)), aEndOffset - readHead);
1710 limit = std::max(static_cast<int64_t>(0), limit);
1711 limit = std::min(limit, static_cast<int64_t>(step));
1712 uint32_t bytesToRead = static_cast<uint32_t>(limit);
1713 uint32_t bytesRead = 0;
1714 tainted_ogg<char*> buffer_tainted =
1715 sandbox_invoke(*mSandbox, ogg_sync_buffer, sync.mState, bytesToRead)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_buffer)>( "ogg_sync_buffer", reinterpret_cast<
void*>(&ogg_sync_buffer), sync.mState, bytesToRead)
;
1716 char* buffer = buffer_tainted.copy_and_verify_buffer_address(
1717 [](uintptr_t val) { return reinterpret_cast<char*>(val); },
1718 bytesToRead);
1719 MOZ_ASSERT(buffer, "Must have buffer")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(buffer)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(buffer))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("buffer" " (" "Must have buffer"
")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1719); AnnotateMozCrashReason("MOZ_ASSERT" "(" "buffer" ") ("
"Must have buffer" ")"); do { *((volatile int*)__null) = 1719
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
1720 nsresult res;
1721 if (aCachedDataOnly) {
1722 res = Resource(aType)->GetResource()->ReadFromCache(buffer, readHead,
1723 bytesToRead);
1724 NS_ENSURE_SUCCESS(res, TimeUnit::Invalid())do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "TimeUnit::Invalid()", static_cast
<uint32_t>(__rv), name ? " (" : "", name ? name : "", name
? ")" : ""); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1724); return TimeUnit::Invalid(); } } while (false)
;
1725 bytesRead = bytesToRead;
1726 } else {
1727 MOZ_ASSERT(readHead < aEndOffset,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(readHead < aEndOffset)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(readHead < aEndOffset))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("readHead < aEndOffset"
" (" "resource pos must be before range end" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1728); AnnotateMozCrashReason("MOZ_ASSERT" "(" "readHead < aEndOffset"
") (" "resource pos must be before range end" ")"); do { *((
volatile int*)__null) = 1728; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
1728 "resource pos must be before range end")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(readHead < aEndOffset)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(readHead < aEndOffset))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("readHead < aEndOffset"
" (" "resource pos must be before range end" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1728); AnnotateMozCrashReason("MOZ_ASSERT" "(" "readHead < aEndOffset"
") (" "resource pos must be before range end" ")"); do { *((
volatile int*)__null) = 1728; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
1729 res = Resource(aType)->Seek(nsISeekableStream::NS_SEEK_SET, readHead);
1730 NS_ENSURE_SUCCESS(res, TimeUnit::Invalid())do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "TimeUnit::Invalid()", static_cast
<uint32_t>(__rv), name ? " (" : "", name ? name : "", name
? ")" : ""); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1730); return TimeUnit::Invalid(); } } while (false)
;
1731 res = Resource(aType)->Read(buffer, bytesToRead, &bytesRead);
1732 NS_ENSURE_SUCCESS(res, TimeUnit::Invalid())do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "TimeUnit::Invalid()", static_cast
<uint32_t>(__rv), name ? " (" : "", name ? name : "", name
? ")" : ""); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1732); return TimeUnit::Invalid(); } } while (false)
;
1733 }
1734 readHead += bytesRead;
1735 if (readHead > readLimitOffset) {
1736 mustBackOff = true;
1737 }
1738
1739 // Update the synchronisation layer with the number
1740 // of bytes written to the buffer
1741 ret = sandbox_invoke(*mSandbox, ogg_sync_wrote, sync.mState, bytesRead)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_sync_wrote)>( "ogg_sync_wrote", reinterpret_cast<void
*>(&ogg_sync_wrote), sync.mState, bytesRead)
;
1742 bool failedWroteVerify = false;
1743 int wrote_success =
1744 CopyAndVerifyOrFail(ret, val == 0 || val == -1, &failedWroteVerify);
1745 if (failedWroteVerify) {
1746 return TimeUnit::Invalid();
1747 }
1748
1749 if (wrote_success != 0) {
1750 endTime = TimeUnit::Invalid();
1751 break;
1752 }
1753 continue;
1754 }
1755
1756 if (ret.unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON) < 0 ||
1757 sandbox_invoke(*mSandbox, ogg_page_granulepos, page)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_page_granulepos)>( "ogg_page_granulepos", reinterpret_cast
<void*>(&ogg_page_granulepos), page)
1758 .unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON) < 0) {
1759 continue;
1760 }
1761
1762 tainted_ogg<uint32_t> checksum_tainted =
1763 rlbox::from_opaque(GetPageChecksum(page.to_opaque()));
1764 uint32_t checksum = checksum_tainted.unverified_safe_because(
1765 "checksum is only being used as a hint as part of search for end time. "
1766 "Incorrect values will not affect the memory safety of the renderer.");
1767 if (checksumAfterSeek == 0) {
1768 // This is the first page we've decoded after a backoff/seek. Remember
1769 // the page checksum. If we backoff further and encounter this page
1770 // again, we'll know that we won't find a page with an end time after
1771 // this one, so we'll know to back off again.
1772 checksumAfterSeek = checksum;
1773 }
1774 if (checksum == prevChecksumAfterSeek) {
1775 // This page has the same checksum as the first page we encountered
1776 // after the last backoff/seek. Since we've already scanned after this
1777 // page and failed to find an end time, we may as well backoff again and
1778 // try to find an end time from an earlier page.
1779 mustBackOff = true;
1780 continue;
1781 }
1782
1783 int64_t granulepos =
1784 sandbox_invoke(*mSandbox, ogg_page_granulepos, page)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_page_granulepos)>( "ogg_page_granulepos", reinterpret_cast
<void*>(&ogg_page_granulepos), page)
1785 .unverified_safe_because(
1786 "If this is incorrect it may lead to incorrect seeking "
1787 "behavior in the stream, however will not affect the memory "
1788 "safety of the Firefox renderer.");
1789 uint32_t serial = static_cast<uint32_t>(
1790 sandbox_invoke(*mSandbox, ogg_page_serialno, page)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_page_serialno)>( "ogg_page_serialno", reinterpret_cast
<void*>(&ogg_page_serialno), page)
1791 .unverified_safe_because(RLBOX_OGG_PAGE_SERIAL_REASON));
1792
1793 OggCodecState* codecState = nullptr;
1794 codecState = mCodecStore.Get(serial);
1795 if (!codecState) {
1796 // This page is from a bitstream which we haven't encountered yet.
1797 // It's probably from a new "link" in a "chained" ogg. Don't
1798 // bother even trying to find a duration...
1799 SetChained();
1800 endTime = TimeUnit::Invalid();
1801 break;
1802 }
1803
1804 TimeUnit t = codecState->Time(granulepos);
1805 if (t.IsValid()) {
1806 endTime = t;
1807 }
1808 }
1809
1810 return endTime;
1811}
1812
1813nsresult OggDemuxer::GetSeekRanges(TrackInfo::TrackType aType,
1814 nsTArray<SeekRange>& aRanges) {
1815 AutoPinned<MediaResource> resource(Resource(aType)->GetResource());
1816 MediaByteRangeSet cached;
1817 nsresult res = resource->GetCachedRanges(cached);
1818 NS_ENSURE_SUCCESS(res, res)do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "res", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1818); return res; } } while (false)
;
1819
1820 for (uint32_t index = 0; index < cached.Length(); index++) {
1821 const auto& range = cached[index];
1822 TimeUnit startTime = TimeUnit::Invalid();
1823 TimeUnit endTime = TimeUnit::Invalid();
1824 if (NS_FAILED(Reset(aType))((bool)(__builtin_expect(!!(NS_FAILED_impl(Reset(aType))), 0)
))
) {
1825 return NS_ERROR_FAILURE;
1826 }
1827 int64_t startOffset = range.mStart;
1828 int64_t endOffset = range.mEnd;
1829 startTime = RangeStartTime(aType, startOffset);
1830 if (startTime.IsValid() &&
1831 ((endTime = RangeEndTime(aType, endOffset)).IsValid())) {
1832 NS_WARNING_ASSERTION(startTime < endTime,do { if (!(startTime < endTime)) { NS_DebugBreak(NS_DEBUG_WARNING
, "Start time must be before end time", "startTime < endTime"
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1833); } } while (false)
1833 "Start time must be before end time")do { if (!(startTime < endTime)) { NS_DebugBreak(NS_DEBUG_WARNING
, "Start time must be before end time", "startTime < endTime"
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1833); } } while (false)
;
1834 aRanges.AppendElement(
1835 SeekRange(startOffset, endOffset, startTime, endTime));
1836 }
1837 }
1838 if (NS_FAILED(Reset(aType))((bool)(__builtin_expect(!!(NS_FAILED_impl(Reset(aType))), 0)
))
) {
1839 return NS_ERROR_FAILURE;
1840 }
1841 return NS_OK;
1842}
1843
1844OggDemuxer::SeekRange OggDemuxer::SelectSeekRange(
1845 TrackInfo::TrackType aType, const nsTArray<SeekRange>& ranges,
1846 const TimeUnit& aTarget, const TimeUnit& aStartTime,
1847 const TimeUnit& aEndTime, bool aExact) {
1848 int64_t so = 0;
1849 int64_t eo = Resource(aType)->GetLength();
1850 TimeUnit st = aStartTime;
1851 TimeUnit et = aEndTime;
1852 for (uint32_t i = 0; i < ranges.Length(); i++) {
1853 const SeekRange& r = ranges[i];
1854 if (r.mTimeStart < aTarget) {
1855 so = r.mOffsetStart;
1856 st = r.mTimeStart;
1857 }
1858 if (r.mTimeEnd >= aTarget && r.mTimeEnd < et) {
1859 eo = r.mOffsetEnd;
1860 et = r.mTimeEnd;
1861 }
1862
1863 if (r.mTimeStart < aTarget && aTarget <= r.mTimeEnd) {
1864 // Target lies exactly in this range.
1865 return ranges[i];
1866 }
1867 }
1868 if (aExact || eo == -1) {
1869 return SeekRange();
1870 }
1871 return SeekRange(so, eo, st, et);
1872}
1873
1874nsresult OggDemuxer::SeekInBufferedRange(TrackInfo::TrackType aType,
1875 const TimeUnit& aTarget,
1876 TimeUnit& aAdjustedTarget,
1877 const TimeUnit& aStartTime,
1878 const TimeUnit& aEndTime,
1879 const nsTArray<SeekRange>& aRanges,
1880 const SeekRange& aRange) {
1881 OGG_DEBUG("Seeking in buffered data to %s using bisection search",
1882 aTarget.ToString().get());
1883 if (aType == TrackInfo::kVideoTrack || aAdjustedTarget >= aTarget) {
1884 // We know the exact byte range in which the target must lie. It must
1885 // be buffered in the media cache. Seek there.
1886 nsresult res = SeekBisection(aType, aTarget, aRange, TimeUnit::Zero());
1887 if (NS_FAILED(res)((bool)(__builtin_expect(!!(NS_FAILED_impl(res)), 0))) || aType != TrackInfo::kVideoTrack) {
1888 return res;
1889 }
1890
1891 // We have an active Theora bitstream. Peek the next Theora frame, and
1892 // extract its keyframe's time.
1893 DemuxUntilPacketAvailable(aType, mTheoraState);
1894 ogg_packet* packet = mTheoraState->PacketPeek();
1895 if (packet && !mTheoraState->IsKeyframe(packet)) {
1896 // First post-seek frame isn't a keyframe, seek back to previous keyframe,
1897 // otherwise we'll get visual artifacts.
1898 MOZ_ASSERT(packet->granulepos != -1, "Must have a granulepos")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(packet->granulepos != -1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(packet->granulepos != -1)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("packet->granulepos != -1"
" (" "Must have a granulepos" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1898); AnnotateMozCrashReason("MOZ_ASSERT" "(" "packet->granulepos != -1"
") (" "Must have a granulepos" ")"); do { *((volatile int*)__null
) = 1898; __attribute__((nomerge)) ::abort(); } while (false)
; } } while (false)
;
1899 int shift = mTheoraState->KeyFrameGranuleJobs();
1900 int64_t keyframeGranulepos = (packet->granulepos >> shift) << shift;
1901 TimeUnit keyframeTime = mTheoraState->StartTime(keyframeGranulepos);
1902 SEEK_LOG(LogLevel::Debug,
1903 ("Keyframe for %lld is at %lld, seeking back to it", frameTime,
1904 keyframeTime));
1905 aAdjustedTarget = std::min(aAdjustedTarget, keyframeTime);
1906 }
1907 }
1908
1909 nsresult res = NS_OK;
1910 if (aAdjustedTarget < aTarget) {
1911 SeekRange k = SelectSeekRange(aType, aRanges, aAdjustedTarget, aStartTime,
1912 aEndTime, false);
1913 res = SeekBisection(aType, aAdjustedTarget, k, OGG_SEEK_FUZZ_USECS);
1914 }
1915 return res;
1916}
1917
1918nsresult OggDemuxer::SeekInUnbuffered(TrackInfo::TrackType aType,
1919 const TimeUnit& aTarget,
1920 const TimeUnit& aStartTime,
1921 const TimeUnit& aEndTime,
1922 const nsTArray<SeekRange>& aRanges) {
1923 OGG_DEBUG("Seeking in unbuffered data to %s using bisection search",
1924 aTarget.ToString().get());
1925
1926 // If we've got an active Theora bitstream, determine the maximum possible
1927 // time in usecs which a keyframe could be before a given interframe. We
1928 // subtract this from our seek target, seek to the new target, and then
1929 // will decode forward to the original seek target. We should encounter a
1930 // keyframe in that interval. This prevents us from needing to run two
1931 // bisections; one for the seek target frame, and another to find its
1932 // keyframe. It's usually faster to just download this extra data, rather
1933 // tham perform two bisections to find the seek target's keyframe. We
1934 // don't do this offsetting when seeking in a buffered range,
1935 // as the extra decoding causes a noticeable speed hit when all the data
1936 // is buffered (compared to just doing a bisection to exactly find the
1937 // keyframe).
1938 TimeUnit keyframeOffset = TimeUnit::Zero();
1939 if (aType == TrackInfo::kVideoTrack && mTheoraState) {
1940 keyframeOffset = mTheoraState->MaxKeyframeOffset();
1941 }
1942 // Add in the Opus pre-roll if necessary, as well.
1943 if (aType == TrackInfo::kAudioTrack && mOpusState) {
1944 keyframeOffset = std::max(keyframeOffset, OGG_SEEK_OPUS_PREROLL);
1945 }
1946 TimeUnit seekTarget = std::max(aStartTime, aTarget - keyframeOffset);
1947 // Minimize the bisection search space using the known timestamps from the
1948 // buffered ranges.
1949 SeekRange k =
1950 SelectSeekRange(aType, aRanges, seekTarget, aStartTime, aEndTime, false);
1951 return SeekBisection(aType, seekTarget, k, OGG_SEEK_FUZZ_USECS);
1952}
1953
1954nsresult OggDemuxer::SeekBisection(TrackInfo::TrackType aType,
1955 const TimeUnit& aTarget,
1956 const SeekRange& aRange,
1957 const TimeUnit& aFuzz) {
1958 nsresult res;
1959
1960 if (aTarget <= aRange.mTimeStart) {
1961 if (NS_FAILED(Reset(aType))((bool)(__builtin_expect(!!(NS_FAILED_impl(Reset(aType))), 0)
))
) {
1962 return NS_ERROR_FAILURE;
1963 }
1964 res = Resource(aType)->Seek(nsISeekableStream::NS_SEEK_SET, 0);
1965 NS_ENSURE_SUCCESS(res, res)do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "res", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1965); return res; } } while (false)
;
1966 return NS_OK;
1967 }
1968
1969 // Bisection search, find start offset of last page with end time less than
1970 // the seek target.
1971 ogg_int64_t startOffset = aRange.mOffsetStart;
1972 ogg_int64_t startTime = aRange.mTimeStart.ToMicroseconds();
1973 ogg_int64_t startLength = 0; // Length of the page at startOffset.
1974 ogg_int64_t endOffset = aRange.mOffsetEnd;
1975 ogg_int64_t endTime = aRange.mTimeEnd.ToMicroseconds();
1976
1977 ogg_int64_t seekTarget = aTarget.ToMicroseconds();
1978 int64_t seekLowerBound =
1979 std::max(static_cast<int64_t>(0),
1980 aTarget.ToMicroseconds() - aFuzz.ToMicroseconds());
1981 int hops = 0;
1982 DebugOnly<ogg_int64_t> previousGuess = -1;
1983 int backsteps = 0;
1984 const int maxBackStep = 10;
1985 MOZ_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(static_cast<uint64_t>(PAGE_STEP) * pow(2.0, maxBackStep
) < (2147483647))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(static_cast<uint64_t>(
PAGE_STEP) * pow(2.0, maxBackStep) < (2147483647)))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("static_cast<uint64_t>(PAGE_STEP) * pow(2.0, maxBackStep) < (2147483647)"
" (" "Backstep calculation must not overflow" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1987); AnnotateMozCrashReason("MOZ_ASSERT" "(" "static_cast<uint64_t>(PAGE_STEP) * pow(2.0, maxBackStep) < (2147483647)"
") (" "Backstep calculation must not overflow" ")"); do { *(
(volatile int*)__null) = 1987; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
1986 static_cast<uint64_t>(PAGE_STEP) * pow(2.0, maxBackStep) < INT32_MAX,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(static_cast<uint64_t>(PAGE_STEP) * pow(2.0, maxBackStep
) < (2147483647))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(static_cast<uint64_t>(
PAGE_STEP) * pow(2.0, maxBackStep) < (2147483647)))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("static_cast<uint64_t>(PAGE_STEP) * pow(2.0, maxBackStep) < (2147483647)"
" (" "Backstep calculation must not overflow" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1987); AnnotateMozCrashReason("MOZ_ASSERT" "(" "static_cast<uint64_t>(PAGE_STEP) * pow(2.0, maxBackStep) < (2147483647)"
") (" "Backstep calculation must not overflow" ")"); do { *(
(volatile int*)__null) = 1987; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
1987 "Backstep calculation must not overflow")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(static_cast<uint64_t>(PAGE_STEP) * pow(2.0, maxBackStep
) < (2147483647))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(static_cast<uint64_t>(
PAGE_STEP) * pow(2.0, maxBackStep) < (2147483647)))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("static_cast<uint64_t>(PAGE_STEP) * pow(2.0, maxBackStep) < (2147483647)"
" (" "Backstep calculation must not overflow" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 1987); AnnotateMozCrashReason("MOZ_ASSERT" "(" "static_cast<uint64_t>(PAGE_STEP) * pow(2.0, maxBackStep) < (2147483647)"
") (" "Backstep calculation must not overflow" ")"); do { *(
(volatile int*)__null) = 1987; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
1988
1989 // Seek via bisection search. Loop until we find the offset where the page
1990 // before the offset is before the seek target, and the page after the offset
1991 // is after the seek target.
1992 tainted_ogg<ogg_page*> page = mSandbox->malloc_in_sandbox<ogg_page>();
1993 if (!page) {
1994 return NS_ERROR_OUT_OF_MEMORY;
1995 }
1996 auto clean_page = MakeScopeExit([&] { mSandbox->free_in_sandbox(page); });
1997 while (true) {
1998 ogg_int64_t duration = 0;
1999 double target = 0;
2000 ogg_int64_t interval = 0;
2001 ogg_int64_t guess = 0;
2002 int skippedBytes = 0;
2003 ogg_int64_t pageOffset = 0;
2004 ogg_int64_t pageLength = 0;
2005 ogg_int64_t granuleTime = -1;
2006 bool mustBackoff = false;
2007
2008 // Guess where we should bisect to, based on the bit rate and the time
2009 // remaining in the interval. Loop until we can determine the time at
2010 // the guess offset.
2011 while (true) {
2012 // Discard any previously buffered packets/pages.
2013 if (NS_FAILED(Reset(aType))((bool)(__builtin_expect(!!(NS_FAILED_impl(Reset(aType))), 0)
))
) {
2014 return NS_ERROR_FAILURE;
2015 }
2016
2017 interval = endOffset - startOffset - startLength;
2018 if (interval == 0) {
2019 // Our interval is empty, we've found the optimal seek point, as the
2020 // page at the start offset is before the seek target, and the page
2021 // at the end offset is after the seek target.
2022 SEEK_LOG(LogLevel::Debug,
2023 ("Interval narrowed, terminating bisection."));
2024 break;
2025 }
2026
2027 // Guess bisection point.
2028 duration = endTime - startTime;
2029 target = (double)(seekTarget - startTime) / (double)duration;
2030 guess = startOffset + startLength +
2031 static_cast<ogg_int64_t>((double)interval * target);
2032 guess = std::min(guess, endOffset - PAGE_STEP);
2033 if (mustBackoff) {
2034 // We previously failed to determine the time at the guess offset,
2035 // probably because we ran out of data to decode. This usually happens
2036 // when we guess very close to the end offset. So reduce the guess
2037 // offset using an exponential backoff until we determine the time.
2038 SEEK_LOG(
2039 LogLevel::Debug,
2040 ("Backing off %d bytes, backsteps=%d",
2041 static_cast<int32_t>(PAGE_STEP * pow(2.0, backsteps)), backsteps));
2042 guess -= PAGE_STEP * static_cast<ogg_int64_t>(pow(2.0, backsteps));
2043
2044 if (guess <= startOffset) {
2045 // We've tried to backoff to before the start offset of our seek
2046 // range. This means we couldn't find a seek termination position
2047 // near the end of the seek range, so just set the seek termination
2048 // condition, and break out of the bisection loop. We'll begin
2049 // decoding from the start of the seek range.
2050 interval = 0;
2051 break;
2052 }
2053
2054 backsteps = std::min(backsteps + 1, maxBackStep);
2055 // We reset mustBackoff. If we still need to backoff further, it will
2056 // be set to true again.
2057 mustBackoff = false;
Value stored to 'mustBackoff' is never read
2058 } else {
2059 backsteps = 0;
2060 }
2061 guess = std::max(guess, startOffset + startLength);
2062
2063 SEEK_LOG(LogLevel::Debug,
2064 ("Seek loop start[o=%lld..%lld t=%lld] "
2065 "end[o=%lld t=%lld] "
2066 "interval=%lld target=%lf guess=%lld",
2067 startOffset, (startOffset + startLength), startTime, endOffset,
2068 endTime, interval, target, guess));
2069
2070 MOZ_ASSERT(guess >= startOffset + startLength,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(guess >= startOffset + startLength)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(guess >= startOffset + startLength))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("guess >= startOffset + startLength"
" (" "Guess must be after range start" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2071); AnnotateMozCrashReason("MOZ_ASSERT" "(" "guess >= startOffset + startLength"
") (" "Guess must be after range start" ")"); do { *((volatile
int*)__null) = 2071; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
2071 "Guess must be after range start")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(guess >= startOffset + startLength)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(guess >= startOffset + startLength))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("guess >= startOffset + startLength"
" (" "Guess must be after range start" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2071); AnnotateMozCrashReason("MOZ_ASSERT" "(" "guess >= startOffset + startLength"
") (" "Guess must be after range start" ")"); do { *((volatile
int*)__null) = 2071; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
2072 MOZ_ASSERT(guess < endOffset, "Guess must be before range end")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(guess < endOffset)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(guess < endOffset))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("guess < endOffset"
" (" "Guess must be before range end" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2072); AnnotateMozCrashReason("MOZ_ASSERT" "(" "guess < endOffset"
") (" "Guess must be before range end" ")"); do { *((volatile
int*)__null) = 2072; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
2073 MOZ_ASSERT(guess != previousGuess,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(guess != previousGuess)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(guess != previousGuess))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("guess != previousGuess"
" (" "Guess should be different to previous" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2074); AnnotateMozCrashReason("MOZ_ASSERT" "(" "guess != previousGuess"
") (" "Guess should be different to previous" ")"); do { *((
volatile int*)__null) = 2074; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
2074 "Guess should be different to previous")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(guess != previousGuess)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(guess != previousGuess))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("guess != previousGuess"
" (" "Guess should be different to previous" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2074); AnnotateMozCrashReason("MOZ_ASSERT" "(" "guess != previousGuess"
") (" "Guess should be different to previous" ")"); do { *((
volatile int*)__null) = 2074; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
2075 previousGuess = guess;
2076
2077 hops++;
2078
2079 // Locate the next page after our seek guess, and then figure out the
2080 // granule time of the audio and video bitstreams there. We can then
2081 // make a bisection decision based on our location in the media.
2082 PageSyncResult pageSyncResult =
2083 PageSync(mSandbox.get(), Resource(aType), OggSyncState(aType), false,
2084 guess, endOffset, page, skippedBytes);
2085 NS_ENSURE_TRUE(pageSyncResult != PAGE_SYNC_ERROR, NS_ERROR_FAILURE)do { if ((__builtin_expect(!!(!(pageSyncResult != PAGE_SYNC_ERROR
)), 0))) { NS_DebugBreak(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "pageSyncResult != PAGE_SYNC_ERROR"
") failed", nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2085); return NS_ERROR_FAILURE; } } while (false)
;
2086
2087 if (pageSyncResult == PAGE_SYNC_END_OF_RANGE) {
2088 // Our guess was too close to the end, we've ended up reading the end
2089 // page. Backoff exponentially from the end point, in case the last
2090 // page/frame/sample is huge.
2091 mustBackoff = true;
2092 SEEK_LOG(LogLevel::Debug, ("Hit the end of range, backing off"));
2093 continue;
2094 }
2095
2096 // We've located a page of length |ret| at |guess + skippedBytes|.
2097 // Remember where the page is located.
2098 pageOffset = guess + skippedBytes;
2099
2100 bool failedPageLenVerify = false;
2101 // Page length should be under 64Kb according to
2102 // https://xiph.org/ogg/doc/libogg/ogg_page.html
2103 pageLength = CopyAndVerifyOrFail(page->header_len + page->body_len,
2104 val <= 64 * 1024, &failedPageLenVerify);
2105 if (failedPageLenVerify) {
2106 return NS_ERROR_FAILURE;
2107 }
2108
2109 // Read pages until we can determine the granule time of the audio and
2110 // video bitstream.
2111 ogg_int64_t audioTime = -1;
2112 ogg_int64_t videoTime = -1;
2113 do {
2114 // Add the page to its codec state, determine its granule time.
2115 uint32_t serial = static_cast<uint32_t>(
2116 sandbox_invoke(*mSandbox, ogg_page_serialno, page)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_page_serialno)>( "ogg_page_serialno", reinterpret_cast
<void*>(&ogg_page_serialno), page)
2117 .unverified_safe_because(RLBOX_OGG_PAGE_SERIAL_REASON));
2118 OggCodecState* codecState = mCodecStore.Get(serial);
2119 if (codecState && GetCodecStateType(codecState) == aType) {
2120 if (codecState->mActive) {
2121 int ret =
2122 sandbox_invoke(*mSandbox, ogg_stream_pagein, codecState->mState,(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_stream_pagein)>( "ogg_stream_pagein", reinterpret_cast
<void*>(&ogg_stream_pagein), codecState->mState,
page)
2123 page)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_stream_pagein)>( "ogg_stream_pagein", reinterpret_cast
<void*>(&ogg_stream_pagein), codecState->mState,
page)
2124 .unverified_safe_because(RLBOX_OGG_STATE_ASSERT_REASON);
2125 NS_ENSURE_TRUE(ret == 0, NS_ERROR_FAILURE)do { if ((__builtin_expect(!!(!(ret == 0)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "ret == 0" ") failed", nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2125); return NS_ERROR_FAILURE; } } while (false)
;
2126 }
2127
2128 ogg_int64_t granulepos =
2129 sandbox_invoke(*mSandbox, ogg_page_granulepos, page)(*mSandbox).template INTERNAL_invoke_with_func_ptr<decltype
(ogg_page_granulepos)>( "ogg_page_granulepos", reinterpret_cast
<void*>(&ogg_page_granulepos), page)
2130 .unverified_safe_because(
2131 "If this is incorrect it may lead to incorrect seeking "
2132 "behavior in the stream, however will not affect the "
2133 "memory safety of the Firefox renderer.");
2134
2135 if (aType == TrackInfo::kAudioTrack && granulepos > 0 &&
2136 audioTime == -1) {
2137 if (mVorbisState && serial == mVorbisState->mSerial) {
2138 audioTime = mVorbisState->Time(granulepos).ToMicroseconds();
2139 } else if (mOpusState && serial == mOpusState->mSerial) {
2140 audioTime = mOpusState->Time(granulepos).ToMicroseconds();
2141 } else if (mFlacState && serial == mFlacState->mSerial) {
2142 audioTime = mFlacState->Time(granulepos).ToMicroseconds();
2143 }
2144 }
2145
2146 if (aType == TrackInfo::kVideoTrack && granulepos > 0 &&
2147 serial == mTheoraState->mSerial && videoTime == -1) {
2148 videoTime = mTheoraState->Time(granulepos).ToMicroseconds();
2149 }
2150
2151 if (pageOffset + pageLength >= endOffset) {
2152 // Hit end of readable data.
2153 break;
2154 }
2155 }
2156 if (!ReadOggPage(aType, page.to_opaque())) {
2157 break;
2158 }
2159
2160 } while ((aType == TrackInfo::kAudioTrack && audioTime == -1) ||
2161 (aType == TrackInfo::kVideoTrack && videoTime == -1));
2162
2163 if ((aType == TrackInfo::kAudioTrack && audioTime == -1) ||
2164 (aType == TrackInfo::kVideoTrack && videoTime == -1)) {
2165 // We don't have timestamps for all active tracks...
2166 if (pageOffset == startOffset + startLength &&
2167 pageOffset + pageLength >= endOffset) {
2168 // We read the entire interval without finding timestamps for all
2169 // active tracks. We know the interval start offset is before the seek
2170 // target, and the interval end is after the seek target, and we can't
2171 // terminate inside the interval, so we terminate the seek at the
2172 // start of the interval.
2173 interval = 0;
2174 break;
2175 }
2176
2177 // We should backoff; cause the guess to back off from the end, so
2178 // that we've got more room to capture.
2179 mustBackoff = true;
2180 continue;
2181 }
2182
2183 // We've found appropriate time stamps here. Proceed to bisect
2184 // the search space.
2185 granuleTime = aType == TrackInfo::kAudioTrack ? audioTime : videoTime;
2186 MOZ_ASSERT(granuleTime > 0, "Must get a granuletime")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(granuleTime > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(granuleTime > 0))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("granuleTime > 0"
" (" "Must get a granuletime" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2186); AnnotateMozCrashReason("MOZ_ASSERT" "(" "granuleTime > 0"
") (" "Must get a granuletime" ")"); do { *((volatile int*)__null
) = 2186; __attribute__((nomerge)) ::abort(); } while (false)
; } } while (false)
;
2187 break;
2188 } // End of "until we determine time at guess offset" loop.
2189
2190 if (interval == 0) {
2191 // Seek termination condition; we've found the page boundary of the
2192 // last page before the target, and the first page after the target.
2193 SEEK_LOG(LogLevel::Debug,
2194 ("Terminating seek at offset=%lld", startOffset));
2195 MOZ_ASSERT(startTime < aTarget.ToMicroseconds(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(startTime < aTarget.ToMicroseconds())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(startTime < aTarget.ToMicroseconds()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("startTime < aTarget.ToMicroseconds()"
" (" "Start time must always be less than target" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2196); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startTime < aTarget.ToMicroseconds()"
") (" "Start time must always be less than target" ")"); do {
*((volatile int*)__null) = 2196; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
2196 "Start time must always be less than target")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(startTime < aTarget.ToMicroseconds())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(startTime < aTarget.ToMicroseconds()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("startTime < aTarget.ToMicroseconds()"
" (" "Start time must always be less than target" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2196); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startTime < aTarget.ToMicroseconds()"
") (" "Start time must always be less than target" ")"); do {
*((volatile int*)__null) = 2196; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
2197 res = Resource(aType)->Seek(nsISeekableStream::NS_SEEK_SET, startOffset);
2198 NS_ENSURE_SUCCESS(res, res)do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "res", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2198); return res; } } while (false)
;
2199 if (NS_FAILED(Reset(aType))((bool)(__builtin_expect(!!(NS_FAILED_impl(Reset(aType))), 0)
))
) {
2200 return NS_ERROR_FAILURE;
2201 }
2202 break;
2203 }
2204
2205 SEEK_LOG(LogLevel::Debug,
2206 ("Time at offset %lld is %lld", guess, granuleTime));
2207 if (granuleTime < seekTarget && granuleTime > seekLowerBound) {
2208 // We're within the fuzzy region in which we want to terminate the search.
2209 res = Resource(aType)->Seek(nsISeekableStream::NS_SEEK_SET, pageOffset);
2210 NS_ENSURE_SUCCESS(res, res)do { nsresult __rv = res; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "res", "res", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2210); return res; } } while (false)
;
2211 if (NS_FAILED(Reset(aType))((bool)(__builtin_expect(!!(NS_FAILED_impl(Reset(aType))), 0)
))
) {
2212 return NS_ERROR_FAILURE;
2213 }
2214 SEEK_LOG(LogLevel::Debug,
2215 ("Terminating seek at offset=%lld", pageOffset));
2216 break;
2217 }
2218
2219 if (granuleTime >= seekTarget) {
2220 // We've landed after the seek target.
2221 MOZ_ASSERT(pageOffset < endOffset, "offset_end must decrease")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(pageOffset < endOffset)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pageOffset < endOffset)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("pageOffset < endOffset"
" (" "offset_end must decrease" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2221); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pageOffset < endOffset"
") (" "offset_end must decrease" ")"); do { *((volatile int*
)__null) = 2221; __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2222 endOffset = pageOffset;
2223 endTime = granuleTime;
2224 } else if (granuleTime < seekTarget) {
2225 // Landed before seek target.
2226 MOZ_ASSERT(pageOffset >= startOffset + startLength,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(pageOffset >= startOffset + startLength)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(pageOffset >= startOffset + startLength))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("pageOffset >= startOffset + startLength"
" (" "Bisection point should be at or after end of first page in "
"interval" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2228); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pageOffset >= startOffset + startLength"
") (" "Bisection point should be at or after end of first page in "
"interval" ")"); do { *((volatile int*)__null) = 2228; __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
2227 "Bisection point should be at or after end of first page in "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(pageOffset >= startOffset + startLength)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(pageOffset >= startOffset + startLength))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("pageOffset >= startOffset + startLength"
" (" "Bisection point should be at or after end of first page in "
"interval" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2228); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pageOffset >= startOffset + startLength"
") (" "Bisection point should be at or after end of first page in "
"interval" ")"); do { *((volatile int*)__null) = 2228; __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
2228 "interval")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(pageOffset >= startOffset + startLength)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(pageOffset >= startOffset + startLength))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("pageOffset >= startOffset + startLength"
" (" "Bisection point should be at or after end of first page in "
"interval" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2228); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pageOffset >= startOffset + startLength"
") (" "Bisection point should be at or after end of first page in "
"interval" ")"); do { *((volatile int*)__null) = 2228; __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
2229 startOffset = pageOffset;
2230 startLength = pageLength;
2231 startTime = granuleTime;
2232 }
2233 MOZ_ASSERT(startTime <= seekTarget, "Must be before seek target")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(startTime <= seekTarget)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(startTime <= seekTarget))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("startTime <= seekTarget"
" (" "Must be before seek target" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2233); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startTime <= seekTarget"
") (" "Must be before seek target" ")"); do { *((volatile int
*)__null) = 2233; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
2234 MOZ_ASSERT(endTime >= seekTarget, "End must be after seek target")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(endTime >= seekTarget)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(endTime >= seekTarget))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("endTime >= seekTarget"
" (" "End must be after seek target" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/media/ogg/OggDemuxer.cpp"
, 2234); AnnotateMozCrashReason("MOZ_ASSERT" "(" "endTime >= seekTarget"
") (" "End must be after seek target" ")"); do { *((volatile
int*)__null) = 2234; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
2235 }
2236
2237 (void)hops;
2238 SEEK_LOG(LogLevel::Debug, ("Seek complete in %d bisections.", hops));
2239
2240 return NS_OK;
2241}
2242
2243#undef OGG_DEBUG
2244#undef SEEK_LOG
2245#undef CopyAndVerifyOrFail
2246} // namespace mozilla