2016-04-19 10:36:19 +03:00
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2.0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/. */
# include "DecoderDoctorDiagnostics.h"
2016-04-19 10:36:19 +03:00
# include "mozilla/dom/DecoderDoctorNotificationBinding.h"
2016-04-19 10:36:19 +03:00
# include "mozilla/Logging.h"
2016-04-22 06:42:11 +03:00
# include "mozilla/Preferences.h"
2016-04-27 14:03:44 +03:00
# include "nsContentUtils.h"
2016-04-19 10:36:19 +03:00
# include "nsGkAtoms.h"
# include "nsIDocument.h"
2016-04-19 10:36:19 +03:00
# include "nsIObserverService.h"
2016-04-27 14:03:44 +03:00
# include "nsIScriptError.h"
2016-04-19 10:36:19 +03:00
# include "nsITimer.h"
# include "nsIWeakReference.h"
2016-04-22 06:42:11 +03:00
# include "nsPluginHost.h"
2016-09-19 05:50:01 +03:00
# include "nsPrintfCString.h"
2016-05-26 12:24:35 +03:00
# include "VideoUtils.h"
2016-04-19 10:36:19 +03:00
2016-09-22 10:06:45 +03:00
# if defined(MOZ_FFMPEG)
# include "FFmpegRuntimeLinker.h"
# endif
2016-04-19 10:36:19 +03:00
static mozilla : : LazyLogModule sDecoderDoctorLog ( " DecoderDoctor " ) ;
# define DD_LOG(level, arg, ...) MOZ_LOG(sDecoderDoctorLog, level, (arg, ##__VA_ARGS__))
# define DD_DEBUG(arg, ...) DD_LOG(mozilla::LogLevel::Debug, arg, ##__VA_ARGS__)
2016-04-19 10:36:19 +03:00
# define DD_INFO(arg, ...) DD_LOG(mozilla::LogLevel::Info, arg, ##__VA_ARGS__)
2016-04-19 10:36:19 +03:00
# define DD_WARN(arg, ...) DD_LOG(mozilla::LogLevel::Warning, arg, ##__VA_ARGS__)
2016-05-16 14:35:14 +03:00
namespace mozilla {
2016-04-19 10:36:19 +03:00
2016-04-19 10:36:19 +03:00
// Class that collects a sequence of diagnostics from the same document over a
// small period of time, in order to provide a synthesized analysis.
//
// Referenced by the document through a nsINode property, mTimer, and
// inter-task captures.
// When notified that the document is dead, or when the timer expires but
// nothing new happened, StopWatching() will remove the document property and
// timer (if present), so no more work will happen and the watcher will be
// destroyed once all references are gone.
2017-02-14 02:45:23 +03:00
class DecoderDoctorDocumentWatcher : public nsITimerCallback , public nsINamed
2016-04-19 10:36:19 +03:00
{
public :
2016-04-22 06:42:11 +03:00
static already_AddRefed < DecoderDoctorDocumentWatcher >
2016-04-19 10:36:19 +03:00
RetrieveOrCreate ( nsIDocument * aDocument ) ;
NS_DECL_ISUPPORTS
NS_DECL_NSITIMERCALLBACK
2017-02-14 02:45:23 +03:00
NS_DECL_NSINAMED
2016-04-19 10:36:19 +03:00
2016-04-22 06:42:11 +03:00
void AddDiagnostics ( DecoderDoctorDiagnostics & & aDiagnostics ,
const char * aCallSite ) ;
2016-04-19 10:36:19 +03:00
private :
explicit DecoderDoctorDocumentWatcher ( nsIDocument * aDocument ) ;
virtual ~ DecoderDoctorDocumentWatcher ( ) ;
// This will prevent further work from happening, watcher will deregister
// itself from document (if requested) and cancel any timer, and soon die.
void StopWatching ( bool aRemoveProperty ) ;
// Remove property from document; will call DestroyPropertyCallback.
void RemovePropertyFromDocument ( ) ;
// Callback for property destructor, will be automatically called when the
// document (in aObject) is being destroyed.
static void DestroyPropertyCallback ( void * aObject ,
nsIAtom * aPropertyName ,
void * aPropertyValue ,
void * aData ) ;
static const uint32_t sAnalysisPeriod_ms = 1000 ;
void EnsureTimerIsStarted ( ) ;
void SynthesizeAnalysis ( ) ;
// Raw pointer to an nsIDocument.
// Must be non-null during construction.
// Nulled when we want to stop watching, because either:
// 1. The document has been destroyed (notified through
// DestroyPropertyCallback).
// 2. We have not received new diagnostic information within a short time
// period, so we just stop watching.
// Once nulled, no more actual work will happen, and the watcher will be
// destroyed soon.
nsIDocument * mDocument ;
struct Diagnostics
{
Diagnostics ( DecoderDoctorDiagnostics & & aDiagnostics ,
const char * aCallSite )
: mDecoderDoctorDiagnostics ( Move ( aDiagnostics ) )
, mCallSite ( aCallSite )
{ }
Diagnostics ( const Diagnostics & ) = delete ;
Diagnostics ( Diagnostics & & aOther )
: mDecoderDoctorDiagnostics ( Move ( aOther . mDecoderDoctorDiagnostics ) )
, mCallSite ( Move ( aOther . mCallSite ) )
{ }
const DecoderDoctorDiagnostics mDecoderDoctorDiagnostics ;
const nsCString mCallSite ;
} ;
typedef nsTArray < Diagnostics > DiagnosticsSequence ;
DiagnosticsSequence mDiagnosticsSequence ;
nsCOMPtr < nsITimer > mTimer ; // Keep timer alive until we run.
DiagnosticsSequence : : size_type mDiagnosticsHandled = 0 ;
} ;
2017-02-14 02:45:23 +03:00
NS_IMPL_ISUPPORTS ( DecoderDoctorDocumentWatcher , nsITimerCallback , nsINamed )
2016-04-19 10:36:19 +03:00
// static
2016-04-22 06:42:11 +03:00
already_AddRefed < DecoderDoctorDocumentWatcher >
2016-04-19 10:36:19 +03:00
DecoderDoctorDocumentWatcher : : RetrieveOrCreate ( nsIDocument * aDocument )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
MOZ_ASSERT ( aDocument ) ;
RefPtr < DecoderDoctorDocumentWatcher > watcher =
static_cast < DecoderDoctorDocumentWatcher * > (
aDocument - > GetProperty ( nsGkAtoms : : decoderDoctor ) ) ;
if ( ! watcher ) {
watcher = new DecoderDoctorDocumentWatcher ( aDocument ) ;
if ( NS_WARN_IF ( NS_FAILED (
aDocument - > SetProperty ( nsGkAtoms : : decoderDoctor ,
watcher . get ( ) ,
DestroyPropertyCallback ,
/*transfer*/ false ) ) ) ) {
DD_WARN ( " DecoderDoctorDocumentWatcher::RetrieveOrCreate(doc=%p) - Could not set property in document, will destroy new watcher[%p] " ,
aDocument , watcher . get ( ) ) ;
return nullptr ;
}
// Document owns watcher through this property.
// Released in DestroyPropertyCallback().
NS_ADDREF ( watcher . get ( ) ) ;
}
2016-04-22 06:42:11 +03:00
return watcher . forget ( ) ;
2016-04-19 10:36:19 +03:00
}
DecoderDoctorDocumentWatcher : : DecoderDoctorDocumentWatcher ( nsIDocument * aDocument )
: mDocument ( aDocument )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
MOZ_ASSERT ( mDocument ) ;
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p]::DecoderDoctorDocumentWatcher(doc=%p) " ,
this , mDocument ) ;
}
DecoderDoctorDocumentWatcher : : ~ DecoderDoctorDocumentWatcher ( )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p <- expect 0]::~DecoderDoctorDocumentWatcher() " ,
this , mDocument ) ;
// mDocument should have been reset through StopWatching()!
MOZ_ASSERT ( ! mDocument ) ;
}
void
DecoderDoctorDocumentWatcher : : RemovePropertyFromDocument ( )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
DecoderDoctorDocumentWatcher * watcher =
static_cast < DecoderDoctorDocumentWatcher * > (
mDocument - > GetProperty ( nsGkAtoms : : decoderDoctor ) ) ;
if ( ! watcher ) {
return ;
}
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::RemovePropertyFromDocument() \n " ,
watcher , watcher - > mDocument ) ;
// This will remove the property and call our DestroyPropertyCallback.
mDocument - > DeleteProperty ( nsGkAtoms : : decoderDoctor ) ;
}
// Callback for property destructors. |aObject| is the object
// the property is being removed for, |aPropertyName| is the property
// being removed, |aPropertyValue| is the value of the property, and |aData|
// is the opaque destructor data that was passed to SetProperty().
// static
void
DecoderDoctorDocumentWatcher : : DestroyPropertyCallback ( void * aObject ,
nsIAtom * aPropertyName ,
void * aPropertyValue ,
void * )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
MOZ_ASSERT ( aPropertyName = = nsGkAtoms : : decoderDoctor ) ;
DecoderDoctorDocumentWatcher * watcher =
static_cast < DecoderDoctorDocumentWatcher * > ( aPropertyValue ) ;
MOZ_ASSERT ( watcher ) ;
2016-09-30 13:00:49 +03:00
# ifdef DEBUG
nsIDocument * document = static_cast < nsIDocument * > ( aObject ) ;
2016-04-19 10:36:19 +03:00
MOZ_ASSERT ( watcher - > mDocument = = document ) ;
2016-09-30 13:00:49 +03:00
# endif
2016-04-19 10:36:19 +03:00
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::DestroyPropertyCallback() \n " ,
watcher , watcher - > mDocument ) ;
// 'false': StopWatching should not try and remove the property.
watcher - > StopWatching ( false ) ;
NS_RELEASE ( watcher ) ;
}
void
DecoderDoctorDocumentWatcher : : StopWatching ( bool aRemoveProperty )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
// StopWatching() shouldn't be called twice.
MOZ_ASSERT ( mDocument ) ;
if ( aRemoveProperty ) {
RemovePropertyFromDocument ( ) ;
}
// Forget document now, this will prevent more work from being started.
mDocument = nullptr ;
if ( mTimer ) {
mTimer - > Cancel ( ) ;
mTimer = nullptr ;
}
}
void
DecoderDoctorDocumentWatcher : : EnsureTimerIsStarted ( )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
if ( ! mTimer ) {
mTimer = do_CreateInstance ( NS_TIMER_CONTRACTID ) ;
if ( NS_WARN_IF ( ! mTimer ) ) {
return ;
}
if ( NS_WARN_IF ( NS_FAILED (
mTimer - > InitWithCallback (
this , sAnalysisPeriod_ms , nsITimer : : TYPE_ONE_SHOT ) ) ) ) {
mTimer = nullptr ;
}
}
}
2017-03-23 02:21:27 +03:00
enum class ReportParam : uint8_t
{
// Marks the end of the parameter list.
// Keep this zero! (For implicit zero-inits when used in definitions below.)
None = 0 ,
2017-03-24 09:09:41 +03:00
Formats ,
DecodeIssue ,
DocURL ,
ResourceURL
2017-03-23 02:21:27 +03:00
} ;
2017-03-22 07:37:41 +03:00
struct NotificationAndReportStringId
{
// Notification type, handled by browser-media.js.
dom : : DecoderDoctorNotificationType mNotificationType ;
// Console message id. Key in dom/locales/.../chrome/dom/dom.properties.
const char * mReportStringId ;
2017-03-23 02:21:27 +03:00
static const int maxReportParams = 4 ;
ReportParam mReportParams [ maxReportParams ] ;
2017-03-22 07:37:41 +03:00
} ;
2017-03-14 01:40:45 +03:00
// Note: ReportStringIds are limited to alphanumeric only.
2016-11-22 08:11:04 +03:00
static const NotificationAndReportStringId sMediaWidevineNoWMF =
2016-05-24 18:03:21 +03:00
{ dom : : DecoderDoctorNotificationType : : Platform_decoder_not_found ,
2017-03-23 02:21:27 +03:00
" MediaWidevineNoWMF " , { ReportParam : : None } } ;
2016-05-24 18:03:21 +03:00
static const NotificationAndReportStringId sMediaWMFNeeded =
{ dom : : DecoderDoctorNotificationType : : Platform_decoder_not_found ,
2017-03-23 02:21:27 +03:00
" MediaWMFNeeded " , { ReportParam : : Formats } } ;
2016-05-24 18:03:21 +03:00
static const NotificationAndReportStringId sMediaPlatformDecoderNotFound =
{ dom : : DecoderDoctorNotificationType : : Platform_decoder_not_found ,
2017-03-23 02:21:27 +03:00
" MediaPlatformDecoderNotFound " , { ReportParam : : Formats } } ;
2016-05-24 18:03:21 +03:00
static const NotificationAndReportStringId sMediaCannotPlayNoDecoders =
{ dom : : DecoderDoctorNotificationType : : Cannot_play ,
2017-03-23 02:21:27 +03:00
" MediaCannotPlayNoDecoders " , { ReportParam : : Formats } } ;
2016-05-24 18:03:21 +03:00
static const NotificationAndReportStringId sMediaNoDecoders =
{ dom : : DecoderDoctorNotificationType : : Can_play_but_some_missing_decoders ,
2017-03-23 02:21:27 +03:00
" MediaNoDecoders " , { ReportParam : : Formats } } ;
2016-09-19 05:41:36 +03:00
static const NotificationAndReportStringId sCannotInitializePulseAudio =
{ dom : : DecoderDoctorNotificationType : : Cannot_initialize_pulseaudio ,
2017-03-23 02:21:27 +03:00
" MediaCannotInitializePulseAudio " , { ReportParam : : None } } ;
2016-09-22 10:06:45 +03:00
static const NotificationAndReportStringId sUnsupportedLibavcodec =
{ dom : : DecoderDoctorNotificationType : : Unsupported_libavcodec ,
2017-03-23 02:21:27 +03:00
" MediaUnsupportedLibavcodec " , { ReportParam : : None } } ;
2017-03-24 09:09:41 +03:00
static const NotificationAndReportStringId sMediaDecodeError =
{ dom : : DecoderDoctorNotificationType : : Decode_error ,
" MediaDecodeError " ,
{ ReportParam : : ResourceURL , ReportParam : : DecodeIssue } } ;
static const NotificationAndReportStringId sMediaDecodeWarning =
{ dom : : DecoderDoctorNotificationType : : Decode_warning ,
" MediaDecodeWarning " ,
{ ReportParam : : ResourceURL , ReportParam : : DecodeIssue } } ;
2016-05-24 18:03:21 +03:00
2016-11-22 08:11:04 +03:00
static const NotificationAndReportStringId * const
2016-05-24 17:41:53 +03:00
sAllNotificationsAndReportStringIds [ ] =
{
2016-11-22 08:11:04 +03:00
& sMediaWidevineNoWMF ,
2016-05-24 17:41:53 +03:00
& sMediaWMFNeeded ,
& sMediaPlatformDecoderNotFound ,
& sMediaCannotPlayNoDecoders ,
2016-09-19 05:41:36 +03:00
& sMediaNoDecoders ,
2016-09-22 10:06:45 +03:00
& sCannotInitializePulseAudio ,
& sUnsupportedLibavcodec ,
2017-03-24 09:09:41 +03:00
& sMediaDecodeError ,
& sMediaDecodeWarning
2016-05-24 17:41:53 +03:00
} ;
2016-04-19 10:36:19 +03:00
static void
DispatchNotification ( nsISupports * aSubject ,
2016-05-24 18:03:21 +03:00
const NotificationAndReportStringId & aNotification ,
2016-05-26 16:26:10 +03:00
bool aIsSolved ,
2017-03-24 09:09:41 +03:00
const nsAString & aFormats ,
const nsAString & aDecodeIssue ,
const nsACString & aDocURL ,
const nsAString & aResourceURL )
2016-04-19 10:36:19 +03:00
{
if ( ! aSubject ) {
return ;
}
dom : : DecoderDoctorNotification data ;
2016-05-24 18:03:21 +03:00
data . mType = aNotification . mNotificationType ;
2016-05-26 16:26:10 +03:00
data . mIsSolved = aIsSolved ;
data . mDecoderDoctorReportId . Assign (
2016-05-24 18:03:21 +03:00
NS_ConvertUTF8toUTF16 ( aNotification . mReportStringId ) ) ;
2016-04-19 10:36:19 +03:00
if ( ! aFormats . IsEmpty ( ) ) {
data . mFormats . Construct ( aFormats ) ;
}
2017-03-24 09:09:41 +03:00
if ( ! aDecodeIssue . IsEmpty ( ) ) {
data . mDecodeIssue . Construct ( aDecodeIssue ) ;
}
if ( ! aDocURL . IsEmpty ( ) ) {
data . mDocURL . Construct ( NS_ConvertUTF8toUTF16 ( aDocURL ) ) ;
}
if ( ! aResourceURL . IsEmpty ( ) ) {
data . mResourceURL . Construct ( aResourceURL ) ;
}
2016-04-19 10:36:19 +03:00
nsAutoString json ;
data . ToJSON ( json ) ;
if ( json . IsEmpty ( ) ) {
DD_WARN ( " DecoderDoctorDiagnostics/DispatchEvent() - Could not create json for dispatch " ) ;
// No point in dispatching this notification without data, the front-end
// wouldn't know what to display.
return ;
}
DD_DEBUG ( " DecoderDoctorDiagnostics/DispatchEvent() %s " , NS_ConvertUTF16toUTF8 ( json ) . get ( ) ) ;
nsCOMPtr < nsIObserverService > obs = services : : GetObserverService ( ) ;
if ( obs ) {
obs - > NotifyObservers ( aSubject , " decoder-doctor-notification " , json . get ( ) ) ;
}
}
2016-09-08 05:22:25 +03:00
static void
ReportToConsole ( nsIDocument * aDocument ,
const char * aConsoleStringId ,
2017-03-23 02:21:27 +03:00
nsTArray < const char16_t * > & aParams )
2016-04-19 10:36:19 +03:00
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
2016-09-08 05:22:25 +03:00
MOZ_ASSERT ( aDocument ) ;
2016-04-19 10:36:19 +03:00
2017-03-23 02:21:27 +03:00
DD_DEBUG ( " DecoderDoctorDiagnostics.cpp:ReportToConsole(doc=%p) ReportToConsole "
2017-03-24 09:09:41 +03:00
" - aMsg='%s' params={%s%s%s%s} " ,
2016-09-08 05:22:25 +03:00
aDocument , aConsoleStringId ,
2017-03-23 02:21:27 +03:00
aParams . IsEmpty ( )
? " <no params> "
2017-03-24 09:09:41 +03:00
: NS_ConvertUTF16toUTF8 ( aParams [ 0 ] ) . get ( ) ,
( aParams . Length ( ) < 1 | | ! aParams [ 1 ] ) ? " " : " , " ,
( aParams . Length ( ) < 1 | | ! aParams [ 1 ] )
? " "
: NS_ConvertUTF16toUTF8 ( aParams [ 1 ] ) . get ( ) ,
aParams . Length ( ) < 2 ? " " : " , ... " ) ;
2016-09-08 05:22:25 +03:00
nsContentUtils : : ReportToConsole ( nsIScriptError : : warningFlag ,
NS_LITERAL_CSTRING ( " Media " ) ,
aDocument ,
nsContentUtils : : eDOM_PROPERTIES ,
aConsoleStringId ,
2017-03-23 02:21:27 +03:00
aParams . IsEmpty ( )
? nullptr
: aParams . Elements ( ) ,
aParams . Length ( ) ) ;
2016-09-08 05:22:25 +03:00
}
static void
ReportAnalysis ( nsIDocument * aDocument ,
const NotificationAndReportStringId & aNotification ,
bool aIsSolved ,
2017-03-24 09:09:41 +03:00
const nsAString & aFormats = NS_LITERAL_STRING ( " " ) ,
const nsAString & aDecodeIssue = NS_LITERAL_STRING ( " " ) ,
const nsACString & aDocURL = NS_LITERAL_CSTRING ( " " ) ,
const nsAString & aResourceURL = NS_LITERAL_STRING ( " " ) )
2016-09-08 05:22:25 +03:00
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
if ( ! aDocument ) {
2016-04-19 10:36:19 +03:00
return ;
}
2016-05-26 16:26:10 +03:00
// Report non-solved issues to console.
if ( ! aIsSolved ) {
2017-03-23 02:21:27 +03:00
// Build parameter array needed by console message.
AutoTArray < const char16_t * ,
NotificationAndReportStringId : : maxReportParams > params ;
for ( int i = 0 ; i < NotificationAndReportStringId : : maxReportParams ; + + i ) {
if ( aNotification . mReportParams [ i ] = = ReportParam : : None ) {
break ;
}
switch ( aNotification . mReportParams [ i ] ) {
case ReportParam : : Formats :
params . AppendElement ( aFormats . Data ( ) ) ;
break ;
2017-03-24 09:09:41 +03:00
case ReportParam : : DecodeIssue :
params . AppendElement ( aDecodeIssue . Data ( ) ) ;
break ;
case ReportParam : : DocURL :
params . AppendElement ( NS_ConvertUTF8toUTF16 ( aDocURL ) . Data ( ) ) ;
break ;
case ReportParam : : ResourceURL :
params . AppendElement ( aResourceURL . Data ( ) ) ;
break ;
2017-03-23 02:21:27 +03:00
default :
MOZ_ASSERT_UNREACHABLE ( " Bad notification parameter choice " ) ;
break ;
}
}
ReportToConsole ( aDocument , aNotification . mReportStringId , params ) ;
2016-05-26 16:26:10 +03:00
}
2016-04-19 10:36:19 +03:00
2016-04-22 06:42:11 +03:00
// "media.decoder-doctor.notifications-allowed" controls which notifications
// may be dispatched to the front-end. It either contains:
// - '*' -> Allow everything.
// - Comma-separater list of ids -> Allow if aReportStringId (from
// dom.properties) is one of them.
// - Nothing (missing or empty) -> Disable everything.
nsAdoptingCString filter =
Preferences : : GetCString ( " media.decoder-doctor.notifications-allowed " ) ;
filter . StripWhitespace ( ) ;
2016-05-26 10:01:52 +03:00
if ( filter . EqualsLiteral ( " * " )
2016-05-24 18:03:21 +03:00
| | StringListContains ( filter , aNotification . mReportStringId ) ) {
2016-04-19 10:36:20 +03:00
DispatchNotification (
2017-03-24 09:09:41 +03:00
aDocument - > GetInnerWindow ( ) , aNotification , aIsSolved ,
aFormats ,
aDecodeIssue ,
aDocURL ,
aResourceURL ) ;
2016-04-19 10:36:20 +03:00
}
2016-04-19 10:36:19 +03:00
}
2016-05-24 20:24:14 +03:00
static nsString
CleanItemForFormatsList ( const nsAString & aItem )
2016-04-22 06:42:11 +03:00
{
2016-05-24 20:24:14 +03:00
nsString item ( aItem ) ;
// Remove commas from item, as commas are used to separate items. It's fine
// to have a one-way mapping, it's only used for comparisons and in
// console display (where formats shouldn't contain commas in the first place)
item . ReplaceChar ( ' , ' , ' ' ) ;
item . CompressWhitespace ( ) ;
return item ;
}
static void
AppendToFormatsList ( nsAString & aList , const nsAString & aItem )
{
if ( ! aList . IsEmpty ( ) ) {
aList + = NS_LITERAL_STRING ( " , " ) ;
2016-04-22 06:42:11 +03:00
}
2016-05-24 20:24:14 +03:00
aList + = CleanItemForFormatsList ( aItem ) ;
}
static bool
FormatsListContains ( const nsAString & aList , const nsAString & aItem )
{
return StringListContains ( aList , CleanItemForFormatsList ( aItem ) ) ;
2016-04-22 06:42:11 +03:00
}
2017-03-24 09:09:41 +03:00
// Create a webcompat-friendly description of a MediaResult.
static nsString
MediaResultDescription ( const MediaResult & aResult , bool aIsError )
{
nsCString name ;
GetErrorName ( aResult . Code ( ) , static_cast < nsACString & > ( name ) ) ;
return NS_ConvertUTF8toUTF16 (
nsPrintfCString (
" %s Code: %s (0x%08 " PRIx32 " )%s%s " ,
aIsError ? " Error " : " Warning " , name . get ( ) ,
static_cast < uint32_t > ( aResult . Code ( ) ) ,
aResult . Message ( ) . IsEmpty ( ) ? " " : " \n Details: " ,
aResult . Message ( ) . get ( ) ) ) ;
}
2016-04-19 10:36:19 +03:00
void
DecoderDoctorDocumentWatcher : : SynthesizeAnalysis ( )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
2016-05-12 09:28:11 +03:00
nsAutoString playableFormats ;
nsAutoString unplayableFormats ;
// Subsets of unplayableFormats that require a specific platform decoder:
2016-04-22 06:42:11 +03:00
# if defined(XP_WIN)
2016-05-12 09:28:11 +03:00
nsAutoString formatsRequiringWMF ;
2016-04-22 06:42:11 +03:00
# endif
2016-04-19 10:36:20 +03:00
# if defined(MOZ_FFMPEG)
2016-05-12 09:28:11 +03:00
nsAutoString formatsRequiringFFMpeg ;
2016-04-19 10:36:20 +03:00
# endif
2016-04-22 06:42:11 +03:00
nsAutoString supportedKeySystems ;
2016-04-22 06:42:11 +03:00
nsAutoString unsupportedKeySystems ;
2016-04-22 06:42:11 +03:00
DecoderDoctorDiagnostics : : KeySystemIssue lastKeySystemIssue =
DecoderDoctorDiagnostics : : eUnset ;
2017-03-24 09:09:41 +03:00
// Only deal with one decode error per document (the first one found).
const MediaResult * firstDecodeError = nullptr ;
const nsString * firstDecodeErrorMediaSrc = nullptr ;
// Only deal with one decode warning per document (the first one found).
const MediaResult * firstDecodeWarning = nullptr ;
const nsString * firstDecodeWarningMediaSrc = nullptr ;
2016-04-22 06:42:11 +03:00
2016-05-12 08:52:24 +03:00
for ( const auto & diag : mDiagnosticsSequence ) {
2016-04-22 06:42:11 +03:00
switch ( diag . mDecoderDoctorDiagnostics . Type ( ) ) {
2016-05-16 14:35:14 +03:00
case DecoderDoctorDiagnostics : : eFormatSupportCheck :
if ( diag . mDecoderDoctorDiagnostics . CanPlay ( ) ) {
2016-05-24 20:24:14 +03:00
AppendToFormatsList ( playableFormats ,
diag . mDecoderDoctorDiagnostics . Format ( ) ) ;
2016-05-16 14:35:14 +03:00
} else {
2016-05-24 20:24:14 +03:00
AppendToFormatsList ( unplayableFormats ,
diag . mDecoderDoctorDiagnostics . Format ( ) ) ;
2016-04-22 06:42:11 +03:00
# if defined(XP_WIN)
2016-05-16 14:35:14 +03:00
if ( diag . mDecoderDoctorDiagnostics . DidWMFFailToLoad ( ) ) {
2016-05-24 20:24:14 +03:00
AppendToFormatsList ( formatsRequiringWMF ,
diag . mDecoderDoctorDiagnostics . Format ( ) ) ;
2016-05-16 14:35:14 +03:00
}
2016-04-22 06:42:11 +03:00
# endif
2016-04-19 10:36:20 +03:00
# if defined(MOZ_FFMPEG)
2016-05-16 14:35:14 +03:00
if ( diag . mDecoderDoctorDiagnostics . DidFFmpegFailToLoad ( ) ) {
2016-05-24 20:24:14 +03:00
AppendToFormatsList ( formatsRequiringFFMpeg ,
diag . mDecoderDoctorDiagnostics . Format ( ) ) ;
2016-05-16 14:35:14 +03:00
}
2016-04-19 10:36:20 +03:00
# endif
2016-04-22 06:42:11 +03:00
}
2016-05-16 14:35:14 +03:00
break ;
case DecoderDoctorDiagnostics : : eMediaKeySystemAccessRequest :
if ( diag . mDecoderDoctorDiagnostics . IsKeySystemSupported ( ) ) {
2016-05-24 20:24:14 +03:00
AppendToFormatsList ( supportedKeySystems ,
diag . mDecoderDoctorDiagnostics . KeySystem ( ) ) ;
2016-05-16 14:35:14 +03:00
} else {
2016-05-24 20:24:14 +03:00
AppendToFormatsList ( unsupportedKeySystems ,
diag . mDecoderDoctorDiagnostics . KeySystem ( ) ) ;
2016-05-16 14:35:14 +03:00
DecoderDoctorDiagnostics : : KeySystemIssue issue =
diag . mDecoderDoctorDiagnostics . GetKeySystemIssue ( ) ;
if ( issue ! = DecoderDoctorDiagnostics : : eUnset ) {
lastKeySystemIssue = issue ;
}
}
break ;
2016-09-19 05:50:01 +03:00
case DecoderDoctorDiagnostics : : eEvent :
2016-09-30 13:00:49 +03:00
MOZ_ASSERT_UNREACHABLE ( " Events shouldn't be stored for processing. " ) ;
2016-09-19 05:50:01 +03:00
break ;
2017-02-21 06:07:23 +03:00
case DecoderDoctorDiagnostics : : eDecodeError :
2017-03-24 09:09:41 +03:00
if ( ! firstDecodeError ) {
firstDecodeError = & diag . mDecoderDoctorDiagnostics . DecodeIssue ( ) ;
firstDecodeErrorMediaSrc =
& diag . mDecoderDoctorDiagnostics . DecodeIssueMediaSrc ( ) ;
}
2017-02-21 06:07:23 +03:00
break ;
case DecoderDoctorDiagnostics : : eDecodeWarning :
2017-03-24 09:09:41 +03:00
if ( ! firstDecodeWarning ) {
firstDecodeWarning = & diag . mDecoderDoctorDiagnostics . DecodeIssue ( ) ;
firstDecodeWarningMediaSrc =
& diag . mDecoderDoctorDiagnostics . DecodeIssueMediaSrc ( ) ;
}
2017-02-21 06:07:23 +03:00
break ;
2016-05-16 14:35:14 +03:00
default :
2017-02-21 06:07:23 +03:00
MOZ_ASSERT_UNREACHABLE ( " Unhandled DecoderDoctorDiagnostics type " ) ;
2016-05-16 14:35:14 +03:00
break ;
2016-04-19 10:36:19 +03:00
}
}
2016-04-22 06:42:11 +03:00
2016-05-24 17:41:53 +03:00
// Check if issues have been solved, by finding if some now-playable
// key systems or formats were previously recorded as having issues.
if ( ! supportedKeySystems . IsEmpty ( ) | | ! playableFormats . IsEmpty ( ) ) {
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - supported key systems '%s', playable formats '%s'; See if they show issues have been solved... " ,
this , mDocument ,
NS_ConvertUTF16toUTF8 ( supportedKeySystems ) . Data ( ) ,
NS_ConvertUTF16toUTF8 ( playableFormats ) . get ( ) ) ;
const nsAString * workingFormatsArray [ ] =
{ & supportedKeySystems , & playableFormats } ;
// For each type of notification, retrieve the pref that contains formats/
// key systems with issues.
for ( const NotificationAndReportStringId * id :
sAllNotificationsAndReportStringIds ) {
nsAutoCString formatsPref ( " media.decoder-doctor. " ) ;
formatsPref + = id - > mReportStringId ;
formatsPref + = " .formats " ;
nsAdoptingString formatsWithIssues =
Preferences : : GetString ( formatsPref . Data ( ) ) ;
if ( formatsWithIssues . IsEmpty ( ) ) {
continue ;
}
// See if that list of formats-with-issues contains any formats that are
// now playable/supported.
bool solved = false ;
for ( const nsAString * workingFormats : workingFormatsArray ) {
for ( const auto & workingFormat : MakeStringListRange ( * workingFormats ) ) {
if ( FormatsListContains ( formatsWithIssues , workingFormat ) ) {
// This now-working format used not to work -> Report solved issue.
DD_INFO ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - %s solved ('%s' now works, it was in pref(%s)='%s') " ,
this , mDocument , id - > mReportStringId ,
NS_ConvertUTF16toUTF8 ( workingFormat ) . get ( ) ,
formatsPref . Data ( ) ,
NS_ConvertUTF16toUTF8 ( formatsWithIssues ) . get ( ) ) ;
2016-09-08 05:22:25 +03:00
ReportAnalysis ( mDocument , * id , true , workingFormat ) ;
2016-05-24 17:41:53 +03:00
// This particular Notification&ReportId has been solved, no need
// to keep looking at other keysys/formats that might solve it too.
solved = true ;
break ;
}
}
if ( solved ) {
break ;
}
}
if ( ! solved ) {
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - %s not solved (pref(%s)='%s') " ,
this , mDocument , id - > mReportStringId , formatsPref . Data ( ) ,
NS_ConvertUTF16toUTF8 ( formatsWithIssues ) . get ( ) ) ;
}
}
}
2016-05-12 05:49:36 +03:00
// Look at Key System issues first, as they take precedence over format checks.
2016-04-22 06:42:11 +03:00
if ( ! unsupportedKeySystems . IsEmpty ( ) & & supportedKeySystems . IsEmpty ( ) ) {
// No supported key systems!
switch ( lastKeySystemIssue ) {
2016-05-16 14:35:14 +03:00
case DecoderDoctorDiagnostics : : eWidevineWithNoWMF :
2016-11-22 08:11:04 +03:00
DD_INFO ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unsupported key systems: %s, Widevine without WMF " ,
this , mDocument , NS_ConvertUTF16toUTF8 ( unsupportedKeySystems ) . get ( ) ) ;
ReportAnalysis ( mDocument , sMediaWidevineNoWMF , false ,
unsupportedKeySystems ) ;
return ;
2016-05-16 14:35:14 +03:00
default :
break ;
2016-04-22 06:42:11 +03:00
}
}
2016-05-12 05:49:36 +03:00
// Next, check playability of requested formats.
if ( ! unplayableFormats . IsEmpty ( ) ) {
// Some requested formats cannot be played.
if ( playableFormats . IsEmpty ( ) ) {
2016-05-12 09:28:11 +03:00
// No requested formats can be played. See if we can help the user, by
// going through expected decoders from most to least desirable.
2016-04-22 06:42:11 +03:00
# if defined(XP_WIN)
2016-05-12 09:28:11 +03:00
if ( ! formatsRequiringWMF . IsEmpty ( ) ) {
2016-12-18 00:13:11 +03:00
DD_INFO ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unplayable formats: %s -> Cannot play media because WMF was not found " ,
this , mDocument , NS_ConvertUTF16toUTF8 ( formatsRequiringWMF ) . get ( ) ) ;
ReportAnalysis ( mDocument , sMediaWMFNeeded , false , formatsRequiringWMF ) ;
2016-05-12 05:49:36 +03:00
return ;
}
2016-04-22 06:42:11 +03:00
# endif
2016-04-19 10:36:20 +03:00
# if defined(MOZ_FFMPEG)
2016-05-12 09:28:11 +03:00
if ( ! formatsRequiringFFMpeg . IsEmpty ( ) ) {
2016-09-22 10:06:45 +03:00
switch ( FFmpegRuntimeLinker : : LinkStatusCode ( ) ) {
case FFmpegRuntimeLinker : : LinkStatus_INVALID_FFMPEG_CANDIDATE :
case FFmpegRuntimeLinker : : LinkStatus_UNUSABLE_LIBAV57 :
case FFmpegRuntimeLinker : : LinkStatus_INVALID_LIBAV_CANDIDATE :
case FFmpegRuntimeLinker : : LinkStatus_OBSOLETE_FFMPEG :
case FFmpegRuntimeLinker : : LinkStatus_OBSOLETE_LIBAV :
case FFmpegRuntimeLinker : : LinkStatus_INVALID_CANDIDATE :
DD_INFO ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unplayable formats: %s -> Cannot play media because of unsupported %s (Reason: %s) " ,
this , mDocument ,
NS_ConvertUTF16toUTF8 ( formatsRequiringFFMpeg ) . get ( ) ,
FFmpegRuntimeLinker : : LinkStatusLibraryName ( ) ,
FFmpegRuntimeLinker : : LinkStatusString ( ) ) ;
ReportAnalysis ( mDocument , sUnsupportedLibavcodec ,
false , formatsRequiringFFMpeg ) ;
return ;
case FFmpegRuntimeLinker : : LinkStatus_INIT :
MOZ_FALLTHROUGH_ASSERT ( " Unexpected LinkStatus_INIT " ) ;
case FFmpegRuntimeLinker : : LinkStatus_SUCCEEDED :
MOZ_FALLTHROUGH_ASSERT ( " Unexpected LinkStatus_SUCCEEDED " ) ;
case FFmpegRuntimeLinker : : LinkStatus_NOT_FOUND :
DD_INFO ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unplayable formats: %s -> Cannot play media because platform decoder was not found (Reason: %s) " ,
this , mDocument ,
NS_ConvertUTF16toUTF8 ( formatsRequiringFFMpeg ) . get ( ) ,
FFmpegRuntimeLinker : : LinkStatusString ( ) ) ;
ReportAnalysis ( mDocument , sMediaPlatformDecoderNotFound ,
false , formatsRequiringFFMpeg ) ;
return ;
}
2016-05-12 05:49:36 +03:00
}
# endif
2016-05-24 17:41:53 +03:00
DD_INFO ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Cannot play media, unplayable formats: %s " ,
2016-05-12 05:49:36 +03:00
this , mDocument , NS_ConvertUTF16toUTF8 ( unplayableFormats ) . get ( ) ) ;
2016-09-08 05:22:25 +03:00
ReportAnalysis ( mDocument , sMediaCannotPlayNoDecoders ,
false , unplayableFormats ) ;
2016-04-22 06:42:11 +03:00
return ;
2016-04-19 10:36:20 +03:00
}
2016-05-12 05:49:36 +03:00
2016-04-22 06:42:11 +03:00
DD_INFO ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Can play media, but no decoders for some requested formats: %s " ,
this , mDocument , NS_ConvertUTF16toUTF8 ( unplayableFormats ) . get ( ) ) ;
2016-04-22 06:42:11 +03:00
if ( Preferences : : GetBool ( " media.decoder-doctor.verbose " , false ) ) {
2016-09-08 05:22:25 +03:00
ReportAnalysis ( mDocument , sMediaNoDecoders , false , unplayableFormats ) ;
2016-04-19 10:36:19 +03:00
}
2016-04-22 06:42:11 +03:00
return ;
2016-04-19 10:36:19 +03:00
}
2017-03-24 09:09:41 +03:00
if ( firstDecodeError ) {
DD_INFO ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Decode error: %s " ,
this , mDocument , firstDecodeError - > Description ( ) . get ( ) ) ;
ReportAnalysis ( mDocument , sMediaDecodeError , false ,
NS_LITERAL_STRING ( " " ) ,
MediaResultDescription ( * firstDecodeError , true ) ,
mDocument - > GetDocumentURI ( ) - > GetSpecOrDefault ( ) ,
* firstDecodeErrorMediaSrc ) ;
return ;
}
if ( firstDecodeWarning ) {
DD_INFO ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Decode warning: %s " ,
this , mDocument , firstDecodeWarning - > Description ( ) . get ( ) ) ;
ReportAnalysis ( mDocument , sMediaDecodeWarning , false ,
NS_LITERAL_STRING ( " " ) ,
MediaResultDescription ( * firstDecodeWarning , false ) ,
mDocument - > GetDocumentURI ( ) - > GetSpecOrDefault ( ) ,
* firstDecodeWarningMediaSrc ) ;
return ;
}
2016-04-22 06:42:11 +03:00
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Can play media, decoders available for all requested formats " ,
this , mDocument ) ;
2016-04-19 10:36:19 +03:00
}
void
2016-04-22 06:42:11 +03:00
DecoderDoctorDocumentWatcher : : AddDiagnostics ( DecoderDoctorDiagnostics & & aDiagnostics ,
const char * aCallSite )
2016-04-19 10:36:19 +03:00
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
2016-09-19 05:50:01 +03:00
MOZ_ASSERT ( aDiagnostics . Type ( ) ! = DecoderDoctorDiagnostics : : eEvent ) ;
2016-04-19 10:36:19 +03:00
if ( ! mDocument ) {
return ;
}
2016-04-22 06:42:11 +03:00
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::AddDiagnostics(DecoderDoctorDiagnostics{%s}, call site '%s') " ,
this , mDocument , aDiagnostics . GetDescription ( ) . Data ( ) , aCallSite ) ;
2016-09-19 05:50:01 +03:00
mDiagnosticsSequence . AppendElement ( Diagnostics ( Move ( aDiagnostics ) , aCallSite ) ) ;
2016-04-19 10:36:19 +03:00
EnsureTimerIsStarted ( ) ;
}
NS_IMETHODIMP
DecoderDoctorDocumentWatcher : : Notify ( nsITimer * timer )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
MOZ_ASSERT ( timer = = mTimer ) ;
// Forget timer. (Assuming timer keeps itself and us alive during this call.)
mTimer = nullptr ;
if ( ! mDocument ) {
return NS_OK ;
}
if ( mDiagnosticsSequence . Length ( ) > mDiagnosticsHandled ) {
// We have new diagnostic data.
mDiagnosticsHandled = mDiagnosticsSequence . Length ( ) ;
SynthesizeAnalysis ( ) ;
// Restart timer, to redo analysis or stop watching this document,
// depending on whether anything new happens.
EnsureTimerIsStarted ( ) ;
} else {
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::Notify() - No new diagnostics to analyze -> Stop watching " ,
this , mDocument ) ;
// Stop watching this document, we don't expect more diagnostics for now.
// If more diagnostics come in, we'll treat them as another burst, separately.
// 'true' to remove the property from the document.
StopWatching ( true ) ;
}
return NS_OK ;
}
2017-02-14 02:45:23 +03:00
NS_IMETHODIMP
DecoderDoctorDocumentWatcher : : GetName ( nsACString & aName )
{
aName . AssignASCII ( " DecoderDoctorDocumentWatcher_timer " ) ;
return NS_OK ;
}
NS_IMETHODIMP
DecoderDoctorDocumentWatcher : : SetName ( const char * aName )
{
return NS_ERROR_NOT_IMPLEMENTED ;
}
2016-04-19 10:36:19 +03:00
2016-04-19 10:36:19 +03:00
void
2016-04-22 06:42:11 +03:00
DecoderDoctorDiagnostics : : StoreFormatDiagnostics ( nsIDocument * aDocument ,
const nsAString & aFormat ,
bool aCanPlay ,
const char * aCallSite )
2016-04-19 10:36:19 +03:00
{
2016-04-19 10:36:19 +03:00
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
2016-04-22 06:42:11 +03:00
// Make sure we only store once.
MOZ_ASSERT ( mDiagnosticsType = = eUnsaved ) ;
mDiagnosticsType = eFormatSupportCheck ;
2016-04-19 10:36:19 +03:00
if ( NS_WARN_IF ( ! aDocument ) ) {
2016-04-22 06:42:11 +03:00
DD_WARN ( " DecoderDoctorDiagnostics[%p]::StoreFormatDiagnostics(nsIDocument* aDocument=nullptr, format='%s', can-play=%d, call site '%s') " ,
this , NS_ConvertUTF16toUTF8 ( aFormat ) . get ( ) , aCanPlay , aCallSite ) ;
return ;
}
if ( NS_WARN_IF ( aFormat . IsEmpty ( ) ) ) {
DD_WARN ( " DecoderDoctorDiagnostics[%p]::StoreFormatDiagnostics(nsIDocument* aDocument=%p, format=<empty>, can-play=%d, call site '%s') " ,
this , aDocument , aCanPlay , aCallSite ) ;
2016-04-19 10:36:19 +03:00
return ;
}
2016-04-19 10:36:19 +03:00
RefPtr < DecoderDoctorDocumentWatcher > watcher =
DecoderDoctorDocumentWatcher : : RetrieveOrCreate ( aDocument ) ;
if ( NS_WARN_IF ( ! watcher ) ) {
2016-04-22 06:42:11 +03:00
DD_WARN ( " DecoderDoctorDiagnostics[%p]::StoreFormatDiagnostics(nsIDocument* aDocument=%p, format='%s', can-play=%d, call site '%s') - Could not create document watcher " ,
this , aDocument , NS_ConvertUTF16toUTF8 ( aFormat ) . get ( ) , aCanPlay , aCallSite ) ;
2016-04-19 10:36:19 +03:00
return ;
}
2016-04-22 06:42:11 +03:00
mFormat = aFormat ;
mCanPlay = aCanPlay ;
2016-04-19 10:36:19 +03:00
// StoreDiagnostics should only be called once, after all data is available,
// so it is safe to Move() from this object.
2016-04-22 06:42:11 +03:00
watcher - > AddDiagnostics ( Move ( * this ) , aCallSite ) ;
2016-04-22 06:42:11 +03:00
// Even though it's moved-from, the type should stay set
// (Only used to ensure that we do store only once.)
MOZ_ASSERT ( mDiagnosticsType = = eFormatSupportCheck ) ;
}
void
DecoderDoctorDiagnostics : : StoreMediaKeySystemAccess ( nsIDocument * aDocument ,
const nsAString & aKeySystem ,
bool aIsSupported ,
const char * aCallSite )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
// Make sure we only store once.
MOZ_ASSERT ( mDiagnosticsType = = eUnsaved ) ;
mDiagnosticsType = eMediaKeySystemAccessRequest ;
if ( NS_WARN_IF ( ! aDocument ) ) {
DD_WARN ( " DecoderDoctorDiagnostics[%p]::StoreMediaKeySystemAccess(nsIDocument* aDocument=nullptr, keysystem='%s', supported=%d, call site '%s') " ,
this , NS_ConvertUTF16toUTF8 ( aKeySystem ) . get ( ) , aIsSupported , aCallSite ) ;
return ;
}
if ( NS_WARN_IF ( aKeySystem . IsEmpty ( ) ) ) {
DD_WARN ( " DecoderDoctorDiagnostics[%p]::StoreMediaKeySystemAccess(nsIDocument* aDocument=%p, keysystem=<empty>, supported=%d, call site '%s') " ,
this , aDocument , aIsSupported , aCallSite ) ;
return ;
}
RefPtr < DecoderDoctorDocumentWatcher > watcher =
DecoderDoctorDocumentWatcher : : RetrieveOrCreate ( aDocument ) ;
if ( NS_WARN_IF ( ! watcher ) ) {
DD_WARN ( " DecoderDoctorDiagnostics[%p]::StoreMediaKeySystemAccess(nsIDocument* aDocument=%p, keysystem='%s', supported=%d, call site '%s') - Could not create document watcher " ,
2016-09-07 08:36:36 +03:00
this , aDocument , NS_ConvertUTF16toUTF8 ( aKeySystem ) . get ( ) , aIsSupported , aCallSite ) ;
2016-04-22 06:42:11 +03:00
return ;
}
mKeySystem = aKeySystem ;
mIsKeySystemSupported = aIsSupported ;
2016-09-19 05:50:01 +03:00
// StoreMediaKeySystemAccess should only be called once, after all data is
// available, so it is safe to Move() from this object.
2016-04-22 06:42:11 +03:00
watcher - > AddDiagnostics ( Move ( * this ) , aCallSite ) ;
// Even though it's moved-from, the type should stay set
// (Only used to ensure that we do store only once.)
MOZ_ASSERT ( mDiagnosticsType = = eMediaKeySystemAccessRequest ) ;
2016-04-22 06:42:11 +03:00
}
2016-09-19 05:50:01 +03:00
void
DecoderDoctorDiagnostics : : StoreEvent ( nsIDocument * aDocument ,
const DecoderDoctorEvent & aEvent ,
const char * aCallSite )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
// Make sure we only store once.
MOZ_ASSERT ( mDiagnosticsType = = eUnsaved ) ;
mDiagnosticsType = eEvent ;
mEvent = aEvent ;
if ( NS_WARN_IF ( ! aDocument ) ) {
DD_WARN ( " DecoderDoctorDiagnostics[%p]::StoreEvent(nsIDocument* aDocument=nullptr, aEvent=%s, call site '%s') " ,
this , GetDescription ( ) . get ( ) , aCallSite ) ;
return ;
}
2016-09-19 05:41:36 +03:00
// Don't keep events for later processing, just handle them now.
# ifdef MOZ_PULSEAUDIO
switch ( aEvent . mDomain ) {
case DecoderDoctorEvent : : eAudioSinkStartup :
if ( aEvent . mResult = = NS_ERROR_DOM_MEDIA_CUBEB_INITIALIZATION_ERR ) {
DD_INFO ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::AddDiagnostics() - unable to initialize PulseAudio " ,
this , aDocument ) ;
ReportAnalysis ( aDocument , sCannotInitializePulseAudio ,
false , NS_LITERAL_STRING ( " * " ) ) ;
} else if ( aEvent . mResult = = NS_OK ) {
DD_INFO ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::AddDiagnostics() - now able to initialize PulseAudio " ,
this , aDocument ) ;
ReportAnalysis ( aDocument , sCannotInitializePulseAudio ,
true , NS_LITERAL_STRING ( " * " ) ) ;
}
break ;
}
# endif // MOZ_PULSEAUDIO
2016-09-19 05:50:01 +03:00
}
2017-02-21 06:07:23 +03:00
void
DecoderDoctorDiagnostics : : StoreDecodeError ( nsIDocument * aDocument ,
const MediaResult & aError ,
const nsString & aMediaSrc ,
const char * aCallSite )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
// Make sure we only store once.
MOZ_ASSERT ( mDiagnosticsType = = eUnsaved ) ;
mDiagnosticsType = eDecodeError ;
if ( NS_WARN_IF ( ! aDocument ) ) {
DD_WARN ( " DecoderDoctorDiagnostics[%p]::StoreDecodeError( "
" nsIDocument* aDocument=nullptr, aError=%s, "
" aMediaSrc=<provided>, call site '%s') " ,
this , aError . Description ( ) . get ( ) , aCallSite ) ;
return ;
}
RefPtr < DecoderDoctorDocumentWatcher > watcher =
DecoderDoctorDocumentWatcher : : RetrieveOrCreate ( aDocument ) ;
if ( NS_WARN_IF ( ! watcher ) ) {
DD_WARN ( " DecoderDoctorDiagnostics[%p]::StoreDecodeError( "
" nsIDocument* aDocument=%p, aError='%s', aMediaSrc=<provided>, "
" call site '%s') - Could not create document watcher " ,
this , aDocument , aError . Description ( ) . get ( ) , aCallSite ) ;
return ;
}
mDecodeIssue = aError ;
mDecodeIssueMediaSrc = aMediaSrc ;
// StoreDecodeError should only be called once, after all data is
// available, so it is safe to Move() from this object.
watcher - > AddDiagnostics ( Move ( * this ) , aCallSite ) ;
// Even though it's moved-from, the type should stay set
// (Only used to ensure that we do store only once.)
MOZ_ASSERT ( mDiagnosticsType = = eDecodeError ) ;
}
void
DecoderDoctorDiagnostics : : StoreDecodeWarning ( nsIDocument * aDocument ,
const MediaResult & aWarning ,
const nsString & aMediaSrc ,
const char * aCallSite )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
// Make sure we only store once.
MOZ_ASSERT ( mDiagnosticsType = = eUnsaved ) ;
mDiagnosticsType = eDecodeWarning ;
if ( NS_WARN_IF ( ! aDocument ) ) {
DD_WARN ( " DecoderDoctorDiagnostics[%p]::StoreDecodeWarning( "
" nsIDocument* aDocument=nullptr, aWarning=%s, "
" aMediaSrc=<provided>, call site '%s') " ,
this , aWarning . Description ( ) . get ( ) , aCallSite ) ;
return ;
}
RefPtr < DecoderDoctorDocumentWatcher > watcher =
DecoderDoctorDocumentWatcher : : RetrieveOrCreate ( aDocument ) ;
if ( NS_WARN_IF ( ! watcher ) ) {
DD_WARN ( " DecoderDoctorDiagnostics[%p]::StoreDecodeWarning( "
" nsIDocument* aDocument=%p, aWarning='%s', aMediaSrc=<provided>, "
" call site '%s') - Could not create document watcher " ,
this , aDocument , aWarning . Description ( ) . get ( ) , aCallSite ) ;
return ;
}
mDecodeIssue = aWarning ;
mDecodeIssueMediaSrc = aMediaSrc ;
// StoreDecodeWarning should only be called once, after all data is
// available, so it is safe to Move() from this object.
watcher - > AddDiagnostics ( Move ( * this ) , aCallSite ) ;
// Even though it's moved-from, the type should stay set
// (Only used to ensure that we do store only once.)
MOZ_ASSERT ( mDiagnosticsType = = eDecodeWarning ) ;
}
2016-09-19 05:50:01 +03:00
static const char *
EventDomainString ( DecoderDoctorEvent : : Domain aDomain )
{
switch ( aDomain ) {
2016-09-19 05:41:36 +03:00
case DecoderDoctorEvent : : eAudioSinkStartup :
return " audio-sink-startup " ;
2016-09-19 05:50:01 +03:00
}
return " ? " ;
}
2016-04-22 06:42:11 +03:00
nsCString
DecoderDoctorDiagnostics : : GetDescription ( ) const
{
nsCString s ;
2016-04-22 06:42:11 +03:00
switch ( mDiagnosticsType ) {
2016-05-16 14:35:14 +03:00
case eUnsaved :
s = " Unsaved diagnostics, cannot get accurate description " ;
2016-04-22 06:42:11 +03:00
break ;
2016-05-16 14:35:14 +03:00
case eFormatSupportCheck :
s = " format=' " ;
s + = NS_ConvertUTF16toUTF8 ( mFormat ) . get ( ) ;
s + = mCanPlay ? " ', can play " : " ', cannot play " ;
2016-08-19 13:14:28 +03:00
if ( mVideoNotSupported ) {
s + = " , but video format not supported " ;
}
if ( mAudioNotSupported ) {
s + = " , but audio format not supported " ;
}
2016-05-16 14:35:14 +03:00
if ( mWMFFailedToLoad ) {
s + = " , Windows platform decoder failed to load " ;
}
if ( mFFmpegFailedToLoad ) {
s + = " , Linux platform decoder failed to load " ;
}
if ( mGMPPDMFailedToStartup ) {
s + = " , GMP PDM failed to startup " ;
} else if ( ! mGMP . IsEmpty ( ) ) {
s + = " , Using GMP ' " ;
s + = mGMP ;
s + = " ' " ;
}
break ;
case eMediaKeySystemAccessRequest :
s = " key system=' " ;
s + = NS_ConvertUTF16toUTF8 ( mKeySystem ) . get ( ) ;
s + = mIsKeySystemSupported ? " ', supported " : " ', not supported " ;
switch ( mKeySystemIssue ) {
case eUnset :
break ;
case eWidevineWithNoWMF :
s + = " , Widevine with no WMF " ;
break ;
}
2016-04-22 06:42:11 +03:00
break ;
2016-09-19 05:50:01 +03:00
case eEvent :
2016-12-14 19:32:21 +03:00
s = nsPrintfCString ( " event domain %s result=% " PRIu32 ,
EventDomainString ( mEvent . mDomain ) , static_cast < uint32_t > ( mEvent . mResult ) ) ;
2016-09-19 05:50:01 +03:00
break ;
2017-02-21 06:07:23 +03:00
case eDecodeError :
s = " decode error: " ;
s + = mDecodeIssue . Description ( ) ;
s + = " , src=' " ;
s + = mDecodeIssueMediaSrc . IsEmpty ( ) ? " <none> " : " <provided> " ;
s + = " ' " ;
break ;
case eDecodeWarning :
s = " decode warning: " ;
s + = mDecodeIssue . Description ( ) ;
s + = " , src=' " ;
s + = mDecodeIssueMediaSrc . IsEmpty ( ) ? " <none> " : " <provided> " ;
s + = " ' " ;
break ;
2016-04-22 06:42:11 +03:00
default :
2016-09-19 05:50:01 +03:00
MOZ_ASSERT_UNREACHABLE ( " Unexpected DiagnosticsType " ) ;
2016-04-22 06:42:11 +03:00
s = " ? " ;
break ;
2016-04-22 06:42:11 +03:00
}
return s ;
2016-04-19 10:36:19 +03:00
}
} // namespace mozilla