| // 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<TrackedCallback> 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<PP_VideoFrame_Format>(attrib_list[i + 1]); | 
 |       break; | 
 |     default: | 
 |       return PP_ERROR_BADARGUMENT; | 
 |     } | 
 |   } | 
 |  | 
 |   if (!MediaStreamVideoTrackShared::VerifyAttributes(attributes)) | 
 |     return PP_ERROR_BADARGUMENT; | 
 |  | 
 |   configure_callback_ = callback; | 
 |   Call<PpapiPluginMsg_MediaStreamVideoTrack_ConfigureReply>( | 
 |       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<TrackedCallback> 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<VideoFrameResource> 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<TrackedCallback> 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<TrackedCallback> 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<VideoFrameResource> resource = | 
 |       new VideoFrameResource(pp_instance(), index, buffer); | 
 |   // Add |pp_resource()| and |resource| into |frames_|. | 
 |   // |frames_| uses std::unique_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<TrackedCallback> callback; | 
 |     callback.swap(configure_callback_); | 
 |     callback->Run(params.result()); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace proxy | 
 | }  // namespace ppapi |