1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ppapi/proxy/media_stream_video_track_resource.h"
6
7#include "base/logging.h"
8#include "ppapi/proxy/ppapi_messages.h"
9#include "ppapi/proxy/video_frame_resource.h"
10#include "ppapi/shared_impl/media_stream_buffer.h"
11#include "ppapi/shared_impl/media_stream_video_track_shared.h"
12#include "ppapi/shared_impl/var.h"
13
14namespace ppapi {
15namespace proxy {
16
17MediaStreamVideoTrackResource::MediaStreamVideoTrackResource(
18    Connection connection,
19    PP_Instance instance,
20    int pending_renderer_id,
21    const std::string& id)
22    : MediaStreamTrackResourceBase(
23        connection, instance, pending_renderer_id, id),
24      get_frame_output_(NULL) {
25}
26
27MediaStreamVideoTrackResource::MediaStreamVideoTrackResource(
28    Connection connection,
29    PP_Instance instance)
30    : MediaStreamTrackResourceBase(connection, instance),
31      get_frame_output_(NULL) {
32  SendCreate(RENDERER, PpapiHostMsg_MediaStreamVideoTrack_Create());
33}
34
35MediaStreamVideoTrackResource::~MediaStreamVideoTrackResource() {
36  Close();
37}
38
39thunk::PPB_MediaStreamVideoTrack_API*
40MediaStreamVideoTrackResource::AsPPB_MediaStreamVideoTrack_API() {
41  return this;
42}
43
44PP_Var MediaStreamVideoTrackResource::GetId() {
45  return StringVar::StringToPPVar(id());
46}
47
48PP_Bool MediaStreamVideoTrackResource::HasEnded() {
49  return PP_FromBool(has_ended());
50}
51
52int32_t MediaStreamVideoTrackResource::Configure(
53    const int32_t attrib_list[],
54    scoped_refptr<TrackedCallback> callback) {
55  if (has_ended())
56    return PP_ERROR_FAILED;
57
58  if (TrackedCallback::IsPending(configure_callback_) ||
59      TrackedCallback::IsPending(get_frame_callback_)) {
60    return PP_ERROR_INPROGRESS;
61  }
62
63  // Do not support configure, if frames are hold by plugin.
64  if (!frames_.empty())
65    return PP_ERROR_INPROGRESS;
66
67  MediaStreamVideoTrackShared::Attributes attributes;
68  int i = 0;
69  for (;attrib_list[i] != PP_MEDIASTREAMVIDEOTRACK_ATTRIB_NONE; i += 2) {
70    switch (attrib_list[i]) {
71    case PP_MEDIASTREAMVIDEOTRACK_ATTRIB_BUFFERED_FRAMES:
72      attributes.buffers = attrib_list[i + 1];
73      break;
74    case PP_MEDIASTREAMVIDEOTRACK_ATTRIB_WIDTH:
75      attributes.width = attrib_list[i + 1];
76      break;
77    case PP_MEDIASTREAMVIDEOTRACK_ATTRIB_HEIGHT:
78      attributes.height = attrib_list[i + 1];
79      break;
80    case PP_MEDIASTREAMVIDEOTRACK_ATTRIB_FORMAT:
81      attributes.format = static_cast<PP_VideoFrame_Format>(attrib_list[i + 1]);
82      break;
83    default:
84      return PP_ERROR_BADARGUMENT;
85    }
86  }
87
88  if (!MediaStreamVideoTrackShared::VerifyAttributes(attributes))
89    return PP_ERROR_BADARGUMENT;
90
91  configure_callback_ = callback;
92  Call<PpapiPluginMsg_MediaStreamVideoTrack_ConfigureReply>(
93      RENDERER,
94      PpapiHostMsg_MediaStreamVideoTrack_Configure(attributes),
95      base::Bind(&MediaStreamVideoTrackResource::OnPluginMsgConfigureReply,
96                 base::Unretained(this)),
97      callback);
98  return PP_OK_COMPLETIONPENDING;
99}
100
101int32_t MediaStreamVideoTrackResource::GetAttrib(
102    PP_MediaStreamVideoTrack_Attrib attrib,
103    int32_t* value) {
104  // TODO(penghuang): implement this function.
105  return PP_ERROR_NOTSUPPORTED;
106}
107
108int32_t MediaStreamVideoTrackResource::GetFrame(
109    PP_Resource* frame,
110    scoped_refptr<TrackedCallback> callback) {
111  if (has_ended())
112    return PP_ERROR_FAILED;
113
114  if (TrackedCallback::IsPending(configure_callback_) ||
115      TrackedCallback::IsPending(get_frame_callback_)) {
116    return PP_ERROR_INPROGRESS;
117  }
118
119  *frame = GetVideoFrame();
120  if (*frame)
121    return PP_OK;
122
123  get_frame_output_ = frame;
124  get_frame_callback_ = callback;
125  return PP_OK_COMPLETIONPENDING;
126}
127
128int32_t MediaStreamVideoTrackResource::RecycleFrame(PP_Resource frame) {
129  FrameMap::iterator it = frames_.find(frame);
130  if (it == frames_.end())
131    return PP_ERROR_BADRESOURCE;
132
133  scoped_refptr<VideoFrameResource> frame_resource = it->second;
134  frames_.erase(it);
135
136  if (has_ended())
137    return PP_OK;
138
139  DCHECK_GE(frame_resource->GetBufferIndex(), 0);
140
141  SendEnqueueBufferMessageToHost(frame_resource->GetBufferIndex());
142  frame_resource->Invalidate();
143  return PP_OK;
144}
145
146void MediaStreamVideoTrackResource::Close() {
147  if (has_ended())
148    return;
149
150  if (TrackedCallback::IsPending(get_frame_callback_)) {
151    *get_frame_output_ = 0;
152    get_frame_callback_->PostAbort();
153    get_frame_callback_ = NULL;
154    get_frame_output_ = 0;
155  }
156
157  ReleaseFrames();
158  MediaStreamTrackResourceBase::CloseInternal();
159}
160
161int32_t MediaStreamVideoTrackResource::GetEmptyFrame(
162    PP_Resource* frame, scoped_refptr<TrackedCallback> callback) {
163  return GetFrame(frame, callback);
164}
165
166int32_t MediaStreamVideoTrackResource::PutFrame(PP_Resource frame) {
167  // TODO(ronghuawu): Consider to rename RecycleFrame to PutFrame and use
168  // one set of GetFrame and PutFrame for both input and output.
169  return RecycleFrame(frame);
170}
171
172void MediaStreamVideoTrackResource::OnNewBufferEnqueued() {
173  if (!TrackedCallback::IsPending(get_frame_callback_))
174    return;
175
176  *get_frame_output_ = GetVideoFrame();
177  int32_t result = *get_frame_output_ ? PP_OK : PP_ERROR_FAILED;
178  get_frame_output_ = NULL;
179  scoped_refptr<TrackedCallback> callback;
180  callback.swap(get_frame_callback_);
181  callback->Run(result);
182}
183
184PP_Resource MediaStreamVideoTrackResource::GetVideoFrame() {
185  int32_t index = buffer_manager()->DequeueBuffer();
186  if (index < 0)
187    return 0;
188
189  MediaStreamBuffer* buffer = buffer_manager()->GetBufferPointer(index);
190  DCHECK(buffer);
191  scoped_refptr<VideoFrameResource> resource =
192      new VideoFrameResource(pp_instance(), index, buffer);
193  // Add |pp_resource()| and |resource| into |frames_|.
194  // |frames_| uses scoped_ptr<> to hold a ref of |resource|. It keeps the
195  // resource alive.
196  frames_.insert(FrameMap::value_type(resource->pp_resource(), resource));
197  return resource->GetReference();
198}
199
200void MediaStreamVideoTrackResource::ReleaseFrames() {
201  for (FrameMap::iterator it = frames_.begin(); it != frames_.end(); ++it) {
202    // Just invalidate and release VideoFrameResorce, but keep PP_Resource.
203    // So plugin can still use |RecycleFrame()|.
204    it->second->Invalidate();
205    it->second = NULL;
206  }
207}
208
209void MediaStreamVideoTrackResource::OnPluginMsgConfigureReply(
210    const ResourceMessageReplyParams& params,
211    const std::string& track_id) {
212  if (id().empty()) {
213    set_id(track_id);
214  } else {
215    DCHECK_EQ(id(), track_id);
216  }
217  if (TrackedCallback::IsPending(configure_callback_)) {
218    scoped_refptr<TrackedCallback> callback;
219    callback.swap(configure_callback_);
220    callback->Run(params.result());
221  }
222}
223
224}  // namespace proxy
225}  // namespace ppapi
226