Line data Source code
1 : /* This Source Code Form is subject to the terms of the Mozilla Public
2 : * License, v. 2.0. If a copy of the MPL was not distributed with this
3 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 :
5 : #include "include/MPEG4Extractor.h"
6 : #include "media/stagefright/DataSource.h"
7 : #include "media/stagefright/MediaDefs.h"
8 : #include "media/stagefright/MediaSource.h"
9 : #include "media/stagefright/MetaData.h"
10 : #include "mozilla/Assertions.h"
11 : #include "mozilla/CheckedInt.h"
12 : #include "mozilla/EndianUtils.h"
13 : #include "mozilla/Logging.h"
14 : #include "mozilla/RefPtr.h"
15 : #include "mozilla/SizePrintfMacros.h"
16 : #include "mozilla/Telemetry.h"
17 : #include "mozilla/UniquePtr.h"
18 : #include "VideoUtils.h"
19 : #include "mp4_demuxer/MoofParser.h"
20 : #include "mp4_demuxer/MP4Metadata.h"
21 : #include "mp4_demuxer/Stream.h"
22 : #include "MediaPrefs.h"
23 : #include "mp4parse.h"
24 :
25 : #include <limits>
26 : #include <stdint.h>
27 : #include <vector>
28 :
29 :
30 0 : struct FreeMP4Parser { void operator()(mp4parse_parser* aPtr) { mp4parse_free(aPtr); } };
31 :
32 : using namespace stagefright;
33 : using mozilla::media::TimeUnit;
34 :
35 : namespace mp4_demuxer
36 : {
37 :
38 : static LazyLogModule sLog("MP4Metadata");
39 :
40 : class DataSourceAdapter : public DataSource
41 : {
42 : public:
43 0 : explicit DataSourceAdapter(Stream* aSource) : mSource(aSource) {}
44 :
45 0 : ~DataSourceAdapter() {}
46 :
47 0 : virtual status_t initCheck() const { return NO_ERROR; }
48 :
49 0 : virtual ssize_t readAt(off64_t offset, void* data, size_t size)
50 : {
51 0 : MOZ_ASSERT(((ssize_t)size) >= 0);
52 : size_t bytesRead;
53 0 : if (!mSource->ReadAt(offset, data, size, &bytesRead))
54 0 : return ERROR_IO;
55 :
56 0 : if (bytesRead == 0)
57 0 : return ERROR_END_OF_STREAM;
58 :
59 0 : MOZ_ASSERT(((ssize_t)bytesRead) > 0);
60 0 : return bytesRead;
61 : }
62 :
63 0 : virtual status_t getSize(off64_t* size)
64 : {
65 0 : if (!mSource->Length(size))
66 0 : return ERROR_UNSUPPORTED;
67 0 : return NO_ERROR;
68 : }
69 :
70 0 : virtual uint32_t flags() { return kWantsPrefetching | kIsHTTPBasedSource; }
71 :
72 0 : virtual status_t reconnectAtOffset(off64_t offset) { return NO_ERROR; }
73 :
74 : private:
75 : RefPtr<Stream> mSource;
76 : };
77 :
78 : class MP4MetadataStagefright
79 : {
80 : public:
81 : explicit MP4MetadataStagefright(Stream* aSource);
82 : ~MP4MetadataStagefright();
83 :
84 : static MP4Metadata::ResultAndByteBuffer Metadata(Stream* aSource);
85 :
86 : MP4Metadata::ResultAndTrackCount
87 : GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
88 : MP4Metadata::ResultAndTrackInfo GetTrackInfo(
89 : mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const;
90 : bool CanSeek() const;
91 :
92 : MP4Metadata::ResultAndCryptoFile Crypto() const;
93 :
94 : MediaResult ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID);
95 :
96 : private:
97 : int32_t GetTrackNumber(mozilla::TrackID aTrackID);
98 : void UpdateCrypto(const stagefright::MetaData* aMetaData);
99 : mozilla::UniquePtr<mozilla::TrackInfo> CheckTrack(const char* aMimeType,
100 : stagefright::MetaData* aMetaData,
101 : int32_t aIndex) const;
102 : CryptoFile mCrypto;
103 : RefPtr<Stream> mSource;
104 : sp<MediaExtractor> mMetadataExtractor;
105 : bool mCanSeek;
106 : };
107 :
108 : // Wrap an mp4_demuxer::Stream to remember the read offset.
109 :
110 : class RustStreamAdaptor {
111 : public:
112 0 : explicit RustStreamAdaptor(Stream* aSource)
113 0 : : mSource(aSource)
114 0 : , mOffset(0)
115 : {
116 0 : }
117 :
118 0 : ~RustStreamAdaptor() {}
119 :
120 : bool Read(uint8_t* buffer, uintptr_t size, size_t* bytes_read);
121 :
122 : private:
123 : Stream* mSource;
124 : CheckedInt<size_t> mOffset;
125 : };
126 :
127 : class MP4MetadataRust
128 : {
129 : public:
130 : explicit MP4MetadataRust(Stream* aSource);
131 : ~MP4MetadataRust();
132 :
133 : static MP4Metadata::ResultAndByteBuffer Metadata(Stream* aSource);
134 :
135 : MP4Metadata::ResultAndTrackCount
136 : GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
137 : MP4Metadata::ResultAndTrackInfo GetTrackInfo(
138 : mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const;
139 : bool CanSeek() const;
140 :
141 : MP4Metadata::ResultAndCryptoFile Crypto() const;
142 :
143 : MediaResult ReadTrackIndice(mp4parse_byte_data* aIndices, mozilla::TrackID aTrackID);
144 :
145 : bool Init();
146 :
147 : private:
148 : void UpdateCrypto();
149 : Maybe<uint32_t> TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const;
150 :
151 : CryptoFile mCrypto;
152 : RefPtr<Stream> mSource;
153 : RustStreamAdaptor mRustSource;
154 : mozilla::UniquePtr<mp4parse_parser, FreeMP4Parser> mRustParser;
155 : };
156 :
157 0 : class IndiceWrapperStagefright : public IndiceWrapper {
158 : public:
159 : size_t Length() const override;
160 :
161 : bool GetIndice(size_t aIndex, Index::Indice& aIndice) const override;
162 :
163 : explicit IndiceWrapperStagefright(FallibleTArray<Index::Indice>& aIndice);
164 :
165 : protected:
166 : FallibleTArray<Index::Indice> mIndice;
167 : };
168 :
169 0 : IndiceWrapperStagefright::IndiceWrapperStagefright(FallibleTArray<Index::Indice>& aIndice)
170 : {
171 0 : mIndice.SwapElements(aIndice);
172 0 : }
173 :
174 : size_t
175 0 : IndiceWrapperStagefright::Length() const
176 : {
177 0 : return mIndice.Length();
178 : }
179 :
180 : bool
181 0 : IndiceWrapperStagefright::GetIndice(size_t aIndex, Index::Indice& aIndice) const
182 : {
183 0 : if (aIndex >= mIndice.Length()) {
184 0 : MOZ_LOG(sLog, LogLevel::Error, ("Index overflow in indice"));
185 0 : return false;
186 : }
187 :
188 0 : aIndice = mIndice[aIndex];
189 0 : return true;
190 : }
191 :
192 : // the owner of mIndice is rust mp4 paser, so lifetime of this class
193 : // SHOULD NOT longer than rust parser.
194 0 : class IndiceWrapperRust : public IndiceWrapper
195 : {
196 : public:
197 : size_t Length() const override;
198 :
199 : bool GetIndice(size_t aIndex, Index::Indice& aIndice) const override;
200 :
201 : explicit IndiceWrapperRust(mp4parse_byte_data& aRustIndice);
202 :
203 : protected:
204 : UniquePtr<mp4parse_byte_data> mIndice;
205 : };
206 :
207 0 : IndiceWrapperRust::IndiceWrapperRust(mp4parse_byte_data& aRustIndice)
208 0 : : mIndice(mozilla::MakeUnique<mp4parse_byte_data>())
209 : {
210 0 : mIndice->length = aRustIndice.length;
211 0 : mIndice->indices = aRustIndice.indices;
212 0 : }
213 :
214 : size_t
215 0 : IndiceWrapperRust::Length() const
216 : {
217 0 : return mIndice->length;
218 : }
219 :
220 : bool
221 0 : IndiceWrapperRust::GetIndice(size_t aIndex, Index::Indice& aIndice) const
222 : {
223 0 : if (aIndex >= mIndice->length) {
224 0 : MOZ_LOG(sLog, LogLevel::Error, ("Index overflow in indice"));
225 0 : return false;
226 : }
227 :
228 0 : const mp4parse_indice* indice = &mIndice->indices[aIndex];
229 0 : aIndice.start_offset = indice->start_offset;
230 0 : aIndice.end_offset = indice->end_offset;
231 0 : aIndice.start_composition = indice->start_composition;
232 0 : aIndice.end_composition = indice->end_composition;
233 0 : aIndice.start_decode = indice->start_decode;
234 0 : aIndice.sync = indice->sync;
235 0 : return true;
236 : }
237 :
238 0 : MP4Metadata::MP4Metadata(Stream* aSource)
239 : : mStagefright(MakeUnique<MP4MetadataStagefright>(aSource))
240 : , mRust(MakeUnique<MP4MetadataRust>(aSource))
241 : , mReportedAudioTrackTelemetry(false)
242 0 : , mReportedVideoTrackTelemetry(false)
243 : {
244 0 : mDisableRust = !MediaPrefs::EnableRustMP4Parser() && !MediaPrefs::RustTestMode();
245 0 : if (mDisableRust) {
246 0 : return;
247 : }
248 : // Fallback to stagefright if it fails.
249 0 : mDisableRust = !mRust->Init();
250 : }
251 :
252 0 : MP4Metadata::~MP4Metadata()
253 : {
254 0 : }
255 :
256 : /*static*/ MP4Metadata::ResultAndByteBuffer
257 0 : MP4Metadata::Metadata(Stream* aSource)
258 : {
259 0 : return MP4MetadataStagefright::Metadata(aSource);
260 : }
261 :
262 : static const char *
263 0 : TrackTypeToString(mozilla::TrackInfo::TrackType aType)
264 : {
265 0 : switch (aType) {
266 : case mozilla::TrackInfo::kAudioTrack:
267 0 : return "audio";
268 : case mozilla::TrackInfo::kVideoTrack:
269 0 : return "video";
270 : default:
271 0 : return "unknown";
272 : }
273 : }
274 :
275 : MP4Metadata::ResultAndTrackCount
276 0 : MP4Metadata::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
277 : {
278 : MP4Metadata::ResultAndTrackCount numTracks =
279 0 : mStagefright->GetNumberTracks(aType);
280 :
281 : MP4Metadata::ResultAndTrackCount numTracksRust =
282 0 : mRust->GetNumberTracks(aType);
283 0 : MOZ_LOG(sLog, LogLevel::Info, ("%s tracks found: stagefright=(%s)%u rust=(%s)%u",
284 : TrackTypeToString(aType),
285 : numTracks.Result().Description().get(),
286 : numTracks.Ref(),
287 : numTracksRust.Result().Description().get(),
288 : numTracksRust.Ref()));
289 :
290 :
291 : // Consider '0' and 'error' the same for comparison purposes.
292 : // (Mostly because Stagefright never returns errors, but Rust may.)
293 : bool numTracksMatch =
294 0 : (numTracks.Ref() != NumberTracksError() ? numTracks.Ref() : 0) ==
295 0 : (numTracksRust.Ref() != NumberTracksError() ? numTracksRust.Ref() : 0);
296 :
297 0 : if (aType == mozilla::TrackInfo::kAudioTrack && !mReportedAudioTrackTelemetry) {
298 0 : Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_TRACK_MATCH_AUDIO,
299 0 : numTracksMatch);
300 0 : mReportedAudioTrackTelemetry = true;
301 0 : } else if (aType == mozilla::TrackInfo::kVideoTrack && !mReportedVideoTrackTelemetry) {
302 0 : Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_TRACK_MATCH_VIDEO,
303 0 : numTracksMatch);
304 0 : mReportedVideoTrackTelemetry = true;
305 : }
306 :
307 0 : if (!numTracksMatch && MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()) {
308 0 : return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
309 0 : RESULT_DETAIL("Different numbers of tracks: "
310 : "Stagefright=%u (%s) Rust=%u (%s)",
311 : numTracks.Ref(),
312 : numTracks.Result().Description().get(),
313 : numTracksRust.Ref(),
314 : numTracksRust.Result().Description().get())),
315 0 : NumberTracksError()};
316 : }
317 :
318 0 : return mDisableRust ? numTracks : numTracksRust;
319 : }
320 :
321 : static const char*
322 0 : GetDifferentField(const mozilla::TrackInfo& info,
323 : const mozilla::TrackInfo& infoRust)
324 : {
325 0 : if (infoRust.mId != info.mId) { return "Id"; }
326 0 : if (infoRust.mKind != info.mKind) { return "Kind"; }
327 0 : if (infoRust.mLabel != info.mLabel) { return "Label"; }
328 0 : if (infoRust.mLanguage != info.mLanguage) { return "Language"; }
329 0 : if (infoRust.mEnabled != info.mEnabled) { return "Enabled"; }
330 0 : if (infoRust.mTrackId != info.mTrackId) { return "TrackId"; }
331 0 : if (infoRust.mMimeType != info.mMimeType) { return "MimeType"; }
332 0 : if (infoRust.mDuration != info.mDuration) { return "Duration"; }
333 0 : if (infoRust.mMediaTime != info.mMediaTime) { return "MediaTime"; }
334 0 : if (infoRust.mCrypto.mValid != info.mCrypto.mValid) { return "Crypto-Valid"; }
335 0 : if (infoRust.mCrypto.mMode != info.mCrypto.mMode) { return "Crypto-Mode"; }
336 0 : if (infoRust.mCrypto.mIVSize != info.mCrypto.mIVSize) { return "Crypto-IVSize"; }
337 0 : if (infoRust.mCrypto.mKeyId != info.mCrypto.mKeyId) { return "Crypto-KeyId"; }
338 0 : switch (info.GetType()) {
339 : case mozilla::TrackInfo::kAudioTrack: {
340 0 : const AudioInfo *audioRust = infoRust.GetAsAudioInfo();
341 0 : const AudioInfo *audio = info.GetAsAudioInfo();
342 0 : if (audioRust->mRate != audio->mRate) { return "Rate"; }
343 0 : if (audioRust->mChannels != audio->mChannels) { return "Channels"; }
344 0 : if (audioRust->mBitDepth != audio->mBitDepth) { return "BitDepth"; }
345 0 : if (audioRust->mProfile != audio->mProfile) { return "Profile"; }
346 0 : if (audioRust->mExtendedProfile != audio->mExtendedProfile) { return "ExtendedProfile"; }
347 0 : break;
348 : }
349 : case mozilla::TrackInfo::kVideoTrack: {
350 0 : const VideoInfo *videoRust = infoRust.GetAsVideoInfo();
351 0 : const VideoInfo *video = info.GetAsVideoInfo();
352 0 : if (videoRust->mDisplay != video->mDisplay) { return "Display"; }
353 0 : if (videoRust->mImage != video->mImage) { return "Image"; }
354 0 : if (*videoRust->mExtraData != *video->mExtraData) { return "ExtraData"; }
355 : // mCodecSpecificConfig is for video/mp4-es, not video/avc. Since video/mp4-es
356 : // is supported on b2g only, it could be removed from TrackInfo.
357 0 : if (*videoRust->mCodecSpecificConfig != *video->mCodecSpecificConfig) { return "CodecSpecificConfig"; }
358 0 : break;
359 : }
360 : default:
361 0 : break;
362 : }
363 :
364 0 : return nullptr;
365 : }
366 :
367 : MP4Metadata::ResultAndTrackInfo
368 0 : MP4Metadata::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
369 : size_t aTrackNumber) const
370 : {
371 : MP4Metadata::ResultAndTrackInfo info =
372 0 : mStagefright->GetTrackInfo(aType, aTrackNumber);
373 :
374 : MP4Metadata::ResultAndTrackInfo infoRust =
375 0 : mRust->GetTrackInfo(aType, aTrackNumber);
376 :
377 0 : if (info.Ref() && infoRust.Ref() && MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()) {
378 0 : const char* diff = GetDifferentField(*info.Ref(), *infoRust.Ref());
379 0 : if (diff) {
380 0 : return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
381 0 : RESULT_DETAIL("Different field '%s' between "
382 : "Stagefright (%s) and Rust (%s)",
383 : diff,
384 : info.Result().Description().get(),
385 : infoRust.Result().Description().get())),
386 0 : MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()
387 0 : ? mozilla::UniquePtr<mozilla::TrackInfo>(nullptr)
388 0 : : mDisableRust ? Move(info.Ref()) : Move(infoRust.Ref())};
389 : }
390 : }
391 :
392 0 : return mDisableRust ? Move(info) : Move(infoRust);
393 : }
394 :
395 : bool
396 0 : MP4Metadata::CanSeek() const
397 : {
398 0 : return mStagefright->CanSeek();
399 : }
400 :
401 : MP4Metadata::ResultAndCryptoFile
402 0 : MP4Metadata::Crypto() const
403 : {
404 0 : MP4Metadata::ResultAndCryptoFile crypto = mStagefright->Crypto();
405 0 : MP4Metadata::ResultAndCryptoFile rustCrypto = mRust->Crypto();
406 :
407 0 : if (MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()) {
408 0 : if (rustCrypto.Ref()->pssh != crypto.Ref()->pssh) {
409 0 : return {MediaResult(
410 : NS_ERROR_DOM_MEDIA_METADATA_ERR,
411 0 : RESULT_DETAIL("Mismatch between Stagefright (%s) and Rust (%s) crypto file",
412 : crypto.Result().Description().get(),
413 : rustCrypto.Result().Description().get())),
414 0 : mDisableRust ? crypto.Ref() : rustCrypto.Ref()};
415 : }
416 : }
417 :
418 0 : return mDisableRust ? crypto : rustCrypto;
419 : }
420 :
421 : MP4Metadata::ResultAndIndice
422 0 : MP4Metadata::GetTrackIndice(mozilla::TrackID aTrackID)
423 : {
424 0 : FallibleTArray<Index::Indice> indiceSF;
425 0 : if (mDisableRust || MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()) {
426 0 : MediaResult rv = mStagefright->ReadTrackIndex(indiceSF, aTrackID);
427 0 : if (NS_FAILED(rv)) {
428 0 : return {Move(rv), nullptr};
429 : }
430 : }
431 :
432 0 : mp4parse_byte_data indiceRust = {};
433 0 : if (!mDisableRust || MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()) {
434 0 : MediaResult rvRust = mRust->ReadTrackIndice(&indiceRust, aTrackID);
435 0 : if (NS_FAILED(rvRust)) {
436 0 : return {Move(rvRust), nullptr};
437 : }
438 : }
439 :
440 0 : if (MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()) {
441 0 : MOZ_DIAGNOSTIC_ASSERT(indiceRust.length == indiceSF.Length());
442 0 : for (uint32_t i = 0; i < indiceRust.length; i++) {
443 0 : MOZ_DIAGNOSTIC_ASSERT(indiceRust.indices[i].start_offset == indiceSF[i].start_offset);
444 0 : MOZ_DIAGNOSTIC_ASSERT(indiceRust.indices[i].end_offset == indiceSF[i].end_offset);
445 0 : MOZ_DIAGNOSTIC_ASSERT(llabs(indiceRust.indices[i].start_composition - int64_t(indiceSF[i].start_composition)) <= 1);
446 0 : MOZ_DIAGNOSTIC_ASSERT(llabs(indiceRust.indices[i].end_composition - int64_t(indiceSF[i].end_composition)) <= 1);
447 0 : MOZ_DIAGNOSTIC_ASSERT(llabs(indiceRust.indices[i].start_decode - int64_t(indiceSF[i].start_decode)) <= 1);
448 0 : MOZ_DIAGNOSTIC_ASSERT(indiceRust.indices[i].sync == indiceSF[i].sync);
449 : }
450 : }
451 :
452 0 : UniquePtr<IndiceWrapper> indice;
453 0 : if (mDisableRust) {
454 0 : indice = mozilla::MakeUnique<IndiceWrapperStagefright>(indiceSF);
455 : } else {
456 0 : indice = mozilla::MakeUnique<IndiceWrapperRust>(indiceRust);
457 : }
458 :
459 0 : return {NS_OK, Move(indice)};
460 : }
461 :
462 : static inline MediaResult
463 0 : ConvertIndex(FallibleTArray<Index::Indice>& aDest,
464 : const nsTArray<stagefright::MediaSource::Indice>& aIndex,
465 : int64_t aMediaTime)
466 : {
467 0 : if (!aDest.SetCapacity(aIndex.Length(), mozilla::fallible)) {
468 : return MediaResult{NS_ERROR_OUT_OF_MEMORY,
469 0 : RESULT_DETAIL("Could not resize to %" PRIuSIZE " indices",
470 0 : aIndex.Length())};
471 : }
472 0 : for (size_t i = 0; i < aIndex.Length(); i++) {
473 : Index::Indice indice;
474 0 : const stagefright::MediaSource::Indice& s_indice = aIndex[i];
475 0 : indice.start_offset = s_indice.start_offset;
476 0 : indice.end_offset = s_indice.end_offset;
477 0 : indice.start_composition = s_indice.start_composition - aMediaTime;
478 0 : indice.end_composition = s_indice.end_composition - aMediaTime;
479 0 : indice.start_decode = s_indice.start_decode;
480 0 : indice.sync = s_indice.sync;
481 : // FIXME: Make this infallible after bug 968520 is done.
482 0 : MOZ_ALWAYS_TRUE(aDest.AppendElement(indice, mozilla::fallible));
483 0 : MOZ_LOG(sLog, LogLevel::Debug, ("s_o: %" PRIu64 ", e_o: %" PRIu64 ", s_c: %" PRIu64 ", e_c: %" PRIu64 ", s_d: %" PRIu64 ", sync: %d\n",
484 : indice.start_offset, indice.end_offset, indice.start_composition, indice.end_composition,
485 : indice.start_decode, indice.sync));
486 : }
487 0 : return NS_OK;
488 : }
489 :
490 0 : MP4MetadataStagefright::MP4MetadataStagefright(Stream* aSource)
491 : : mSource(aSource)
492 0 : , mMetadataExtractor(new MPEG4Extractor(new DataSourceAdapter(mSource)))
493 0 : , mCanSeek(mMetadataExtractor->flags() & MediaExtractor::CAN_SEEK)
494 : {
495 0 : sp<MetaData> metaData = mMetadataExtractor->getMetaData();
496 :
497 0 : if (metaData.get()) {
498 0 : UpdateCrypto(metaData.get());
499 : }
500 0 : }
501 :
502 0 : MP4MetadataStagefright::~MP4MetadataStagefright()
503 : {
504 0 : }
505 :
506 : MP4Metadata::ResultAndTrackCount
507 0 : MP4MetadataStagefright::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
508 : {
509 0 : size_t tracks = mMetadataExtractor->countTracks();
510 0 : uint32_t total = 0;
511 0 : for (size_t i = 0; i < tracks; i++) {
512 0 : sp<MetaData> metaData = mMetadataExtractor->getTrackMetaData(i);
513 :
514 : const char* mimeType;
515 0 : if (metaData == nullptr || !metaData->findCString(kKeyMIMEType, &mimeType)) {
516 0 : continue;
517 : }
518 0 : switch (aType) {
519 : case mozilla::TrackInfo::kAudioTrack:
520 0 : if (!strncmp(mimeType, "audio/", 6) &&
521 0 : CheckTrack(mimeType, metaData.get(), i)) {
522 0 : total++;
523 : }
524 0 : break;
525 : case mozilla::TrackInfo::kVideoTrack:
526 0 : if (!strncmp(mimeType, "video/", 6) &&
527 0 : CheckTrack(mimeType, metaData.get(), i)) {
528 0 : total++;
529 : }
530 0 : break;
531 : default:
532 0 : break;
533 : }
534 : }
535 0 : return {NS_OK, total};
536 : }
537 :
538 : MP4Metadata::ResultAndTrackInfo
539 0 : MP4MetadataStagefright::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
540 : size_t aTrackNumber) const
541 : {
542 0 : size_t tracks = mMetadataExtractor->countTracks();
543 0 : if (!tracks) {
544 0 : return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
545 0 : RESULT_DETAIL("No %s tracks",
546 : TrackTypeToStr(aType))),
547 0 : nullptr};
548 : }
549 0 : int32_t index = -1;
550 : const char* mimeType;
551 0 : sp<MetaData> metaData;
552 :
553 0 : size_t i = 0;
554 0 : while (i < tracks) {
555 0 : metaData = mMetadataExtractor->getTrackMetaData(i);
556 :
557 0 : if (metaData == nullptr || !metaData->findCString(kKeyMIMEType, &mimeType)) {
558 0 : continue;
559 : }
560 0 : switch (aType) {
561 : case mozilla::TrackInfo::kAudioTrack:
562 0 : if (!strncmp(mimeType, "audio/", 6) &&
563 0 : CheckTrack(mimeType, metaData.get(), i)) {
564 0 : index++;
565 : }
566 0 : break;
567 : case mozilla::TrackInfo::kVideoTrack:
568 0 : if (!strncmp(mimeType, "video/", 6) &&
569 0 : CheckTrack(mimeType, metaData.get(), i)) {
570 0 : index++;
571 : }
572 0 : break;
573 : default:
574 0 : break;
575 : }
576 0 : if (index == aTrackNumber) {
577 0 : break;
578 : }
579 0 : i++;
580 : }
581 0 : if (index < 0) {
582 0 : return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
583 0 : RESULT_DETAIL("Cannot access %s track #%zu",
584 : TrackTypeToStr(aType),
585 : aTrackNumber)),
586 0 : nullptr};
587 : }
588 :
589 0 : UniquePtr<mozilla::TrackInfo> e = CheckTrack(mimeType, metaData.get(), index);
590 :
591 0 : if (e) {
592 0 : metaData = mMetadataExtractor->getMetaData();
593 : int64_t movieDuration;
594 0 : if (!e->mDuration.IsPositive() &&
595 0 : metaData->findInt64(kKeyMovieDuration, &movieDuration)) {
596 : // No duration in track, use movie extend header box one.
597 0 : e->mDuration = TimeUnit::FromMicroseconds(movieDuration);
598 : }
599 : }
600 :
601 0 : return {NS_OK, Move(e)};
602 : }
603 :
604 : mozilla::UniquePtr<mozilla::TrackInfo>
605 0 : MP4MetadataStagefright::CheckTrack(const char* aMimeType,
606 : stagefright::MetaData* aMetaData,
607 : int32_t aIndex) const
608 : {
609 0 : sp<MediaSource> track = mMetadataExtractor->getTrack(aIndex);
610 0 : if (!track.get()) {
611 0 : return nullptr;
612 : }
613 :
614 0 : UniquePtr<mozilla::TrackInfo> e;
615 :
616 0 : if (!strncmp(aMimeType, "audio/", 6)) {
617 0 : auto info = mozilla::MakeUnique<MP4AudioInfo>();
618 0 : info->Update(aMetaData, aMimeType);
619 0 : e = Move(info);
620 0 : } else if (!strncmp(aMimeType, "video/", 6)) {
621 0 : auto info = mozilla::MakeUnique<MP4VideoInfo>();
622 0 : info->Update(aMetaData, aMimeType);
623 0 : e = Move(info);
624 : }
625 :
626 0 : if (e && e->IsValid()) {
627 0 : return e;
628 : }
629 :
630 0 : return nullptr;
631 : }
632 :
633 : bool
634 0 : MP4MetadataStagefright::CanSeek() const
635 : {
636 0 : return mCanSeek;
637 : }
638 :
639 : MP4Metadata::ResultAndCryptoFile
640 0 : MP4MetadataStagefright::Crypto() const
641 : {
642 0 : return {NS_OK, &mCrypto};
643 : }
644 :
645 : void
646 0 : MP4MetadataStagefright::UpdateCrypto(const MetaData* aMetaData)
647 : {
648 : const void* data;
649 : size_t size;
650 : uint32_t type;
651 :
652 : // There's no point in checking that the type matches anything because it
653 : // isn't set consistently in the MPEG4Extractor.
654 0 : if (!aMetaData->findData(kKeyPssh, &type, &data, &size)) {
655 0 : return;
656 : }
657 0 : mCrypto.Update(reinterpret_cast<const uint8_t*>(data), size);
658 : }
659 :
660 : MediaResult
661 0 : MP4MetadataStagefright::ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID)
662 : {
663 0 : size_t numTracks = mMetadataExtractor->countTracks();
664 0 : int32_t trackNumber = GetTrackNumber(aTrackID);
665 0 : if (trackNumber < 0) {
666 : return MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
667 0 : RESULT_DETAIL("Cannot find track id %d",
668 0 : int(aTrackID)));
669 : }
670 0 : sp<MediaSource> track = mMetadataExtractor->getTrack(trackNumber);
671 0 : if (!track.get()) {
672 : return MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
673 0 : RESULT_DETAIL("Cannot access track id %d",
674 0 : int(aTrackID)));
675 : }
676 0 : sp<MetaData> metadata = mMetadataExtractor->getTrackMetaData(trackNumber);
677 : int64_t mediaTime;
678 0 : if (!metadata->findInt64(kKeyMediaTime, &mediaTime)) {
679 0 : mediaTime = 0;
680 : }
681 :
682 0 : return ConvertIndex(aDest, track->exportIndex(), mediaTime);
683 : }
684 :
685 : int32_t
686 0 : MP4MetadataStagefright::GetTrackNumber(mozilla::TrackID aTrackID)
687 : {
688 0 : size_t numTracks = mMetadataExtractor->countTracks();
689 0 : for (size_t i = 0; i < numTracks; i++) {
690 0 : sp<MetaData> metaData = mMetadataExtractor->getTrackMetaData(i);
691 0 : if (!metaData.get()) {
692 0 : continue;
693 : }
694 : int32_t value;
695 0 : if (metaData->findInt32(kKeyTrackID, &value) && value == aTrackID) {
696 0 : return i;
697 : }
698 : }
699 0 : return -1;
700 : }
701 :
702 : /*static*/ MP4Metadata::ResultAndByteBuffer
703 0 : MP4MetadataStagefright::Metadata(Stream* aSource)
704 : {
705 0 : auto parser = mozilla::MakeUnique<MoofParser>(aSource, 0, false);
706 0 : RefPtr<mozilla::MediaByteBuffer> buffer = parser->Metadata();
707 0 : if (!buffer) {
708 0 : return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
709 0 : RESULT_DETAIL("Cannot parse metadata")),
710 0 : nullptr};
711 : }
712 0 : return {NS_OK, Move(buffer)};
713 : }
714 :
715 : bool
716 0 : RustStreamAdaptor::Read(uint8_t* buffer, uintptr_t size, size_t* bytes_read)
717 : {
718 0 : if (!mOffset.isValid()) {
719 0 : MOZ_LOG(sLog, LogLevel::Error, ("Overflow in source stream offset"));
720 0 : return false;
721 : }
722 0 : bool rv = mSource->ReadAt(mOffset.value(), buffer, size, bytes_read);
723 0 : if (rv) {
724 0 : mOffset += *bytes_read;
725 : }
726 0 : return rv;
727 : }
728 :
729 : // Wrapper to allow rust to call our read adaptor.
730 : static intptr_t
731 0 : read_source(uint8_t* buffer, uintptr_t size, void* userdata)
732 : {
733 0 : MOZ_ASSERT(buffer);
734 0 : MOZ_ASSERT(userdata);
735 :
736 0 : auto source = reinterpret_cast<RustStreamAdaptor*>(userdata);
737 0 : size_t bytes_read = 0;
738 0 : bool rv = source->Read(buffer, size, &bytes_read);
739 0 : if (!rv) {
740 0 : MOZ_LOG(sLog, LogLevel::Warning, ("Error reading source data"));
741 0 : return -1;
742 : }
743 0 : return bytes_read;
744 : }
745 :
746 0 : MP4MetadataRust::MP4MetadataRust(Stream* aSource)
747 : : mSource(aSource)
748 0 : , mRustSource(aSource)
749 : {
750 0 : }
751 :
752 0 : MP4MetadataRust::~MP4MetadataRust()
753 : {
754 0 : }
755 :
756 : bool
757 0 : MP4MetadataRust::Init()
758 : {
759 0 : mp4parse_io io = { read_source, &mRustSource };
760 0 : mRustParser.reset(mp4parse_new(&io));
761 0 : MOZ_ASSERT(mRustParser);
762 :
763 0 : if (MOZ_LOG_TEST(sLog, LogLevel::Debug)) {
764 0 : mp4parse_log(true);
765 : }
766 :
767 0 : mp4parse_status rv = mp4parse_read(mRustParser.get());
768 0 : MOZ_LOG(sLog, LogLevel::Debug, ("rust parser returned %d\n", rv));
769 0 : Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_SUCCESS,
770 0 : rv == mp4parse_status_OK);
771 0 : if (rv != mp4parse_status_OK) {
772 0 : MOZ_LOG(sLog, LogLevel::Info, ("Rust mp4 parser fails to parse this stream."));
773 0 : MOZ_ASSERT(rv > 0);
774 0 : Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_ERROR_CODE, rv);
775 0 : return false;
776 : }
777 :
778 0 : UpdateCrypto();
779 :
780 0 : return true;
781 : }
782 :
783 : void
784 0 : MP4MetadataRust::UpdateCrypto()
785 : {
786 0 : mp4parse_pssh_info info = {};
787 0 : if (mp4parse_get_pssh_info(mRustParser.get(), &info) != mp4parse_status_OK) {
788 0 : return;
789 : }
790 :
791 0 : if (info.data.length == 0) {
792 0 : return;
793 : }
794 :
795 0 : mCrypto.Update(info.data.data, info.data.length);
796 : }
797 :
798 : bool
799 0 : TrackTypeEqual(TrackInfo::TrackType aLHS, mp4parse_track_type aRHS)
800 : {
801 0 : switch (aLHS) {
802 : case TrackInfo::kAudioTrack:
803 0 : return aRHS == mp4parse_track_type_AUDIO;
804 : case TrackInfo::kVideoTrack:
805 0 : return aRHS == mp4parse_track_type_VIDEO;
806 : default:
807 0 : return false;
808 : }
809 : }
810 :
811 : MP4Metadata::ResultAndTrackCount
812 0 : MP4MetadataRust::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
813 : {
814 : uint32_t tracks;
815 0 : auto rv = mp4parse_get_track_count(mRustParser.get(), &tracks);
816 0 : if (rv != mp4parse_status_OK) {
817 0 : MOZ_LOG(sLog, LogLevel::Warning,
818 : ("rust parser error %d counting tracks", rv));
819 0 : return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
820 0 : RESULT_DETAIL("Rust parser error %d", rv)),
821 0 : MP4Metadata::NumberTracksError()};
822 : }
823 0 : MOZ_LOG(sLog, LogLevel::Info, ("rust parser found %u tracks", tracks));
824 :
825 0 : uint32_t total = 0;
826 0 : for (uint32_t i = 0; i < tracks; ++i) {
827 : mp4parse_track_info track_info;
828 0 : rv = mp4parse_get_track_info(mRustParser.get(), i, &track_info);
829 0 : if (rv != mp4parse_status_OK) {
830 0 : continue;
831 : }
832 : // JPEG 'video' decoder is not supported in media stack yet.
833 0 : if (track_info.codec == mp4parse_codec::mp4parse_codec_JPEG ||
834 0 : track_info.codec == mp4parse_codec::mp4parse_codec_UNKNOWN) {
835 0 : continue;
836 : }
837 0 : if (TrackTypeEqual(aType, track_info.track_type)) {
838 0 : total += 1;
839 : }
840 : }
841 :
842 0 : return {NS_OK, total};
843 : }
844 :
845 : Maybe<uint32_t>
846 0 : MP4MetadataRust::TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const
847 : {
848 : uint32_t tracks;
849 0 : auto rv = mp4parse_get_track_count(mRustParser.get(), &tracks);
850 0 : if (rv != mp4parse_status_OK) {
851 0 : return Nothing();
852 : }
853 :
854 : /* The MP4Metadata API uses a per-TrackType index of tracks, but mp4parse
855 : (and libstagefright) use a global track index. Convert the index by
856 : counting the tracks of the requested type and returning the global
857 : track index when a match is found. */
858 0 : uint32_t perType = 0;
859 0 : for (uint32_t i = 0; i < tracks; ++i) {
860 : mp4parse_track_info track_info;
861 0 : rv = mp4parse_get_track_info(mRustParser.get(), i, &track_info);
862 0 : if (rv != mp4parse_status_OK) {
863 0 : continue;
864 : }
865 0 : if (TrackTypeEqual(aType, track_info.track_type)) {
866 0 : if (perType == aTrackNumber) {
867 0 : return Some(i);
868 : }
869 0 : perType += 1;
870 : }
871 : }
872 :
873 0 : return Nothing();
874 : }
875 :
876 : MP4Metadata::ResultAndTrackInfo
877 0 : MP4MetadataRust::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
878 : size_t aTrackNumber) const
879 : {
880 0 : Maybe<uint32_t> trackIndex = TrackTypeToGlobalTrackIndex(aType, aTrackNumber);
881 0 : if (trackIndex.isNothing()) {
882 0 : return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
883 0 : RESULT_DETAIL("No %s tracks",
884 : TrackTypeToStr(aType))),
885 0 : nullptr};
886 : }
887 :
888 : mp4parse_track_info info;
889 0 : auto rv = mp4parse_get_track_info(mRustParser.get(), trackIndex.value(), &info);
890 0 : if (rv != mp4parse_status_OK) {
891 0 : MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_info returned %d", rv));
892 0 : return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
893 0 : RESULT_DETAIL("Cannot find %s track #%zu",
894 : TrackTypeToStr(aType),
895 : aTrackNumber)),
896 0 : nullptr};
897 : }
898 : #ifdef DEBUG
899 0 : const char* codec_string = "unrecognized";
900 0 : switch (info.codec) {
901 0 : case mp4parse_codec_UNKNOWN: codec_string = "unknown"; break;
902 0 : case mp4parse_codec_AAC: codec_string = "aac"; break;
903 0 : case mp4parse_codec_OPUS: codec_string = "opus"; break;
904 0 : case mp4parse_codec_FLAC: codec_string = "flac"; break;
905 0 : case mp4parse_codec_AVC: codec_string = "h.264"; break;
906 0 : case mp4parse_codec_VP9: codec_string = "vp9"; break;
907 0 : case mp4parse_codec_MP3: codec_string = "mp3"; break;
908 0 : case mp4parse_codec_MP4V: codec_string = "mp4v"; break;
909 0 : case mp4parse_codec_JPEG: codec_string = "jpeg"; break;
910 0 : case mp4parse_codec_AC3: codec_string = "ac-3"; break;
911 0 : case mp4parse_codec_EC3: codec_string = "ec-3"; break;
912 : }
913 0 : MOZ_LOG(sLog, LogLevel::Debug, ("track codec %s (%u)\n",
914 : codec_string, info.codec));
915 : #endif
916 :
917 : // This specialization interface is crazy.
918 0 : UniquePtr<mozilla::TrackInfo> e;
919 0 : switch (aType) {
920 : case TrackInfo::TrackType::kAudioTrack: {
921 : mp4parse_track_audio_info audio;
922 0 : auto rv = mp4parse_get_track_audio_info(mRustParser.get(), trackIndex.value(), &audio);
923 0 : if (rv != mp4parse_status_OK) {
924 0 : MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_audio_info returned error %d", rv));
925 0 : return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
926 0 : RESULT_DETAIL("Cannot parse %s track #%zu",
927 : TrackTypeToStr(aType),
928 : aTrackNumber)),
929 0 : nullptr};
930 : }
931 0 : auto track = mozilla::MakeUnique<MP4AudioInfo>();
932 0 : track->Update(&info, &audio);
933 0 : e = Move(track);
934 : }
935 0 : break;
936 : case TrackInfo::TrackType::kVideoTrack: {
937 : mp4parse_track_video_info video;
938 0 : auto rv = mp4parse_get_track_video_info(mRustParser.get(), trackIndex.value(), &video);
939 0 : if (rv != mp4parse_status_OK) {
940 0 : MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_video_info returned error %d", rv));
941 0 : return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
942 0 : RESULT_DETAIL("Cannot parse %s track #%zu",
943 : TrackTypeToStr(aType),
944 : aTrackNumber)),
945 0 : nullptr};
946 : }
947 0 : auto track = mozilla::MakeUnique<MP4VideoInfo>();
948 0 : track->Update(&info, &video);
949 0 : e = Move(track);
950 : }
951 0 : break;
952 : default:
953 0 : MOZ_LOG(sLog, LogLevel::Warning, ("unhandled track type %d", aType));
954 0 : return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
955 0 : RESULT_DETAIL("Cannot handle %s track #%zu",
956 : TrackTypeToStr(aType),
957 : aTrackNumber)),
958 0 : nullptr};
959 : }
960 :
961 : // No duration in track, use fragment_duration.
962 0 : if (e && !e->mDuration.IsPositive()) {
963 : mp4parse_fragment_info info;
964 0 : auto rv = mp4parse_get_fragment_info(mRustParser.get(), &info);
965 0 : if (rv == mp4parse_status_OK) {
966 0 : e->mDuration = TimeUnit::FromMicroseconds(info.fragment_duration);
967 : }
968 : }
969 :
970 0 : if (e && e->IsValid()) {
971 0 : return {NS_OK, Move(e)};
972 : }
973 0 : MOZ_LOG(sLog, LogLevel::Debug, ("TrackInfo didn't validate"));
974 :
975 0 : return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
976 0 : RESULT_DETAIL("Invalid %s track #%zu",
977 : TrackTypeToStr(aType),
978 : aTrackNumber)),
979 0 : nullptr};
980 : }
981 :
982 : bool
983 0 : MP4MetadataRust::CanSeek() const
984 : {
985 0 : MOZ_ASSERT(false, "Not yet implemented");
986 : return false;
987 : }
988 :
989 : MP4Metadata::ResultAndCryptoFile
990 0 : MP4MetadataRust::Crypto() const
991 : {
992 0 : return {NS_OK, &mCrypto};
993 : }
994 :
995 : MediaResult
996 0 : MP4MetadataRust::ReadTrackIndice(mp4parse_byte_data* aIndices, mozilla::TrackID aTrackID)
997 : {
998 0 : uint8_t fragmented = false;
999 0 : auto rv = mp4parse_is_fragmented(mRustParser.get(), aTrackID, &fragmented);
1000 0 : if (rv != mp4parse_status_OK) {
1001 : return MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
1002 0 : RESULT_DETAIL("Cannot parse whether track id %d is "
1003 : "fragmented, mp4parse_error=%d",
1004 0 : int(aTrackID), int(rv)));
1005 : }
1006 :
1007 0 : if (fragmented) {
1008 0 : return NS_OK;
1009 : }
1010 :
1011 0 : rv = mp4parse_get_indice_table(mRustParser.get(), aTrackID, aIndices);
1012 0 : if (rv != mp4parse_status_OK) {
1013 : return MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
1014 0 : RESULT_DETAIL("Cannot parse index table in track id %d, "
1015 : "mp4parse_error=%d",
1016 0 : int(aTrackID), int(rv)));
1017 : }
1018 :
1019 0 : return NS_OK;
1020 : }
1021 :
1022 : /*static*/ MP4Metadata::ResultAndByteBuffer
1023 0 : MP4MetadataRust::Metadata(Stream* aSource)
1024 : {
1025 0 : MOZ_ASSERT(false, "Not yet implemented");
1026 : return {NS_ERROR_NOT_IMPLEMENTED, nullptr};
1027 : }
1028 :
1029 : } // namespace mp4_demuxer
|