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_audio_track_resource.h"
6
7#include "ppapi/proxy/audio_buffer_resource.h"
8#include "ppapi/proxy/ppapi_messages.h"
9#include "ppapi/shared_impl/media_stream_audio_track_shared.h"
10#include "ppapi/shared_impl/media_stream_buffer.h"
11#include "ppapi/shared_impl/var.h"
12
13namespace ppapi {
14namespace proxy {
15
16MediaStreamAudioTrackResource::MediaStreamAudioTrackResource(
17    Connection connection,
18    PP_Instance instance,
19    int pending_renderer_id,
20    const std::string& id)
21    : MediaStreamTrackResourceBase(
22        connection, instance, pending_renderer_id, id),
23      get_buffer_output_(NULL) {
24}
25
26MediaStreamAudioTrackResource::~MediaStreamAudioTrackResource() {
27  Close();
28}
29
30thunk::PPB_MediaStreamAudioTrack_API*
31MediaStreamAudioTrackResource::AsPPB_MediaStreamAudioTrack_API() {
32  return this;
33}
34
35PP_Var MediaStreamAudioTrackResource::GetId() {
36  return StringVar::StringToPPVar(id());
37}
38
39PP_Bool MediaStreamAudioTrackResource::HasEnded() {
40  return PP_FromBool(has_ended());
41}
42
43int32_t MediaStreamAudioTrackResource::Configure(
44    const int32_t attrib_list[],
45    scoped_refptr<TrackedCallback> callback) {
46  if (has_ended())
47    return PP_ERROR_FAILED;
48
49  if (TrackedCallback::IsPending(configure_callback_) ||
50      TrackedCallback::IsPending(get_buffer_callback_)) {
51    return PP_ERROR_INPROGRESS;
52  }
53
54  // Do not support configure if audio buffers are held by plugin.
55  if (!buffers_.empty())
56    return PP_ERROR_INPROGRESS;
57
58  MediaStreamAudioTrackShared::Attributes attributes;
59  int i = 0;
60  for (; attrib_list[i] != PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE; i += 2) {
61    switch (attrib_list[i]) {
62      case PP_MEDIASTREAMAUDIOTRACK_ATTRIB_BUFFERS:
63        attributes.buffers = attrib_list[i + 1];
64        break;
65      case PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION:
66        attributes.duration = attrib_list[i + 1];
67        break;
68      case PP_MEDIASTREAMAUDIOTRACK_ATTRIB_SAMPLE_RATE:
69      case PP_MEDIASTREAMAUDIOTRACK_ATTRIB_SAMPLE_SIZE:
70      case PP_MEDIASTREAMAUDIOTRACK_ATTRIB_CHANNELS:
71        return PP_ERROR_NOTSUPPORTED;
72      default:
73        return PP_ERROR_BADARGUMENT;
74    }
75  }
76
77  if (!MediaStreamAudioTrackShared::VerifyAttributes(attributes))
78    return PP_ERROR_BADARGUMENT;
79
80  configure_callback_ = callback;
81  Call<PpapiPluginMsg_MediaStreamAudioTrack_ConfigureReply>(
82      RENDERER,
83      PpapiHostMsg_MediaStreamAudioTrack_Configure(attributes),
84      base::Bind(&MediaStreamAudioTrackResource::OnPluginMsgConfigureReply,
85                 base::Unretained(this)),
86      callback);
87  return PP_OK_COMPLETIONPENDING;
88}
89
90int32_t MediaStreamAudioTrackResource::GetAttrib(
91    PP_MediaStreamAudioTrack_Attrib attrib,
92    int32_t* value) {
93  // TODO(penghuang): Implement this function.
94  return PP_ERROR_NOTSUPPORTED;
95}
96
97int32_t MediaStreamAudioTrackResource::GetBuffer(
98    PP_Resource* buffer,
99    scoped_refptr<TrackedCallback> callback) {
100  if (has_ended())
101    return PP_ERROR_FAILED;
102
103  if (TrackedCallback::IsPending(configure_callback_) ||
104      TrackedCallback::IsPending(get_buffer_callback_))
105    return PP_ERROR_INPROGRESS;
106
107  *buffer = GetAudioBuffer();
108  if (*buffer)
109    return PP_OK;
110
111  // TODO(penghuang): Use the callback as hints to determine which thread will
112  // use the resource, so we could deliver buffers to the target thread directly
113  // for better performance.
114  get_buffer_output_ = buffer;
115  get_buffer_callback_ = callback;
116  return PP_OK_COMPLETIONPENDING;
117}
118
119int32_t MediaStreamAudioTrackResource::RecycleBuffer(PP_Resource buffer) {
120  BufferMap::iterator it = buffers_.find(buffer);
121  if (it == buffers_.end())
122    return PP_ERROR_BADRESOURCE;
123
124  scoped_refptr<AudioBufferResource> buffer_resource = it->second;
125  buffers_.erase(it);
126
127  if (has_ended())
128    return PP_OK;
129
130  DCHECK_GE(buffer_resource->GetBufferIndex(), 0);
131
132  SendEnqueueBufferMessageToHost(buffer_resource->GetBufferIndex());
133  buffer_resource->Invalidate();
134  return PP_OK;
135}
136
137void MediaStreamAudioTrackResource::Close() {
138  if (has_ended())
139    return;
140
141  if (TrackedCallback::IsPending(get_buffer_callback_)) {
142    *get_buffer_output_ = 0;
143    get_buffer_callback_->PostAbort();
144    get_buffer_callback_ = NULL;
145    get_buffer_output_ = 0;
146  }
147
148  ReleaseBuffers();
149  MediaStreamTrackResourceBase::CloseInternal();
150}
151
152void MediaStreamAudioTrackResource::OnNewBufferEnqueued() {
153  if (!TrackedCallback::IsPending(get_buffer_callback_))
154    return;
155
156  *get_buffer_output_ = GetAudioBuffer();
157  int32_t result = *get_buffer_output_ ? PP_OK : PP_ERROR_FAILED;
158  get_buffer_output_ = NULL;
159  scoped_refptr<TrackedCallback> callback;
160  callback.swap(get_buffer_callback_);
161  callback->Run(result);
162}
163
164PP_Resource MediaStreamAudioTrackResource::GetAudioBuffer() {
165  int32_t index = buffer_manager()->DequeueBuffer();
166  if (index < 0)
167      return 0;
168
169  MediaStreamBuffer* buffer = buffer_manager()->GetBufferPointer(index);
170  DCHECK(buffer);
171  scoped_refptr<AudioBufferResource> resource =
172      new AudioBufferResource(pp_instance(), index, buffer);
173  // Add |pp_resource()| and |resource| into |buffers_|.
174  // |buffers_| uses scoped_ptr<> to hold a ref of |resource|. It keeps the
175  // resource alive.
176  buffers_.insert(BufferMap::value_type(resource->pp_resource(), resource));
177  return resource->GetReference();
178}
179
180void MediaStreamAudioTrackResource::ReleaseBuffers() {
181  for (BufferMap::iterator it = buffers_.begin(); it != buffers_.end(); ++it) {
182    // Just invalidate and release VideoBufferResorce, but keep PP_Resource.
183    // So plugin can still use |RecycleBuffer()|.
184    it->second->Invalidate();
185    it->second = NULL;
186  }
187}
188
189void MediaStreamAudioTrackResource::OnPluginMsgConfigureReply(
190    const ResourceMessageReplyParams& params) {
191  if (TrackedCallback::IsPending(configure_callback_)) {
192    scoped_refptr<TrackedCallback> callback;
193    callback.swap(configure_callback_);
194    callback->Run(params.result());
195  }
196}
197
198}  // namespace proxy
199}  // namespace ppapi
200