// 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_audio_track_resource.h" #include "ppapi/proxy/audio_buffer_resource.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/shared_impl/media_stream_audio_track_shared.h" #include "ppapi/shared_impl/media_stream_buffer.h" #include "ppapi/shared_impl/var.h" namespace ppapi { namespace proxy { MediaStreamAudioTrackResource::MediaStreamAudioTrackResource( Connection connection, PP_Instance instance, int pending_renderer_id, const std::string& id) : MediaStreamTrackResourceBase( connection, instance, pending_renderer_id, id), get_buffer_output_(NULL) { } MediaStreamAudioTrackResource::~MediaStreamAudioTrackResource() { Close(); } thunk::PPB_MediaStreamAudioTrack_API* MediaStreamAudioTrackResource::AsPPB_MediaStreamAudioTrack_API() { return this; } PP_Var MediaStreamAudioTrackResource::GetId() { return StringVar::StringToPPVar(id()); } PP_Bool MediaStreamAudioTrackResource::HasEnded() { return PP_FromBool(has_ended()); } int32_t MediaStreamAudioTrackResource::Configure( const int32_t attrib_list[], scoped_refptr callback) { if (has_ended()) return PP_ERROR_FAILED; if (TrackedCallback::IsPending(configure_callback_) || TrackedCallback::IsPending(get_buffer_callback_)) { return PP_ERROR_INPROGRESS; } // Do not support configure if audio buffers are held by plugin. if (!buffers_.empty()) return PP_ERROR_INPROGRESS; MediaStreamAudioTrackShared::Attributes attributes; int i = 0; for (; attrib_list[i] != PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE; i += 2) { switch (attrib_list[i]) { case PP_MEDIASTREAMAUDIOTRACK_ATTRIB_BUFFERS: attributes.buffers = attrib_list[i + 1]; break; case PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION: attributes.duration = attrib_list[i + 1]; break; case PP_MEDIASTREAMAUDIOTRACK_ATTRIB_SAMPLE_RATE: case PP_MEDIASTREAMAUDIOTRACK_ATTRIB_SAMPLE_SIZE: case PP_MEDIASTREAMAUDIOTRACK_ATTRIB_CHANNELS: return PP_ERROR_NOTSUPPORTED; default: return PP_ERROR_BADARGUMENT; } } if (!MediaStreamAudioTrackShared::VerifyAttributes(attributes)) return PP_ERROR_BADARGUMENT; configure_callback_ = callback; Call( RENDERER, PpapiHostMsg_MediaStreamAudioTrack_Configure(attributes), base::Bind(&MediaStreamAudioTrackResource::OnPluginMsgConfigureReply, base::Unretained(this)), callback); return PP_OK_COMPLETIONPENDING; } int32_t MediaStreamAudioTrackResource::GetAttrib( PP_MediaStreamAudioTrack_Attrib attrib, int32_t* value) { // TODO(penghuang): Implement this function. return PP_ERROR_NOTSUPPORTED; } int32_t MediaStreamAudioTrackResource::GetBuffer( PP_Resource* buffer, scoped_refptr callback) { if (has_ended()) return PP_ERROR_FAILED; if (TrackedCallback::IsPending(configure_callback_) || TrackedCallback::IsPending(get_buffer_callback_)) return PP_ERROR_INPROGRESS; *buffer = GetAudioBuffer(); if (*buffer) return PP_OK; // TODO(penghuang): Use the callback as hints to determine which thread will // use the resource, so we could deliver buffers to the target thread directly // for better performance. get_buffer_output_ = buffer; get_buffer_callback_ = callback; return PP_OK_COMPLETIONPENDING; } int32_t MediaStreamAudioTrackResource::RecycleBuffer(PP_Resource buffer) { BufferMap::iterator it = buffers_.find(buffer); if (it == buffers_.end()) return PP_ERROR_BADRESOURCE; scoped_refptr buffer_resource = it->second; buffers_.erase(it); if (has_ended()) return PP_OK; DCHECK_GE(buffer_resource->GetBufferIndex(), 0); SendEnqueueBufferMessageToHost(buffer_resource->GetBufferIndex()); buffer_resource->Invalidate(); return PP_OK; } void MediaStreamAudioTrackResource::Close() { if (has_ended()) return; if (TrackedCallback::IsPending(get_buffer_callback_)) { *get_buffer_output_ = 0; get_buffer_callback_->PostAbort(); get_buffer_callback_ = NULL; get_buffer_output_ = 0; } ReleaseBuffers(); MediaStreamTrackResourceBase::CloseInternal(); } void MediaStreamAudioTrackResource::OnNewBufferEnqueued() { if (!TrackedCallback::IsPending(get_buffer_callback_)) return; *get_buffer_output_ = GetAudioBuffer(); int32_t result = *get_buffer_output_ ? PP_OK : PP_ERROR_FAILED; get_buffer_output_ = NULL; scoped_refptr callback; callback.swap(get_buffer_callback_); callback->Run(result); } PP_Resource MediaStreamAudioTrackResource::GetAudioBuffer() { int32_t index = buffer_manager()->DequeueBuffer(); if (index < 0) return 0; MediaStreamBuffer* buffer = buffer_manager()->GetBufferPointer(index); DCHECK(buffer); scoped_refptr resource = new AudioBufferResource(pp_instance(), index, buffer); // Add |pp_resource()| and |resource| into |buffers_|. // |buffers_| uses scoped_ptr<> to hold a ref of |resource|. It keeps the // resource alive. buffers_.insert(BufferMap::value_type(resource->pp_resource(), resource)); return resource->GetReference(); } void MediaStreamAudioTrackResource::ReleaseBuffers() { for (BufferMap::iterator it = buffers_.begin(); it != buffers_.end(); ++it) { // Just invalidate and release VideoBufferResorce, but keep PP_Resource. // So plugin can still use |RecycleBuffer()|. it->second->Invalidate(); it->second = NULL; } } void MediaStreamAudioTrackResource::OnPluginMsgConfigureReply( const ResourceMessageReplyParams& params) { if (TrackedCallback::IsPending(configure_callback_)) { scoped_refptr callback; callback.swap(configure_callback_); callback->Run(params.result()); } } } // namespace proxy } // namespace ppapi