// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "media/base/media_log.h" #include <string> #include "base/atomic_sequence_num.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/values.h" namespace media { // A count of all MediaLogs created on this render process. // Used to generate unique ids. static base::AtomicSequenceNumber media_log_count(base::LINKER_INITIALIZED); const char* MediaLog::EventTypeToString(MediaLogEvent::Type type) { switch (type) { case MediaLogEvent::WEBMEDIAPLAYER_CREATED: return "WEBMEDIAPLAYER_CREATED"; case MediaLogEvent::WEBMEDIAPLAYER_DESTROYED: return "WEBMEDIAPLAYER_DESTROYED"; case MediaLogEvent::PIPELINE_CREATED: return "PIPELINE_CREATED"; case MediaLogEvent::PIPELINE_DESTROYED: return "PIPELINE_DESTROYED"; case MediaLogEvent::LOAD: return "LOAD"; case MediaLogEvent::SEEK: return "SEEK"; case MediaLogEvent::PLAY: return "PLAY"; case MediaLogEvent::PAUSE: return "PAUSE"; case MediaLogEvent::PIPELINE_STATE_CHANGED: return "PIPELINE_STATE_CHANGED"; case MediaLogEvent::PIPELINE_ERROR: return "PIPELINE_ERROR"; case MediaLogEvent::VIDEO_SIZE_SET: return "VIDEO_SIZE_SET"; case MediaLogEvent::DURATION_SET: return "DURATION_SET"; case MediaLogEvent::TOTAL_BYTES_SET: return "TOTAL_BYTES_SET"; case MediaLogEvent::STREAMING_SET: return "STREAMING_SET"; case MediaLogEvent::LOADED_SET: return "LOADED_SET"; case MediaLogEvent::NETWORK_ACTIVITY_SET: return "NETWORK_ACTIVITY_SET"; case MediaLogEvent::ENDED: return "ENDED"; case MediaLogEvent::AUDIO_RENDERER_DISABLED: return "AUDIO_RENDERER_DISABLED"; case MediaLogEvent::BUFFERED_EXTENTS_CHANGED: return "BUFFERED_EXTENTS_CHANGED"; case MediaLogEvent::STATISTICS_UPDATED: return "STATISTICS_UPDATED"; } NOTREACHED(); return NULL; } const char* MediaLog::PipelineStateToString(PipelineImpl::State state) { switch (state) { case PipelineImpl::kCreated: return "created"; case PipelineImpl::kInitDemuxer: return "initDemuxer"; case PipelineImpl::kInitAudioDecoder: return "initAudioDecoder"; case PipelineImpl::kInitAudioRenderer: return "initAudioRenderer"; case PipelineImpl::kInitVideoDecoder: return "initVideoDecoder"; case PipelineImpl::kInitVideoRenderer: return "initVideoRenderer"; case PipelineImpl::kPausing: return "pausing"; case PipelineImpl::kSeeking: return "seeking"; case PipelineImpl::kFlushing: return "flushing"; case PipelineImpl::kStarting: return "starting"; case PipelineImpl::kStarted: return "started"; case PipelineImpl::kEnded: return "ended"; case PipelineImpl::kStopping: return "stopping"; case PipelineImpl::kStopped: return "stopped"; case PipelineImpl::kError: return "error"; } NOTREACHED(); return NULL; } const char* MediaLog::PipelineStatusToString(PipelineStatus status) { switch (status) { case PIPELINE_OK: return "pipeline: ok"; case PIPELINE_ERROR_URL_NOT_FOUND: return "pipeline: url not found"; case PIPELINE_ERROR_NETWORK: return "pipeline: network error"; case PIPELINE_ERROR_DECODE: return "pipeline: decode error"; case PIPELINE_ERROR_ABORT: return "pipeline: abort"; case PIPELINE_ERROR_INITIALIZATION_FAILED: return "pipeline: initialization failed"; case PIPELINE_ERROR_REQUIRED_FILTER_MISSING: return "pipeline: required filter missing"; case PIPELINE_ERROR_OUT_OF_MEMORY: return "pipeline: out of memory"; case PIPELINE_ERROR_COULD_NOT_RENDER: return "pipeline: could not render"; case PIPELINE_ERROR_READ: return "pipeline: read error"; case PIPELINE_ERROR_AUDIO_HARDWARE: return "pipeline: audio hardware error"; case PIPELINE_ERROR_OPERATION_PENDING: return "pipeline: operation pending"; case PIPELINE_ERROR_INVALID_STATE: return "pipeline: invalid state"; case DEMUXER_ERROR_COULD_NOT_OPEN: return "demuxer: could not open"; case DEMUXER_ERROR_COULD_NOT_PARSE: return "dumuxer: could not parse"; case DEMUXER_ERROR_NO_SUPPORTED_STREAMS: return "demuxer: no supported streams"; case DEMUXER_ERROR_COULD_NOT_CREATE_THREAD: return "demuxer: could not create thread"; case DATASOURCE_ERROR_URL_NOT_SUPPORTED: return "data source: url not supported"; } NOTREACHED(); return NULL; } MediaLog::MediaLog() { id_ = media_log_count.GetNext(); stats_update_pending_ = false; } MediaLog::~MediaLog() {} void MediaLog::AddEvent(MediaLogEvent* event) { scoped_ptr<MediaLogEvent> e(event); } MediaLogEvent* MediaLog::CreateEvent(MediaLogEvent::Type type) { scoped_ptr<MediaLogEvent> event(new MediaLogEvent); event->id = id_; event->type = type; event->time = base::Time::Now(); return event.release(); } MediaLogEvent* MediaLog::CreateBooleanEvent(MediaLogEvent::Type type, const char* property, bool value) { scoped_ptr<MediaLogEvent> event(CreateEvent(type)); event->params.SetBoolean(property, value); return event.release(); } MediaLogEvent* MediaLog::CreateIntegerEvent(MediaLogEvent::Type type, const char* property, int64 value) { scoped_ptr<MediaLogEvent> event(CreateEvent(type)); event->params.SetInteger(property, value); return event.release(); } MediaLogEvent* MediaLog::CreateTimeEvent(MediaLogEvent::Type type, const char* property, base::TimeDelta value) { scoped_ptr<MediaLogEvent> event(CreateEvent(type)); event->params.SetDouble(property, value.InSecondsF()); return event.release(); } MediaLogEvent* MediaLog::CreateLoadEvent(const std::string& url) { scoped_ptr<MediaLogEvent> event(CreateEvent(MediaLogEvent::LOAD)); event->params.SetString("url", url); return event.release(); } MediaLogEvent* MediaLog::CreateSeekEvent(float seconds) { scoped_ptr<MediaLogEvent> event(CreateEvent(MediaLogEvent::SEEK)); event->params.SetDouble("seek_target", seconds); return event.release(); } MediaLogEvent* MediaLog::CreatePipelineStateChangedEvent( PipelineImpl::State state) { scoped_ptr<MediaLogEvent> event( CreateEvent(MediaLogEvent::PIPELINE_STATE_CHANGED)); event->params.SetString("pipeline_state", PipelineStateToString(state)); return event.release(); } MediaLogEvent* MediaLog::CreatePipelineErrorEvent(PipelineStatus error) { scoped_ptr<MediaLogEvent> event(CreateEvent(MediaLogEvent::PIPELINE_ERROR)); event->params.SetString("pipeline_error", PipelineStatusToString(error)); return event.release(); } MediaLogEvent* MediaLog::CreateVideoSizeSetEvent(size_t width, size_t height) { scoped_ptr<MediaLogEvent> event(CreateEvent(MediaLogEvent::VIDEO_SIZE_SET)); event->params.SetInteger("width", width); event->params.SetInteger("height", height); return event.release(); } MediaLogEvent* MediaLog::CreateBufferedExtentsChangedEvent( size_t start, size_t current, size_t end) { scoped_ptr<MediaLogEvent> event( CreateEvent(MediaLogEvent::BUFFERED_EXTENTS_CHANGED)); event->params.SetInteger("buffer_start", start); event->params.SetInteger("buffer_current", current); event->params.SetInteger("buffer_end", end); return event.release(); } void MediaLog::QueueStatisticsUpdatedEvent(PipelineStatistics stats) { base::AutoLock auto_lock(stats_lock_); last_statistics_ = stats; // Sadly, this function can get dispatched on threads not running a message // loop. Happily, this is pretty rare (only VideoRendererBase at this time) // so we simply leave stats updating for another call to trigger. if (!stats_update_pending_ && MessageLoop::current()) { stats_update_pending_ = true; MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod(this, &media::MediaLog::AddStatisticsUpdatedEvent), 500); } } void MediaLog::AddStatisticsUpdatedEvent() { base::AutoLock auto_lock(stats_lock_); scoped_ptr<MediaLogEvent> event( CreateEvent(MediaLogEvent::STATISTICS_UPDATED)); event->params.SetInteger("audio_bytes_decoded", last_statistics_.audio_bytes_decoded); event->params.SetInteger("video_bytes_decoded", last_statistics_.video_bytes_decoded); event->params.SetInteger("video_frames_decoded", last_statistics_.video_frames_decoded); event->params.SetInteger("video_frames_dropped", last_statistics_.video_frames_dropped); AddEvent(event.release()); stats_update_pending_ = false; } } //namespace media