// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/renderers/win/media_engine_notify_impl.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "media/base/win/mf_helpers.h"
namespace media {
namespace {
#define ENUM_TO_STRING(enum) \
case enum: \
return #enum
std::string MediaEngineErrorToString(MF_MEDIA_ENGINE_ERR error) {
switch (error) {
ENUM_TO_STRING(MF_MEDIA_ENGINE_ERR_NOERROR);
ENUM_TO_STRING(MF_MEDIA_ENGINE_ERR_ABORTED);
ENUM_TO_STRING(MF_MEDIA_ENGINE_ERR_NETWORK);
ENUM_TO_STRING(MF_MEDIA_ENGINE_ERR_DECODE);
ENUM_TO_STRING(MF_MEDIA_ENGINE_ERR_ENCRYPTED);
ENUM_TO_STRING(MF_MEDIA_ENGINE_ERR_SRC_NOT_SUPPORTED);
default:
return "Unknown MF_MEDIA_ENGINE_ERR";
}
}
std::string MediaEngineEventToString(MF_MEDIA_ENGINE_EVENT event) {
switch (event) {
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_LOADSTART);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_PROGRESS);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_SUSPEND);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_ABORT);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_ERROR);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_EMPTIED);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_STALLED);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_PLAY);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_PAUSE);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_LOADEDMETADATA);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_LOADEDDATA);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_WAITING);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_PLAYING);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_CANPLAY);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_CANPLAYTHROUGH);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_SEEKING);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_SEEKED);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_TIMEUPDATE);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_ENDED);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_RATECHANGE);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_DURATIONCHANGE);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_VOLUMECHANGE);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_FORMATCHANGE);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_TIMELINE_MARKER);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_BALANCECHANGE);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_DOWNLOADCOMPLETE);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_BUFFERINGSTARTED);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_BUFFERINGENDED);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_FRAMESTEPCOMPLETED);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_NOTIFYSTABLESTATE);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_TRACKSCHANGE);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_OPMINFO);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_RESOURCELOST);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_DELAYLOADEVENT_CHANGED);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_STREAMRENDERINGERROR);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_SUPPORTEDRATES_CHANGED);
ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_AUDIOENDPOINTCHANGE);
default:
return "Unknown MF_MEDIA_ENGINE_EVENT";
}
}
#undef ENUM_TO_STRING
PipelineStatus MediaEngineErrorToPipelineStatus(
MF_MEDIA_ENGINE_ERR media_engine_error) {
switch (media_engine_error) {
case MF_MEDIA_ENGINE_ERR_NOERROR:
return PIPELINE_OK;
case MF_MEDIA_ENGINE_ERR_ABORTED:
return PIPELINE_ERROR_ABORT;
case MF_MEDIA_ENGINE_ERR_NETWORK:
return PIPELINE_ERROR_NETWORK;
case MF_MEDIA_ENGINE_ERR_DECODE:
[[fallthrough]];
case MF_MEDIA_ENGINE_ERR_ENCRYPTED:
return PIPELINE_ERROR_DECODE;
case MF_MEDIA_ENGINE_ERR_SRC_NOT_SUPPORTED:
return DEMUXER_ERROR_COULD_NOT_OPEN;
default:
NOTREACHED();
}
}
} // namespace
MediaEngineNotifyImpl::MediaEngineNotifyImpl() = default;
MediaEngineNotifyImpl::~MediaEngineNotifyImpl() = default;
HRESULT MediaEngineNotifyImpl::RuntimeClassInitialize(
ErrorCB error_cb,
EndedCB ended_cb,
FormatChangeCB format_change_cb,
LoadedDataCB loaded_data_cb,
CanPlayThroughCB can_play_through_cb,
PlayingCB playing_cb,
FirstFrameReadyCB first_frame_ready_cb,
WaitingCB waiting_cb,
FrameStepCompletedCB frame_step_completed_cb,
TimeUpdateCB time_update_cb) {
DVLOG_FUNC(1);
error_cb_ = std::move(error_cb);
ended_cb_ = std::move(ended_cb);
format_change_cb_ = std::move(format_change_cb);
loaded_data_cb_ = std::move(loaded_data_cb);
can_play_through_cb_ = std::move(can_play_through_cb);
playing_cb_ = std::move(playing_cb);
first_frame_ready_cb_ = std::move(first_frame_ready_cb);
waiting_cb_ = std::move(waiting_cb);
frame_step_completed_cb_ = std::move(frame_step_completed_cb);
time_update_cb_ = std::move(time_update_cb);
return S_OK;
}
// |param1| and |param2|'s meaning depends on the |event_code| from
// https://docs.microsoft.com/en-us/windows/win32/api/mfmediaengine/ne-mfmediaengine-mf_media_engine_event
// This method always return S_OK. Even for error |event_code| because we
// successfully handled the event.
HRESULT MediaEngineNotifyImpl::EventNotify(DWORD event_code,
DWORD_PTR param1,
DWORD param2) {
auto event = static_cast<MF_MEDIA_ENGINE_EVENT>(event_code);
DVLOG_FUNC(3) << "event=" << MediaEngineEventToString(event);
base::AutoLock lock(lock_);
if (has_shutdown_)
return S_OK;
switch (event) {
case MF_MEDIA_ENGINE_EVENT_ERROR: {
// |param1| - A member of the MF_MEDIA_ENGINE_ERR enumeration.
// |param2| - An HRESULT error code, or zero.
MF_MEDIA_ENGINE_ERR error = static_cast<MF_MEDIA_ENGINE_ERR>(param1);
HRESULT hr = param2;
LOG(ERROR) << __func__ << ": error=" << error << ", hr=" << PrintHr(hr);
// Report the HRESULT corresponding to certain MF_MEDIA_ENGINE_ERR
// TODO(b/315860185): Remove this after the investigation is done.
base::UmaHistogramSparse(
base::StrCat({"Media.MediaFoundation.MediaEngineError.",
MediaEngineErrorToString(error), ".Hresult"}),
hr);
error_cb_.Run(MediaEngineErrorToPipelineStatus(error), hr);
break;
}
case MF_MEDIA_ENGINE_EVENT_ENDED:
ended_cb_.Run();
break;
case MF_MEDIA_ENGINE_EVENT_FORMATCHANGE:
format_change_cb_.Run();
break;
case MF_MEDIA_ENGINE_EVENT_LOADEDDATA:
loaded_data_cb_.Run();
break;
case MF_MEDIA_ENGINE_EVENT_CANPLAYTHROUGH:
can_play_through_cb_.Run();
break;
case MF_MEDIA_ENGINE_EVENT_PLAYING:
playing_cb_.Run();
break;
case MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY:
first_frame_ready_cb_.Run();
break;
case MF_MEDIA_ENGINE_EVENT_WAITING:
waiting_cb_.Run();
break;
case MF_MEDIA_ENGINE_EVENT_FRAMESTEPCOMPLETED:
frame_step_completed_cb_.Run();
break;
case MF_MEDIA_ENGINE_EVENT_TIMEUPDATE:
time_update_cb_.Run();
break;
default:
DVLOG_FUNC(2) << "Unhandled event=" << MediaEngineEventToString(event);
break;
}
return S_OK;
}
void MediaEngineNotifyImpl::Shutdown() {
DVLOG_FUNC(1);
base::AutoLock lock(lock_);
has_shutdown_ = true;
}
} // namespace media