// Copyright 2014 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 "ppapi/proxy/media_stream_video_track_resource.h" #include "base/logging.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/video_frame_resource.h" #include "ppapi/shared_impl/media_stream_buffer.h" #include "ppapi/shared_impl/media_stream_video_track_shared.h" #include "ppapi/shared_impl/var.h" namespace ppapi { namespace proxy { MediaStreamVideoTrackResource::MediaStreamVideoTrackResource( Connection connection, PP_Instance instance, int pending_renderer_id, const std::string& id) : MediaStreamTrackResourceBase( connection, instance, pending_renderer_id, id), get_frame_output_(NULL) { } MediaStreamVideoTrackResource::MediaStreamVideoTrackResource( Connection connection, PP_Instance instance) : MediaStreamTrackResourceBase(connection, instance), get_frame_output_(NULL) { SendCreate(RENDERER, PpapiHostMsg_MediaStreamVideoTrack_Create()); } MediaStreamVideoTrackResource::~MediaStreamVideoTrackResource() { Close(); } thunk::PPB_MediaStreamVideoTrack_API* MediaStreamVideoTrackResource::AsPPB_MediaStreamVideoTrack_API() { return this; } PP_Var MediaStreamVideoTrackResource::GetId() { return StringVar::StringToPPVar(id()); } PP_Bool MediaStreamVideoTrackResource::HasEnded() { return PP_FromBool(has_ended()); } int32_t MediaStreamVideoTrackResource::Configure( const int32_t attrib_list[], scoped_refptr callback) { if (has_ended()) return PP_ERROR_FAILED; if (TrackedCallback::IsPending(configure_callback_) || TrackedCallback::IsPending(get_frame_callback_)) { return PP_ERROR_INPROGRESS; } // Do not support configure, if frames are hold by plugin. if (!frames_.empty()) return PP_ERROR_INPROGRESS; MediaStreamVideoTrackShared::Attributes attributes; int i = 0; for (;attrib_list[i] != PP_MEDIASTREAMVIDEOTRACK_ATTRIB_NONE; i += 2) { switch (attrib_list[i]) { case PP_MEDIASTREAMVIDEOTRACK_ATTRIB_BUFFERED_FRAMES: attributes.buffers = attrib_list[i + 1]; break; case PP_MEDIASTREAMVIDEOTRACK_ATTRIB_WIDTH: attributes.width = attrib_list[i + 1]; break; case PP_MEDIASTREAMVIDEOTRACK_ATTRIB_HEIGHT: attributes.height = attrib_list[i + 1]; break; case PP_MEDIASTREAMVIDEOTRACK_ATTRIB_FORMAT: attributes.format = static_cast(attrib_list[i + 1]); break; default: return PP_ERROR_BADARGUMENT; } } if (!MediaStreamVideoTrackShared::VerifyAttributes(attributes)) return PP_ERROR_BADARGUMENT; configure_callback_ = callback; Call( RENDERER, PpapiHostMsg_MediaStreamVideoTrack_Configure(attributes), base::Bind(&MediaStreamVideoTrackResource::OnPluginMsgConfigureReply, base::Unretained(this)), callback); return PP_OK_COMPLETIONPENDING; } int32_t MediaStreamVideoTrackResource::GetAttrib( PP_MediaStreamVideoTrack_Attrib attrib, int32_t* value) { // TODO(penghuang): implement this function. return PP_ERROR_NOTSUPPORTED; } int32_t MediaStreamVideoTrackResource::GetFrame( PP_Resource* frame, scoped_refptr callback) { if (has_ended()) return PP_ERROR_FAILED; if (TrackedCallback::IsPending(configure_callback_) || TrackedCallback::IsPending(get_frame_callback_)) { return PP_ERROR_INPROGRESS; } *frame = GetVideoFrame(); if (*frame) return PP_OK; get_frame_output_ = frame; get_frame_callback_ = callback; return PP_OK_COMPLETIONPENDING; } int32_t MediaStreamVideoTrackResource::RecycleFrame(PP_Resource frame) { FrameMap::iterator it = frames_.find(frame); if (it == frames_.end()) return PP_ERROR_BADRESOURCE; scoped_refptr frame_resource = it->second; frames_.erase(it); if (has_ended()) return PP_OK; DCHECK_GE(frame_resource->GetBufferIndex(), 0); SendEnqueueBufferMessageToHost(frame_resource->GetBufferIndex()); frame_resource->Invalidate(); return PP_OK; } void MediaStreamVideoTrackResource::Close() { if (has_ended()) return; if (TrackedCallback::IsPending(get_frame_callback_)) { *get_frame_output_ = 0; get_frame_callback_->PostAbort(); get_frame_callback_ = NULL; get_frame_output_ = 0; } ReleaseFrames(); MediaStreamTrackResourceBase::CloseInternal(); } int32_t MediaStreamVideoTrackResource::GetEmptyFrame( PP_Resource* frame, scoped_refptr callback) { return GetFrame(frame, callback); } int32_t MediaStreamVideoTrackResource::PutFrame(PP_Resource frame) { // TODO(ronghuawu): Consider to rename RecycleFrame to PutFrame and use // one set of GetFrame and PutFrame for both input and output. return RecycleFrame(frame); } void MediaStreamVideoTrackResource::OnNewBufferEnqueued() { if (!TrackedCallback::IsPending(get_frame_callback_)) return; *get_frame_output_ = GetVideoFrame(); int32_t result = *get_frame_output_ ? PP_OK : PP_ERROR_FAILED; get_frame_output_ = NULL; scoped_refptr callback; callback.swap(get_frame_callback_); callback->Run(result); } PP_Resource MediaStreamVideoTrackResource::GetVideoFrame() { int32_t index = buffer_manager()->DequeueBuffer(); if (index < 0) return 0; MediaStreamBuffer* buffer = buffer_manager()->GetBufferPointer(index); DCHECK(buffer); scoped_refptr resource = new VideoFrameResource(pp_instance(), index, buffer); // Add |pp_resource()| and |resource| into |frames_|. // |frames_| uses scoped_ptr<> to hold a ref of |resource|. It keeps the // resource alive. frames_.insert(FrameMap::value_type(resource->pp_resource(), resource)); return resource->GetReference(); } void MediaStreamVideoTrackResource::ReleaseFrames() { for (FrameMap::iterator it = frames_.begin(); it != frames_.end(); ++it) { // Just invalidate and release VideoFrameResorce, but keep PP_Resource. // So plugin can still use |RecycleFrame()|. it->second->Invalidate(); it->second = NULL; } } void MediaStreamVideoTrackResource::OnPluginMsgConfigureReply( const ResourceMessageReplyParams& params, const std::string& track_id) { if (id().empty()) { set_id(track_id); } else { DCHECK_EQ(id(), track_id); } if (TrackedCallback::IsPending(configure_callback_)) { scoped_refptr callback; callback.swap(configure_callback_); callback->Run(params.result()); } } } // namespace proxy } // namespace ppapi