NS_IMETHODIMP MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize) { int64_t video = 0, audio = 0; // NB: When resourceSizes' ref count goes to 0 the promise will report the // resources memory and finish the asynchronous memory report. RefPtr<MediaDecoder::ResourceSizes> resourceSizes = new MediaDecoder::ResourceSizes(MediaMemoryTracker::MallocSizeOf); nsCOMPtr<nsIHandleReportCallback> handleReport = aHandleReport; nsCOMPtr<nsISupports> data = aData; resourceSizes->Promise()->Then( AbstractThread::MainThread(), __func__, [handleReport, data] (size_t size) { handleReport->Callback( EmptyCString(), NS_LITERAL_CSTRING("explicit/media/resources"), KIND_HEAP, UNITS_BYTES, size, NS_LITERAL_CSTRING("Memory used by media resources including " "streaming buffers, caches, etc."), data); nsCOMPtr<nsIMemoryReporterManager> imgr = do_GetService("@mozilla.org/memory-reporter-manager;1"); if (imgr) { imgr->EndReport(); } }, [] (size_t) { /* unused reject function */ }); DecodersArray& decoders = Decoders(); for (size_t i = 0; i < decoders.Length(); ++i) { MediaDecoder* decoder = decoders[i]; video += decoder->SizeOfVideoQueue(); audio += decoder->SizeOfAudioQueue(); decoder->AddSizeOfResources(resourceSizes); } #define REPORT(_path, _amount, _desc) \ do { \ nsresult rv; \ rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \ KIND_HEAP, UNITS_BYTES, _amount, \ NS_LITERAL_CSTRING(_desc), aData); \ NS_ENSURE_SUCCESS(rv, rv); \ } while (0) REPORT("explicit/media/decoded/video", video, "Memory used by decoded video frames."); REPORT("explicit/media/decoded/audio", audio, "Memory used by decoded audio chunks."); #undef REPORT return NS_OK; }
void MediaDecoder::DormantTimerExpired(nsITimer* aTimer, void* aClosure) { MOZ_ASSERT(aClosure); MediaDecoder* decoder = static_cast<MediaDecoder*>(aClosure); ReentrantMonitorAutoEnter mon(decoder->GetReentrantMonitor()); decoder->UpdateDormantState(true /* aDormantTimeout */, false /* aActivity */); }
void MediaDecoder::DormantTimerExpired(nsITimer* aTimer, void* aClosure) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aClosure); MediaDecoder* decoder = static_cast<MediaDecoder*>(aClosure); decoder->UpdateDormantState(true /* aDormantTimeout */, false /* aActivity */); }
NS_IMETHODIMP MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize) { // NB: When resourceSizes' ref count goes to 0 the promise will report the // resources memory and finish the asynchronous memory report. RefPtr<MediaDecoder::ResourceSizes> resourceSizes = new MediaDecoder::ResourceSizes(MediaMemoryTracker::MallocSizeOf); nsCOMPtr<nsIHandleReportCallback> handleReport = aHandleReport; nsCOMPtr<nsISupports> data = aData; resourceSizes->Promise()->Then( // Don't use SystemGroup::AbstractMainThreadFor() for // handleReport->Callback() will run scripts. AbstractThread::MainThread(), __func__, [handleReport, data] (size_t size) { handleReport->Callback( EmptyCString(), NS_LITERAL_CSTRING("explicit/media/resources"), KIND_HEAP, UNITS_BYTES, size, NS_LITERAL_CSTRING("Memory used by media resources including " "streaming buffers, caches, etc."), data); nsCOMPtr<nsIMemoryReporterManager> imgr = do_GetService("@mozilla.org/memory-reporter-manager;1"); if (imgr) { imgr->EndReport(); } }, [] (size_t) { /* unused reject function */ }); int64_t video = 0; int64_t audio = 0; DecodersArray& decoders = Decoders(); for (size_t i = 0; i < decoders.Length(); ++i) { MediaDecoder* decoder = decoders[i]; video += decoder->SizeOfVideoQueue(); audio += decoder->SizeOfAudioQueue(); decoder->AddSizeOfResources(resourceSizes); } MOZ_COLLECT_REPORT( "explicit/media/decoded/video", KIND_HEAP, UNITS_BYTES, video, "Memory used by decoded video frames."); MOZ_COLLECT_REPORT( "explicit/media/decoded/audio", KIND_HEAP, UNITS_BYTES, audio, "Memory used by decoded audio chunks."); return NS_OK; }
NS_IMETHODIMP MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize) { int64_t video = 0, audio = 0; size_t resources = 0; DecodersArray& decoders = Decoders(); for (size_t i = 0; i < decoders.Length(); ++i) { MediaDecoder* decoder = decoders[i]; video += decoder->SizeOfVideoQueue(); audio += decoder->SizeOfAudioQueue(); if (decoder->GetResource()) { resources += decoder->GetResource()->SizeOfIncludingThis(MallocSizeOf); } } #define REPORT(_path, _amount, _desc) \ do { \ nsresult rv; \ rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \ KIND_HEAP, UNITS_BYTES, _amount, \ NS_LITERAL_CSTRING(_desc), aData); \ NS_ENSURE_SUCCESS(rv, rv); \ } while (0) REPORT("explicit/media/decoded/video", video, "Memory used by decoded video frames."); REPORT("explicit/media/decoded/audio", audio, "Memory used by decoded audio chunks."); REPORT("explicit/media/resources", resources, "Memory used by media resources including streaming buffers, caches, " "etc."); #undef REPORT return NS_OK; }
static void ProgressCallback(nsITimer* aTimer, void* aClosure) { MediaDecoder* decoder = static_cast<MediaDecoder*>(aClosure); decoder->Progress(true); }
M4Err MediaCodec_Process(GenericCodec *codec, u32 TimeAvailable) { LPAUBUFFER AU; Channel *ch; char *cu_buf; u32 cu_buf_size, mmlevel, deltaTS; u32 first, entryTime, now, obj_time; MediaDecoder *mdec = (MediaDecoder*)codec->decio; M4Err e = M4OK; /*if video codec muted don't decode if audio codec muted we dispatch to keep sync in place*/ if (codec->Muted && (codec->type==M4ST_VISUAL) ) return M4OK; entryTime = Term_GetTime(codec->odm->term); /*fetch next AU in DTS order for this codec*/ Decoder_GetNextAU(codec, &ch, &AU); /*no active channel return*/ if (!AU || !ch) { /*if the codec is in EOS state, assume we're done*/ if (codec->Status == CODEC_EOS) { /*if codec is reordering, try to flush it*/ if (codec->is_reordering) { if ( LockCompositionUnit(codec, codec->last_unit_cts+1, &cu_buf, &cu_buf_size) == M4OutOfMem) return M4OK; e = mdec->ProcessData(mdec, NULL, 0, 0, cu_buf, &cu_buf_size, 0, 0); if (e==M4OK) e = UnlockCompositionUnit(codec, codec->last_unit_cts+1, cu_buf_size); } MM_StopCodec(codec); if (codec->CB) CB_SetEndOfStream(codec->CB); } /*if no data, and channel not buffering, ABORT CB buffer (data timeout or EOS not detectable)*/ else if (ch && !ch->BufferOn) CB_AbortBuffering(codec->CB); return M4OK; } /*get the object time*/ obj_time = CK_GetTime(codec->ck); /*Media Time for media codecs is updated in the CB*/ if (!codec->CB) { Channel_DropAU(ch); return M4BadParam; } /*try to refill the full buffer*/ first = 1; while (codec->CB->Capacity > codec->CB->UnitCount) { /*set media processing level*/ mmlevel = MM_LEVEL_NORMAL; /*SEEK: if the last frame had the same TS, we are seeking. Ask the codec to drop*/ if (!ch->skip_sl && codec->last_unit_cts && (codec->last_unit_cts == AU->CTS) && !ch->esd->dependsOnESID) { mmlevel = MM_LEVEL_SEEK; } /*only perform drop in normal playback*/ else if (codec->CB->Status == CB_PLAY) { if (!ch->skip_sl && (AU->CTS < obj_time)) { /*extremely late, even if we decode the renderer will drop the frame so set the level to drop*/ mmlevel = MM_LEVEL_DROP; } /*we are late according to the media manager*/ else if (codec->PriorityBoost) { mmlevel = MM_LEVEL_VERY_LATE; } /*otherwise we must have an idea of the load in order to set the right level use the composition buffer for that, only on the first frame*/ else if (first) { //if the CB is almost empty set to very late if (codec->CB->UnitCount <= codec->CB->Min+1) { mmlevel = MM_LEVEL_VERY_LATE; } else if (codec->CB->UnitCount * 2 <= codec->CB->Capacity) { mmlevel = MM_LEVEL_LATE; } first = 0; } } /*when using temporal scalability make sure we can decode*/ if (ch->esd->dependsOnESID && (codec->last_unit_dts > AU->DTS)){ // printf("SCALABLE STREAM DEAD!!\n"); goto drop; } if (ch->skip_sl) { if (codec->bytes_per_sec) { AU->CTS = codec->last_unit_cts + ch->ts_offset + codec->cur_audio_bytes * 1000 / codec->bytes_per_sec; } else if (codec->fps) { AU->CTS = codec->last_unit_cts + ch->ts_offset + (u32) (codec->cur_video_frames * 1000 / codec->fps); } } if ( LockCompositionUnit(codec, AU->CTS, &cu_buf, &cu_buf_size) == M4OutOfMem) return M4OK; now = Term_GetTime(codec->odm->term); e = mdec->ProcessData(mdec, AU->data, AU->dataLength, ch->esd->ESID, cu_buf, &cu_buf_size, AU->PaddingBits, mmlevel); now = Term_GetTime(codec->odm->term) - now; /*input is too small, resize composition memory*/ switch (e) { case M4BufferTooSmall: /*release but no dispatch*/ UnlockCompositionUnit(codec, AU->CTS, 0); if (ResizeCompositionBuffer(codec, cu_buf_size)==M4OK) continue; break; case M4OK: /*in seek don't dispatch any output*/ if (mmlevel == MM_LEVEL_SEEK) cu_buf_size = 0; e = UnlockCompositionUnit(codec, AU->CTS, cu_buf_size); codec_update_stats(codec, AU->dataLength, now); if (ch->skip_sl) { if (codec->bytes_per_sec) { codec->cur_audio_bytes += cu_buf_size; while (codec->cur_audio_bytes>codec->bytes_per_sec) { codec->cur_audio_bytes -= codec->bytes_per_sec; codec->last_unit_cts += 1000; } } else if (codec->fps && cu_buf_size) { codec->cur_video_frames += 1; } } break; /*this happens a lot when using non-MPEG-4 streams (ex: ffmpeg demuxer)*/ case M4PackedFrames: /*in seek don't dispatch any output*/ if (mmlevel == MM_LEVEL_SEEK) cu_buf_size = 0; e = UnlockCompositionUnit(codec, AU->CTS, cu_buf_size); if (ch->skip_sl) { if (codec->bytes_per_sec) { codec->cur_audio_bytes += cu_buf_size; } else if (codec->fps && cu_buf_size) { codec->cur_video_frames += 1; } } else { if (codec->bytes_per_sec) { deltaTS = cu_buf_size * 1000 / codec->bytes_per_sec; } else if (codec->fps && cu_buf_size) { deltaTS = (u32) (1000.0f / codec->fps); } AU->DTS += deltaTS; AU->CTS += deltaTS; } codec_update_stats(codec, 0, now); continue; default: UnlockCompositionUnit(codec, AU->CTS, 0); /*error - if the object is in intitial buffering resume it!!*/ CB_AbortBuffering(codec->CB); break; } codec->last_unit_dts = AU->DTS; /*remember base layer timing*/ if (!ch->esd->dependsOnESID && !ch->skip_sl) codec->last_unit_cts = AU->CTS; drop: Channel_DropAU(ch); if (e) return e; /*escape from decoding loop only if above critical limit - this is to avoid starvation on audio*/ if (codec->CB->UnitCount > codec->CB->Min) { now = Term_GetTime(codec->odm->term); if (now - entryTime >= TimeAvailable - TIME_CHECK) { return M4OK; } } Decoder_GetNextAU(codec, &ch, &AU); if (!ch || !AU) return M4OK; } return M4OK; }