diff options
author | neb@chromium.org <neb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-25 23:25:16 +0000 |
---|---|---|
committer | neb@chromium.org <neb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-25 23:25:16 +0000 |
commit | 9e7e0e0c96dcd2773747077ac1041913e73a481a (patch) | |
tree | 715cf95cf9de7f75fd0eca1846cfb2c346c462eb | |
parent | 1e730344d06a78c40062912a50aa3b4e61fc160d (diff) | |
download | chromium_src-9e7e0e0c96dcd2773747077ac1041913e73a481a.zip chromium_src-9e7e0e0c96dcd2773747077ac1041913e73a481a.tar.gz chromium_src-9e7e0e0c96dcd2773747077ac1041913e73a481a.tar.bz2 |
Refactor Pepper device API
Track Pepper DeviceContexts using ID (and reclaim them upon shutdown)
Refactor device-specific code into pepper_devices.h
Review URL: http://codereview.chromium.org/548100
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@37060 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/id_map.h | 62 | ||||
-rw-r--r-- | base/id_map_unittest.cc | 80 | ||||
-rwxr-xr-x | chrome/chrome_renderer.gypi | 2 | ||||
-rw-r--r-- | chrome/renderer/pepper_devices.cc | 172 | ||||
-rw-r--r-- | chrome/renderer/pepper_devices.h | 77 | ||||
-rw-r--r-- | chrome/renderer/webplugin_delegate_pepper.cc | 230 | ||||
-rw-r--r-- | chrome/renderer/webplugin_delegate_pepper.h | 64 | ||||
-rw-r--r-- | third_party/npapi/bindings/npapi_extensions.h | 5 |
8 files changed, 424 insertions, 268 deletions
diff --git a/base/id_map.h b/base/id_map.h index 13fb2f2..b108dbd 100644 --- a/base/id_map.h +++ b/base/id_map.h @@ -11,6 +11,13 @@ #include "base/hash_tables.h" #include "base/logging.h" +// Ownership semantics - own pointer means the pointer is deleted in Remove() +// & during destruction +enum IDMapOwnershipSemantics { + IDMapExternalPointer, + IDMapOwnPointer +}; + // This object maintains a list of IDs that can be quickly converted to // pointers to objects. It is implemented as a hash table, optimized for // relatively small data sets (in the common case, there will be exactly one @@ -19,22 +26,30 @@ // Items can be inserted into the container with arbitrary ID, but the caller // must ensure they are unique. Inserting IDs and relying on automatically // generated ones is not allowed because they can collide. -template<class T> +// +// This class does not have a virtual destructor, do not inherit from it when +// ownership semantics are set to own because pointers will leak. +template<typename T, IDMapOwnershipSemantics OS = IDMapExternalPointer> class IDMap { private: - typedef base::hash_map<int32, T*> HashTable; + typedef int32 KeyType; + typedef base::hash_map<KeyType, T*> HashTable; public: IDMap() : iteration_depth_(0), next_id_(1), check_on_null_data_(false) { } + ~IDMap() { + Releaser<OS, 0>::release_all(&data_); + } + // Sets whether Add should CHECK if passed in NULL data. Default is false. void set_check_on_null_data(bool value) { check_on_null_data_ = value; } // Adds a view with an automatically generated unique ID. See AddWithID. - int32 Add(T* data) { + KeyType Add(T* data) { CHECK(!check_on_null_data_ || data); - int32 this_id = next_id_; + KeyType this_id = next_id_; DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item"; data_[this_id] = data; next_id_++; @@ -45,30 +60,32 @@ class IDMap { // the list. The caller either must generate all unique IDs itself and use // this function, or allow this object to generate IDs and call Add. These // two methods may not be mixed, or duplicate IDs may be generated - void AddWithID(T* data, int32 id) { + void AddWithID(T* data, KeyType id) { CHECK(!check_on_null_data_ || data); DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item"; data_[id] = data; } - void Remove(int32 id) { + void Remove(KeyType id) { typename HashTable::iterator i = data_.find(id); if (i == data_.end()) { NOTREACHED() << "Attempting to remove an item not in the list"; return; } - if (iteration_depth_ == 0) + if (iteration_depth_ == 0) { + Releaser<OS, 0>::release(i->second); data_.erase(i); - else + } else { removed_ids_.insert(id); + } } bool IsEmpty() const { return data_.empty(); } - T* Lookup(int32 id) const { + T* Lookup(KeyType id) const { typename HashTable::const_iterator i = data_.find(id); if (i == data_.end()) return NULL; @@ -100,7 +117,7 @@ class IDMap { return iter_ == map_->data_.end(); } - int32 GetCurrentKey() const { + KeyType GetCurrentKey() const { return iter_->first; } @@ -130,9 +147,28 @@ class IDMap { typedef Iterator<const T> const_iterator; private: + + // The dummy parameter is there because C++ standard does not allow + // explicitly specialized templates inside classes + template<IDMapOwnershipSemantics OI, int dummy> struct Releaser { + static inline void release(T* ptr) {} + static inline void release_all(HashTable* table) {} + }; + + template<int dummy> struct Releaser<IDMapOwnPointer, dummy> { + static inline void release(T* ptr) { delete ptr;} + static inline void release_all(HashTable* table) { + for (typename HashTable::iterator i = table->begin(); + i != table->end(); ++i) { + delete i->second; + } + table->clear(); + } + }; + void Compact() { DCHECK_EQ(0, iteration_depth_); - for (std::set<int32>::const_iterator i = removed_ids_.begin(); + for (std::set<KeyType>::const_iterator i = removed_ids_.begin(); i != removed_ids_.end(); ++i) { Remove(*i); } @@ -146,10 +182,10 @@ class IDMap { // Keep set of IDs that should be removed after the outermost iteration has // finished. This way we manage to not invalidate the iterator when an element // is removed. - std::set<int32> removed_ids_; + std::set<KeyType> removed_ids_; // The next ID that we will return from Add() - int32 next_id_; + KeyType next_id_; HashTable data_; diff --git a/base/id_map_unittest.cc b/base/id_map_unittest.cc index 7b12e35..58abf96 100644 --- a/base/id_map_unittest.cc +++ b/base/id_map_unittest.cc @@ -14,6 +14,14 @@ class IDMapTest : public testing::Test { class TestObject { }; +class DestructorCounter { + public: + explicit DestructorCounter(int* counter) : counter_(counter) {} + ~DestructorCounter() { ++(*counter_); } + private: + int* counter_; +}; + TEST_F(IDMapTest, Basic) { IDMap<TestObject> map; EXPECT_TRUE(map.IsEmpty()); @@ -103,4 +111,76 @@ TEST_F(IDMapTest, IteratorRemainsValidWhenRemovingOtherElements) { } } +TEST_F(IDMapTest, OwningPointersDeletesThemOnRemove) { + const int kCount = 3; + + int external_del_count = 0; + DestructorCounter* external_obj[kCount]; + int map_external_ids[kCount]; + + int owned_del_count = 0; + DestructorCounter* owned_obj[kCount]; + int map_owned_ids[kCount]; + + IDMap<DestructorCounter> map_external; + IDMap<DestructorCounter, IDMapOwnPointer> map_owned; + + for (int i = 0; i < kCount; ++i) { + external_obj[i] = new DestructorCounter(&external_del_count); + map_external_ids[i] = map_external.Add(external_obj[i]); + + owned_obj[i] = new DestructorCounter(&owned_del_count); + map_owned_ids[i] = map_owned.Add(owned_obj[i]); + } + + for (int i = 0; i < kCount; ++i) { + EXPECT_EQ(external_del_count, 0); + EXPECT_EQ(owned_del_count, i); + + map_external.Remove(map_external_ids[i]); + map_owned.Remove(map_owned_ids[i]); + } + + for (int i = 0; i < kCount; ++i) { + delete external_obj[i]; + } + + EXPECT_EQ(external_del_count, kCount); + EXPECT_EQ(owned_del_count, kCount); +} + +TEST_F(IDMapTest, OwningPointersDeletesThemOnDestruct) { + const int kCount = 3; + + int external_del_count = 0; + DestructorCounter* external_obj[kCount]; + int map_external_ids[kCount]; + + int owned_del_count = 0; + DestructorCounter* owned_obj[kCount]; + int map_owned_ids[kCount]; + + { + IDMap<DestructorCounter> map_external; + IDMap<DestructorCounter, IDMapOwnPointer> map_owned; + + for (int i = 0; i < kCount; ++i) { + external_obj[i] = new DestructorCounter(&external_del_count); + map_external_ids[i] = map_external.Add(external_obj[i]); + + owned_obj[i] = new DestructorCounter(&owned_del_count); + map_owned_ids[i] = map_owned.Add(owned_obj[i]); + } + } + + EXPECT_EQ(external_del_count, 0); + + for (int i = 0; i < kCount; ++i) { + delete external_obj[i]; + } + + EXPECT_EQ(external_del_count, kCount); + EXPECT_EQ(owned_del_count, kCount); +} + } // namespace diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index ea95478..a15c82b 100755 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -87,6 +87,8 @@ 'renderer/notification_provider.cc', 'renderer/notification_provider.h', 'renderer/paint_aggregator.cc', + 'renderer/pepper_devices.cc', + 'renderer/pepper_devices.h', 'renderer/plugin_channel_host.cc', 'renderer/plugin_channel_host.h', 'renderer/print_web_view_helper.cc', diff --git a/chrome/renderer/pepper_devices.cc b/chrome/renderer/pepper_devices.cc new file mode 100644 index 0000000..ff39088 --- /dev/null +++ b/chrome/renderer/pepper_devices.cc @@ -0,0 +1,172 @@ +// Copyright (c) 2010 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 "chrome/renderer/pepper_devices.h" +#include "skia/ext/platform_canvas.h" + +namespace { + const uint32 kBytesPerPixel = 4; // Only 8888 RGBA for now. +} // namespace + +int Graphics2DDeviceContext::next_buffer_id_ = 0; + +NPError Graphics2DDeviceContext::Initialize( + gfx::Rect window_rect, const NPDeviceContext2DConfig* config, + NPDeviceContext2D* context) { + int width = window_rect.width(); + int height = window_rect.height(); + uint32 buffer_size = width * height * kBytesPerPixel; + + // Allocate the transport DIB and the PlatformCanvas pointing to it. + transport_dib_.reset(TransportDIB::Create(buffer_size, ++next_buffer_id_)); + if (!transport_dib_.get()) + return NPERR_OUT_OF_MEMORY_ERROR; + canvas_.reset(transport_dib_->GetPlatformCanvas(width, height)); + if (!canvas_.get()) + return NPERR_OUT_OF_MEMORY_ERROR; + + // Note that we need to get the address out of the bitmap rather than + // using plugin_buffer_->memory(). The memory() is when the bitmap data + // has had "Map" called on it. For Windows, this is separate than making a + // bitmap using the shared section. + const SkBitmap& plugin_bitmap = + canvas_->getTopPlatformDevice().accessBitmap(true); + SkAutoLockPixels locker(plugin_bitmap); + + // TODO(brettw) this theoretically shouldn't be necessary. But the + // platform device on Windows will fill itself with green to help you + // catch areas you didn't paint. + plugin_bitmap.eraseARGB(0, 0, 0, 0); + + // Save the canvas to the output context structure and save the + // OpenPaintContext for future reference. + context->region = plugin_bitmap.getAddr32(0, 0); + context->stride = width * kBytesPerPixel; + context->dirty.left = 0; + context->dirty.top = 0; + context->dirty.right = width; + context->dirty.bottom = height; + return NPERR_NO_ERROR; +} + +NPError Graphics2DDeviceContext::Flush(SkBitmap* committed_bitmap, + NPDeviceContext2D* context, + NPDeviceFlushContextCallbackPtr callback, + NPP id, void* user_data) { + // Draw the bitmap to the backing store. + // + // TODO(brettw) we can optimize this in the case where the entire canvas is + // updated by actually taking ownership of the buffer and not telling the + // plugin we're done using it. This wat we can avoid the copy when the entire + // canvas has been updated. + SkIRect src_rect = { context->dirty.left, + context->dirty.top, + context->dirty.right, + context->dirty.bottom }; + SkRect dest_rect = { SkIntToScalar(context->dirty.left), + SkIntToScalar(context->dirty.top), + SkIntToScalar(context->dirty.right), + SkIntToScalar(context->dirty.bottom) }; + SkCanvas committed_canvas(*committed_bitmap); + + // We want to replace the contents of the bitmap rather than blend. + SkPaint paint; + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + committed_canvas.drawBitmapRect( + canvas_->getTopPlatformDevice().accessBitmap(false), + &src_rect, dest_rect); + + committed_bitmap->setIsOpaque(false); + + // Invoke the callback to inform the caller the work was done. + // TODO(brettw) this is not how we want this to work, this should + // happen when the frame is painted so the plugin knows when it can draw + // the next frame. + // + // This should also be called in the failure cases as well. + if (callback != NULL) + (*callback)(id, context, NPERR_NO_ERROR, user_data); + + return NPERR_NO_ERROR; +} + + +AudioDeviceContext::~AudioDeviceContext() { + if (stream_id_) { + OnDestroy(); + } +} + +NPError AudioDeviceContext::Initialize( + AudioMessageFilter* filter, const NPDeviceContextAudioConfig* config, + NPDeviceContextAudio* context) { + filter_ = filter; + context_= context; + // Make sure we don't call init more than once. + DCHECK_EQ(0, stream_id_); + stream_id_ = filter_->AddDelegate(this); + + ViewHostMsg_Audio_CreateStream params; + params.format = AudioManager::AUDIO_PCM_LINEAR; + params.channels = config->outputChannelMap; + params.sample_rate = config->sampleRate; + switch (config->sampleType) { + case NPAudioSampleTypeInt16: + params.bits_per_sample = 16; + break; + case NPAudioSampleTypeFloat32: + params.bits_per_sample = 32; + break; + default: + return NPERR_INVALID_PARAM; + } + + context->config = *config; + params.packet_size = config->sampleFrameCount * config->outputChannelMap + * (params.bits_per_sample >> 3); + + // TODO(neb): figure out if this number is grounded in reality + params.buffer_capacity = params.packet_size * 3; + + LOG(INFO) << "Initializing Pepper Audio Context (" << + config->sampleFrameCount << "Hz, " << params.bits_per_sample << + " bits, " << config->outputChannelMap << "channels"; + + filter->Send(new ViewHostMsg_CreateAudioStream(0, stream_id_, params)); + return NPERR_NO_ERROR; +} + +void AudioDeviceContext::OnDestroy() { + // Make sure we don't call destroy more than once. + DCHECK_NE(0, stream_id_); + filter_->RemoveDelegate(stream_id_); + filter_->Send(new ViewHostMsg_CloseAudioStream(0, stream_id_)); + stream_id_ = 0; +} + +void AudioDeviceContext::OnRequestPacket( + size_t bytes_in_buffer, const base::Time& message_timestamp) { + context_->config.callback(context_); + filter_->Send(new ViewHostMsg_NotifyAudioPacketReady(0, stream_id_, + shared_memory_size_)); +} + +void AudioDeviceContext::OnStateChanged( + ViewMsg_AudioStreamState state) { +} + +void AudioDeviceContext::OnCreated( + base::SharedMemoryHandle handle, size_t length) { + shared_memory_.reset(new base::SharedMemory(handle, false)); + shared_memory_->Map(length); + shared_memory_size_ = length; + + context_->outBuffer = shared_memory_->memory(); + // TODO(neb): call play after prefilling + filter_->Send(new ViewHostMsg_PlayAudioStream(0, stream_id_)); +} + +void AudioDeviceContext::OnVolume(double volume) { +} + diff --git a/chrome/renderer/pepper_devices.h b/chrome/renderer/pepper_devices.h new file mode 100644 index 0000000..50b3013 --- /dev/null +++ b/chrome/renderer/pepper_devices.h @@ -0,0 +1,77 @@ +// Copyright (c) 2010 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. + +#ifndef CHROME_RENDERER_PEPPER_DEVICES_H_ +#define CHROME_RENDERER_PEPPER_DEVICES_H_ + +#include <stdint.h> + +#include "base/scoped_ptr.h" +#include "base/shared_memory.h" +#include "base/gfx/rect.h" +#include "chrome/common/render_messages.h" +#include "chrome/common/transport_dib.h" +#include "chrome/renderer/audio_message_filter.h" +#include "third_party/npapi/bindings/npapi.h" +#include "third_party/npapi/bindings/npapi_extensions.h" +#include "third_party/skia/include/core/SkBitmap.h" + +// Lists all contexts currently open for painting. These are ones requested by +// the plugin but not destroyed by it yet. The source pointer is the raw +// pixels. We use this to look up the corresponding transport DIB when the +// plugin tells us to flush or destroy it. +class Graphics2DDeviceContext { + public: + NPError Initialize(gfx::Rect window_rect, + const NPDeviceContext2DConfig* config, + NPDeviceContext2D* context); + + NPError Flush(SkBitmap* commited_bitmap, NPDeviceContext2D* context, + NPDeviceFlushContextCallbackPtr callback, NPP id, + void* user_data); + + private: + static int32 next_buffer_id_; + scoped_ptr<TransportDIB> transport_dib_; + + // The canvas associated with the transport DIB, containing the mapped + // memory of the image. + scoped_ptr<skia::PlatformCanvas> canvas_; +}; + + +// Each instance of AudioDeviceContext corresponds to one host stream (and one +// audio context). NPDeviceContextAudio contains the id of the context's +// stream in the privatePtr member. +class AudioDeviceContext : public AudioMessageFilter::Delegate { + public: + // TODO(neb): if plugin_delegate parameter is indeed unused, remove it + explicit AudioDeviceContext() : stream_id_(0) { + } + virtual ~AudioDeviceContext(); + + NPError Initialize(AudioMessageFilter* filter, + const NPDeviceContextAudioConfig* config, + NPDeviceContextAudio* context); + + // AudioMessageFilter::Delegate implementation + virtual void OnRequestPacket(size_t bytes_in_buffer, + const base::Time& message_timestamp); + virtual void OnStateChanged(ViewMsg_AudioStreamState state); + virtual void OnCreated(base::SharedMemoryHandle handle, size_t length); + virtual void OnVolume(double volume); + // End of AudioMessageFilter::Delegate implementation + + private: + void OnDestroy(); + + NPDeviceContextAudio* context_; + scoped_refptr<AudioMessageFilter> filter_; + int32 stream_id_; + scoped_ptr<base::SharedMemory> shared_memory_; + size_t shared_memory_size_; +}; + +#endif // CHROME_RENDERER_PEPPER_DEVICES_H_ + diff --git a/chrome/renderer/webplugin_delegate_pepper.cc b/chrome/renderer/webplugin_delegate_pepper.cc index c5b0b14..68e611d 100644 --- a/chrome/renderer/webplugin_delegate_pepper.cc +++ b/chrome/renderer/webplugin_delegate_pepper.cc @@ -42,12 +42,6 @@ using WebKit::WebInputEvent; using WebKit::WebMouseEvent; using WebKit::WebMouseWheelEvent; -namespace { - -const uint32 kBytesPerPixel = 4; // Only 8888 RGBA for now. - -} // namespace - // Implementation artifacts for a context struct Device2DImpl { TransportDIB* dib; @@ -57,8 +51,6 @@ struct Device3DImpl { gpu::CommandBuffer* command_buffer; }; -uint32 WebPluginDelegatePepper::next_buffer_id = 0; - WebPluginDelegatePepper* WebPluginDelegatePepper::Create( const FilePath& filename, const std::string& mime_type, @@ -260,63 +252,23 @@ NPError WebPluginDelegatePepper::Device2DQueryConfig( NPError WebPluginDelegatePepper::Device2DInitializeContext( const NPDeviceContext2DConfig* config, NPDeviceContext2D* context) { + + if (!render_view_) { + return NPERR_GENERIC_ERROR; + } + // This is a windowless plugin, so set it to have a NULL handle. Defer this // until we know the plugin will use the 2D device. If it uses the 3D device // it will have a window handle. plugin_->SetWindow(NULL); - int width = window_rect_.width(); - int height = window_rect_.height(); - uint32 buffer_size = width * height * kBytesPerPixel; - - // Initialize the impelementation information in case of failure. - context->reserved = NULL; - - // Allocate the transport DIB and the PlatformCanvas pointing to it. - scoped_ptr<OpenPaintContext> paint_context(new OpenPaintContext); - paint_context->transport_dib.reset( - TransportDIB::Create(buffer_size, ++next_buffer_id)); - if (!paint_context->transport_dib.get()) - return NPERR_OUT_OF_MEMORY_ERROR; - paint_context->canvas.reset( - paint_context->transport_dib->GetPlatformCanvas(width, height)); - if (!paint_context->canvas.get()) - return NPERR_OUT_OF_MEMORY_ERROR; - - // Note that we need to get the address out of the bitmap rather than - // using plugin_buffer_->memory(). The memory() is when the bitmap data - // has had "Map" called on it. For Windows, this is separate than making a - // bitmap using the shared section. - const SkBitmap& plugin_bitmap = - paint_context->canvas->getTopPlatformDevice().accessBitmap(true); - SkAutoLockPixels locker(plugin_bitmap); - - // TODO(brettw) this theoretically shouldn't be necessary. But the - // platform device on Windows will fill itself with green to help you - // catch areas you didn't paint. - plugin_bitmap.eraseARGB(0, 0, 0, 0); - - // Save the implementation information (the TransportDIB). - Device2DImpl* impl = new Device2DImpl; - if (impl == NULL) { - // TODO(sehr,brettw): cleanup the context if we fail. - return NPERR_GENERIC_ERROR; + scoped_ptr<Graphics2DDeviceContext> g2d(new Graphics2DDeviceContext()); + NPError status = g2d->Initialize(window_rect_, config, context); + if (NPERR_NO_ERROR == status) { + context->reserved = reinterpret_cast<void *>( + graphic2d_contexts_.Add(g2d.release())); } - impl->dib = paint_context->transport_dib.get(); - context->reserved = reinterpret_cast<void*>(impl); - - // Save the canvas to the output context structure and save the - // OpenPaintContext for future reference. - context->region = plugin_bitmap.getAddr32(0, 0); - context->stride = width * kBytesPerPixel; - context->dirty.left = 0; - context->dirty.top = 0; - context->dirty.right = width; - context->dirty.bottom = height; - open_paint_contexts_[context->region] = - linked_ptr<OpenPaintContext>(paint_context.release()); - - return NPERR_NO_ERROR; + return status; } NPError WebPluginDelegatePepper::Device2DSetStateContext( @@ -338,61 +290,28 @@ NPError WebPluginDelegatePepper::Device2DFlushContext( NPDeviceContext2D* context, NPDeviceFlushContextCallbackPtr callback, void* user_data) { - // Get the bitmap data associated with the incoming context. - OpenPaintContextMap::iterator found = open_paint_contexts_.find( - context->region); - if (found == open_paint_contexts_.end()) - return NPERR_INVALID_PARAM; // TODO(brettw) call callback. - OpenPaintContext* paint_context = found->second.get(); - - // Draw the bitmap to the backing store. - // - // TODO(brettw) we can optimize this in the case where the entire canvas is - // updated by actually taking ownership of the buffer and not telling the - // plugin we're done using it. This wat we can avoid the copy when the entire - // canvas has been updated. - SkIRect src_rect = { context->dirty.left, - context->dirty.top, - context->dirty.right, - context->dirty.bottom }; - SkRect dest_rect = { SkIntToScalar(context->dirty.left), - SkIntToScalar(context->dirty.top), - SkIntToScalar(context->dirty.right), - SkIntToScalar(context->dirty.bottom) }; - SkCanvas committed_canvas(committed_bitmap_); - - // We want to replace the contents of the bitmap rather than blend. - SkPaint paint; - paint.setXfermodeMode(SkXfermode::kSrc_Mode); - committed_canvas.drawBitmapRect( - paint_context->canvas->getTopPlatformDevice().accessBitmap(false), - &src_rect, dest_rect); - - committed_bitmap_.setIsOpaque(false); - - // Invoke the callback to inform the caller the work was done. - // TODO(brettw) this is not how we want this to work, this should - // happen when the frame is painted so the plugin knows when it can draw - // the next frame. - // - // This should also be called in the failure cases as well. - if (callback != NULL) - (*callback)(id, context, NPERR_NO_ERROR, user_data); + if (!context) { + return NPERR_INVALID_PARAM; + } - return NPERR_NO_ERROR; + Graphics2DDeviceContext* ctx = graphic2d_contexts_.Lookup( + reinterpret_cast<intptr_t>(context->reserved)); + if (!ctx) { + return NPERR_INVALID_PARAM; // TODO(brettw) call callback. + } + return ctx->Flush(&committed_bitmap_, context, callback, id, user_data); } NPError WebPluginDelegatePepper::Device2DDestroyContext( NPDeviceContext2D* context) { - OpenPaintContextMap::iterator found = open_paint_contexts_.find( - context->region); - if (found == open_paint_contexts_.end()) - return NPERR_INVALID_PARAM; - open_paint_contexts_.erase(found); - // Free the implementation information. - delete reinterpret_cast<Device2DImpl*>(context->reserved); + if (!context || !graphic2d_contexts_.Lookup( + reinterpret_cast<intptr_t>(context->reserved))) { + return NPERR_INVALID_PARAM; + } + graphic2d_contexts_.Remove(reinterpret_cast<intptr_t>(context->reserved)); + memset(context, 0, sizeof(NPDeviceContext2D)); return NPERR_NO_ERROR; } @@ -585,39 +504,14 @@ NPError WebPluginDelegatePepper::DeviceAudioInitializeContext( return NPERR_GENERIC_ERROR; } - scoped_refptr<AudioMessageFilter> filter = - render_view_->audio_message_filter(); - ViewHostMsg_Audio_CreateStream params; - - params.format = AudioManager::AUDIO_PCM_LINEAR; - params.channels = config->outputChannelMap; - params.sample_rate = config->sampleRate; - switch (config->sampleType) { - case NPAudioSampleTypeInt16: - params.bits_per_sample = 16; - break; - case NPAudioSampleTypeFloat32: - params.bits_per_sample = 32; - break; - default: - return NPERR_INVALID_PARAM; + scoped_ptr<AudioDeviceContext> audio(new AudioDeviceContext()); + NPError status = audio->Initialize(render_view_->audio_message_filter(), + config, context); + if (NPERR_NO_ERROR == status) { + context->reserved = + reinterpret_cast<void *>(audio_contexts_.Add(audio.release())); } - - context->config = *config; - params.packet_size = config->sampleFrameCount * config->outputChannelMap - * (params.bits_per_sample >> 3); - LOG(INFO) << "Initializing Pepper Audio Context (" << - config->sampleFrameCount << "Hz, " << params.bits_per_sample << - " bits, " << config->outputChannelMap << "channels"; - - // TODO(neb): figure out if this number is grounded in reality - params.buffer_capacity = params.packet_size * 3; - - // TODO(neb): keep these guys tracked somewhere so we can delete them - // even if the plugin doesn't - AudioStream *audio = new AudioStream(this); - audio->Initialize(filter, params, context); - return NPERR_NO_ERROR; + return status; } NPError WebPluginDelegatePepper::DeviceAudioSetStateContext( @@ -647,73 +541,21 @@ NPError WebPluginDelegatePepper::DeviceAudioFlushContext( NPError WebPluginDelegatePepper::DeviceAudioDestroyContext( NPDeviceContextAudio* context) { - if (!context || !context->privatePtr) { + if (!context || !audio_contexts_.Lookup( + reinterpret_cast<intptr_t>(context->reserved))) { return NPERR_INVALID_PARAM; } - delete reinterpret_cast<AudioStream *>(context->privatePtr); + audio_contexts_.Remove(reinterpret_cast<intptr_t>(context->reserved)); memset(context, 0, sizeof(NPDeviceContextAudio)); return NPERR_NO_ERROR; } -WebPluginDelegatePepper::AudioStream::~AudioStream() { - if (stream_id_) { - OnDestroy(); - } -} - -void WebPluginDelegatePepper::AudioStream::Initialize( - AudioMessageFilter* filter, const ViewHostMsg_Audio_CreateStream& params, - NPDeviceContextAudio* context) { - filter_ = filter; - context_= context; - context_->privatePtr = this; - // Make sure we don't call init more than once. - DCHECK_EQ(0, stream_id_); - stream_id_ = filter_->AddDelegate(this); - filter->Send(new ViewHostMsg_CreateAudioStream(0, stream_id_, params)); -} - -void WebPluginDelegatePepper::AudioStream::OnDestroy() { - // Make sure we don't call destroy more than once. - DCHECK_NE(0, stream_id_); - filter_->RemoveDelegate(stream_id_); - filter_->Send(new ViewHostMsg_CloseAudioStream(0, stream_id_)); - stream_id_ = 0; -} - -void WebPluginDelegatePepper::AudioStream::OnRequestPacket( - size_t bytes_in_buffer, const base::Time& message_timestamp) { - context_->config.callback(context_); - filter_->Send(new ViewHostMsg_NotifyAudioPacketReady(0, stream_id_, - shared_memory_size_)); -} - -void WebPluginDelegatePepper::AudioStream::OnStateChanged( - ViewMsg_AudioStreamState state) { -} - -void WebPluginDelegatePepper::AudioStream::OnCreated( - base::SharedMemoryHandle handle, size_t length) { - shared_memory_.reset(new base::SharedMemory(handle, false)); - shared_memory_->Map(length); - shared_memory_size_ = length; - - context_->outBuffer = shared_memory_->memory(); - // TODO(neb): call play after prefilling - filter_->Send(new ViewHostMsg_PlayAudioStream(0, stream_id_)); -} - -void WebPluginDelegatePepper::AudioStream::OnVolume(double volume) { -} - WebPluginDelegatePepper::WebPluginDelegatePepper( const base::WeakPtr<RenderView>& render_view, NPAPI::PluginInstance *instance) : render_view_(render_view), plugin_(NULL), instance_(instance), - buffer_size_(0), - plugin_buffer_(0), nested_delegate_(NULL) { // For now we keep a window struct, although it isn't used. memset(&window_, 0, sizeof(window_)); diff --git a/chrome/renderer/webplugin_delegate_pepper.h b/chrome/renderer/webplugin_delegate_pepper.h index 608748c..91e35b6 100644 --- a/chrome/renderer/webplugin_delegate_pepper.h +++ b/chrome/renderer/webplugin_delegate_pepper.h @@ -13,20 +13,15 @@ #include "app/gfx/native_widget_types.h" #include "base/file_path.h" -#include "base/linked_ptr.h" +#include "base/id_map.h" #include "base/gfx/rect.h" #include "base/ref_counted.h" -#include "base/scoped_ptr.h" #include "base/task.h" #include "base/weak_ptr.h" -#include "chrome/common/render_messages.h" -#include "chrome/common/transport_dib.h" -#include "chrome/renderer/audio_message_filter.h" +#include "chrome/renderer/pepper_devices.h" #include "chrome/renderer/render_view.h" #include "chrome/renderer/command_buffer_proxy.h" -#include "skia/ext/platform_canvas.h" #include "third_party/npapi/bindings/npapi.h" -#include "third_party/npapi/bindings/npapi_extensions.h" #include "webkit/glue/webcursor.h" #include "webkit/glue/webplugin_delegate.h" @@ -145,40 +140,6 @@ class WebPluginDelegatePepper : public webkit_glue::WebPluginDelegate { // End of WebPluginDelegate implementation. - // Each instance of AudioStream corresponds to one host stream (and one - // audio context). NPDeviceContextAudio contains a pointer to the context's - // stream as privatePtr. - class AudioStream : public AudioMessageFilter::Delegate { - public: - // TODO(neb): if plugin_delegate parameter is indeed unused, remove it - explicit AudioStream(WebPluginDelegatePepper* plugin_delegate) - : plugin_delegate_(plugin_delegate), stream_id_(0) { - } - virtual ~AudioStream(); - - void Initialize(AudioMessageFilter *filter, - const ViewHostMsg_Audio_CreateStream& params, - NPDeviceContextAudio* context); - - // AudioMessageFilter::Delegate implementation - virtual void OnRequestPacket(size_t bytes_in_buffer, - const base::Time& message_timestamp); - virtual void OnStateChanged(ViewMsg_AudioStreamState state); - virtual void OnCreated(base::SharedMemoryHandle handle, size_t length); - virtual void OnVolume(double volume); - // End of AudioMessageFilter::Delegate implementation - - private: - void OnDestroy(); - - NPDeviceContextAudio *context_; - WebPluginDelegatePepper* plugin_delegate_; - scoped_refptr<AudioMessageFilter> filter_; - int32 stream_id_; - scoped_ptr<base::SharedMemory> shared_memory_; - size_t shared_memory_size_; - }; - gfx::Rect GetRect() const { return window_rect_; } gfx::Rect GetClipRect() const { return clip_rect_; } @@ -213,26 +174,13 @@ class WebPluginDelegatePepper : public webkit_glue::WebPluginDelegate { gfx::Rect clip_rect_; std::vector<gfx::Rect> cutout_rects_; + // Open device contexts + IDMap<Graphics2DDeviceContext, IDMapOwnPointer> graphic2d_contexts_; + IDMap<AudioDeviceContext, IDMapOwnPointer> audio_contexts_; + // Plugin graphics context implementation - size_t buffer_size_; - TransportDIB* plugin_buffer_; - static uint32 next_buffer_id; SkBitmap committed_bitmap_; - // Lists all contexts currently open for painting. These are ones requested by - // the plugin but not destroyed by it yet. The source pointer is the raw - // pixels. We use this to look up the corresponding transport DIB when the - // plugin tells us to flush or destroy it. - struct OpenPaintContext { - scoped_ptr<TransportDIB> transport_dib; - - // The canvas associated with the transport DIB, containing the mapped - // memory of the image. - scoped_ptr<skia::PlatformCanvas> canvas; - }; - typedef std::map< void*, linked_ptr<OpenPaintContext> > OpenPaintContextMap; - OpenPaintContextMap open_paint_contexts_; - // The url with which the plugin was instantiated. std::string plugin_url_; diff --git a/third_party/npapi/bindings/npapi_extensions.h b/third_party/npapi/bindings/npapi_extensions.h index a53ead2..c24cd99 100644 --- a/third_party/npapi/bindings/npapi_extensions.h +++ b/third_party/npapi/bindings/npapi_extensions.h @@ -29,7 +29,7 @@ typedef struct _NPDeviceBuffer { /* completion callback for flush device */ typedef void (*NPDeviceFlushContextCallbackPtr)( - NPP instace, + NPP instance, NPDeviceContext* context, NPError err, NPUserData* userData); @@ -378,11 +378,10 @@ typedef struct _NPDeviceContextAudioConfig { } NPDeviceContextAudioConfig; struct _NPDeviceContextAudio { -// NPP npp; NPDeviceContextAudioConfig config; void *outBuffer; void *inBuffer; - void *privatePtr; + void *reserved; }; #endif /* _NP_EXTENSIONS_H_ */ |