Revert of Revert of Implement software fallback for PPB_VideoDecoder. (https://codereview.chromium.org/337683002/)

Reason for revert:
Revert of revert, which apparently wasn't a full revert.

Original issue's description:
> Revert of Implement software fallback for PPB_VideoDecoder. (https://codereview.chromium.org/311853005/)
> 
> Reason for revert:
> Broke blink Linux tests compile.http://build.chromium.org/p/chromium.webkit/builders/Linux%20Tests/builds/37259
> 
> Original issue's description:
> > Implement software fallback for PPB_VideoDecoder.
> > This modifies the proxy to implement software fallback mode.
> > The main change is to the host, which now can work with
> > media::VideoDecoders.
> > 
> > media::VideoDecoder works differently from media::VideoDecodeAccelerator
> > so an adapter object, content::VideoDecoderShim is defined. It lives on the main thread and drives the actual decoder on the media thread via a child DecoderImpl class, which sends back frames of video. VideoDecoderShim receives those and converts frames to GL textures.
> > 
> > gpu::Mailboxes are used so the host can create textures that are aliased
> > to the plugin's textures.
> > 
> > The test plugin has been changed to include bitstream data for VP8 in order to
> > test the software decoder. The data is in ppapi/examples/video_decode/testdata.h
> > alongside the H264 data. The file diff is too large for this site but is structured
> > something like this:
> > 
> > const unsigned char kData[] = {
> > #if defined USE_VP8_TESTDATA_INSTEAD_OF_H264
> > ...  lots of VP8 data
> > 
> > #else  // !USE_VP8_TESTDATA_INSTEAD_OF_H264
> > ...  lots of H264 data
> > 
> > #endif  // USE_VP8_TESTDATA_INSTEAD_OF_H264
> > };
> > 
> > 
> > There is a TODO to convert the example to load a file. I'm not sure how to go
> > about that but am willing to do the work if someone can point the way.
> > 
> > BUG=281689
> > [email protected], [email protected], [email protected], [email protected]
> > 
> > Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=277012
> 
> [email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected]
> NOTREECHECKS=true
> NOTRY=true
> BUG=281689
> 
> Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=277015

[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected]
NOTREECHECKS=true
NOTRY=true
BUG=281689

Review URL: https://codereview.chromium.org/333903002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@277020 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index 119f0c9..d23323bd 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -1206,6 +1206,8 @@
 
 TEST_PPAPI_NACL(TrueTypeFont)
 
+TEST_PPAPI_NACL(VideoDecoder)
+
 // VideoDestination doesn't work in content_browsertests.
 TEST_PPAPI_OUT_OF_PROCESS(VideoDestination)
 TEST_PPAPI_NACL(VideoDestination)
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi
index dd10f21..c7a3bfa 100644
--- a/content/content_renderer.gypi
+++ b/content/content_renderer.gypi
@@ -322,7 +322,7 @@
       'renderer/mouse_lock_dispatcher.cc',
       'renderer/mouse_lock_dispatcher.h',
       'renderer/net_info_helper.cc',
-      'renderer/net_info_helper.h',      
+      'renderer/net_info_helper.h',
       'renderer/notification_provider.cc',
       'renderer/notification_provider.h',
       'renderer/push_messaging_dispatcher.cc',
@@ -566,6 +566,8 @@
       'renderer/pepper/usb_key_code_conversion_win.cc',
       'renderer/pepper/v8_var_converter.cc',
       'renderer/pepper/v8_var_converter.h',
+      'renderer/pepper/video_decoder_shim.cc',
+      'renderer/pepper/video_decoder_shim.h',
       'renderer/render_widget_fullscreen_pepper.cc',
       'renderer/render_widget_fullscreen_pepper.h',
     ],
@@ -644,7 +646,7 @@
       'renderer/media/webrtc/webrtc_video_track_adapter.cc',
       'renderer/media/webrtc/webrtc_video_track_adapter.h',
       'renderer/media/webrtc/media_stream_remote_video_source.cc',
-      'renderer/media/webrtc/media_stream_remote_video_source.h', 
+      'renderer/media/webrtc/media_stream_remote_video_source.h',
       'renderer/media/webrtc/media_stream_track_metrics.cc',
       'renderer/media/webrtc/media_stream_track_metrics.h',
       'renderer/media/webrtc/peer_connection_dependency_factory.cc',
diff --git a/content/renderer/pepper/pepper_video_decoder_host.cc b/content/renderer/pepper/pepper_video_decoder_host.cc
index 16019f72..d3364e12 100644
--- a/content/renderer/pepper/pepper_video_decoder_host.cc
+++ b/content/renderer/pepper/pepper_video_decoder_host.cc
@@ -10,9 +10,7 @@
 #include "content/public/renderer/render_thread.h"
 #include "content/public/renderer/renderer_ppapi_host.h"
 #include "content/renderer/pepper/ppb_graphics_3d_impl.h"
-#include "content/renderer/render_thread_impl.h"
-#include "content/renderer/render_view_impl.h"
-#include "media/video/picture.h"
+#include "content/renderer/pepper/video_decoder_shim.h"
 #include "media/video/video_decode_accelerator.h"
 #include "ppapi/c/pp_completion_callback.h"
 #include "ppapi/c/pp_errors.h"
@@ -119,9 +117,10 @@
       graphics_context.host_resource(), true);
   if (enter_graphics.failed())
     return PP_ERROR_FAILED;
-  graphics3d_ = static_cast<PPB_Graphics3D_Impl*>(enter_graphics.object());
+  PPB_Graphics3D_Impl* graphics3d =
+      static_cast<PPB_Graphics3D_Impl*>(enter_graphics.object());
 
-  int command_buffer_route_id = graphics3d_->GetCommandBufferRouteId();
+  int command_buffer_route_id = graphics3d->GetCommandBufferRouteId();
   if (!command_buffer_route_id)
     return PP_ERROR_FAILED;
 
@@ -129,7 +128,7 @@
 
   // This is not synchronous, but subsequent IPC messages will be buffered, so
   // it is okay to immediately send IPC messages through the returned channel.
-  GpuChannelHost* channel = graphics3d_->channel();
+  GpuChannelHost* channel = graphics3d->channel();
   DCHECK(channel);
   decoder_ = channel->CreateVideoDecoder(command_buffer_route_id);
   if (decoder_ && decoder_->Initialize(media_profile, this)) {
@@ -138,8 +137,14 @@
   }
   decoder_.reset();
 
-  // TODO(bbudge) Implement software fallback.
-  return PP_ERROR_NOTSUPPORTED;
+  if (!allow_software_fallback)
+    return PP_ERROR_NOTSUPPORTED;
+
+  decoder_.reset(new VideoDecoderShim(this));
+  initialize_reply_context_ = context->MakeReplyMessageContext();
+  decoder_->Initialize(media_profile, this);
+
+  return PP_OK_COMPLETIONPENDING;
 }
 
 int32_t PepperVideoDecoderHost::OnHostMsgGetShm(
@@ -176,8 +181,8 @@
     shm_buffers_.push_back(shm.release());
     shm_buffer_busy_.push_back(false);
   } else {
-    // Fill in the new resized buffer. Delete it manually since ScopedVector
-    // won't delete the existing element if we just assign it.
+    // Remove the old buffer. Delete manually since ScopedVector won't delete
+    // the existing element if we just assign over it.
     delete shm_buffers_[shm_id];
     shm_buffers_[shm_id] = shm.release();
   }
@@ -262,7 +267,6 @@
     return PP_ERROR_FAILED;
 
   decoder_->ReusePictureBuffer(texture_id);
-
   return PP_OK;
 }
 
@@ -298,17 +302,13 @@
     uint32 requested_num_of_buffers,
     const gfx::Size& dimensions,
     uint32 texture_target) {
-  DCHECK(RenderThreadImpl::current());
-  host()->SendUnsolicitedReply(
-      pp_resource(),
-      PpapiPluginMsg_VideoDecoder_RequestTextures(
-          requested_num_of_buffers,
-          PP_MakeSize(dimensions.width(), dimensions.height()),
-          texture_target));
+  RequestTextures(requested_num_of_buffers,
+                  dimensions,
+                  texture_target,
+                  std::vector<gpu::Mailbox>());
 }
 
 void PepperVideoDecoderHost::PictureReady(const media::Picture& picture) {
-  DCHECK(RenderThreadImpl::current());
   host()->SendUnsolicitedReply(
       pp_resource(),
       PpapiPluginMsg_VideoDecoder_PictureReady(picture.bitstream_buffer_id(),
@@ -316,15 +316,40 @@
 }
 
 void PepperVideoDecoderHost::DismissPictureBuffer(int32 picture_buffer_id) {
-  DCHECK(RenderThreadImpl::current());
   host()->SendUnsolicitedReply(
       pp_resource(),
       PpapiPluginMsg_VideoDecoder_DismissPicture(picture_buffer_id));
 }
 
+void PepperVideoDecoderHost::NotifyEndOfBitstreamBuffer(
+    int32 bitstream_buffer_id) {
+  PendingDecodeMap::iterator it = pending_decodes_.find(bitstream_buffer_id);
+  if (it == pending_decodes_.end()) {
+    NOTREACHED();
+    return;
+  }
+  const PendingDecode& pending_decode = it->second;
+  host()->SendReply(
+      pending_decode.reply_context,
+      PpapiPluginMsg_VideoDecoder_DecodeReply(pending_decode.shm_id));
+  shm_buffer_busy_[pending_decode.shm_id] = false;
+  pending_decodes_.erase(it);
+}
+
+void PepperVideoDecoderHost::NotifyFlushDone() {
+  host()->SendReply(flush_reply_context_,
+                    PpapiPluginMsg_VideoDecoder_FlushReply());
+  flush_reply_context_ = ppapi::host::ReplyMessageContext();
+}
+
+void PepperVideoDecoderHost::NotifyResetDone() {
+  host()->SendReply(reset_reply_context_,
+                    PpapiPluginMsg_VideoDecoder_ResetReply());
+  reset_reply_context_ = ppapi::host::ReplyMessageContext();
+}
+
 void PepperVideoDecoderHost::NotifyError(
     media::VideoDecodeAccelerator::Error error) {
-  DCHECK(RenderThreadImpl::current());
   int32_t pp_error = PP_ERROR_FAILED;
   switch (error) {
     case media::VideoDecodeAccelerator::UNREADABLE_INPUT:
@@ -342,34 +367,35 @@
       pp_resource(), PpapiPluginMsg_VideoDecoder_NotifyError(pp_error));
 }
 
-void PepperVideoDecoderHost::NotifyResetDone() {
-  DCHECK(RenderThreadImpl::current());
-  host()->SendReply(reset_reply_context_,
-                    PpapiPluginMsg_VideoDecoder_ResetReply());
-  reset_reply_context_ = ppapi::host::ReplyMessageContext();
-}
-
-void PepperVideoDecoderHost::NotifyEndOfBitstreamBuffer(
-    int32 bitstream_buffer_id) {
-  DCHECK(RenderThreadImpl::current());
-  PendingDecodeMap::iterator it = pending_decodes_.find(bitstream_buffer_id);
-  if (it == pending_decodes_.end()) {
-    NOTREACHED();
-    return;
+void PepperVideoDecoderHost::OnInitializeComplete(int32_t result) {
+  if (!initialized_) {
+    if (result == PP_OK)
+      initialized_ = true;
+    initialize_reply_context_.params.set_result(result);
+    host()->SendReply(initialize_reply_context_,
+                      PpapiPluginMsg_VideoDecoder_InitializeReply());
   }
-  const PendingDecode& pending_decode = it->second;
-  host()->SendReply(
-      pending_decode.reply_context,
-      PpapiPluginMsg_VideoDecoder_DecodeReply(pending_decode.shm_id));
-  shm_buffer_busy_[pending_decode.shm_id] = false;
-  pending_decodes_.erase(it);
 }
 
-void PepperVideoDecoderHost::NotifyFlushDone() {
-  DCHECK(RenderThreadImpl::current());
-  host()->SendReply(flush_reply_context_,
-                    PpapiPluginMsg_VideoDecoder_FlushReply());
-  flush_reply_context_ = ppapi::host::ReplyMessageContext();
+const uint8_t* PepperVideoDecoderHost::DecodeIdToAddress(uint32_t decode_id) {
+  PendingDecodeMap::const_iterator it = pending_decodes_.find(decode_id);
+  DCHECK(it != pending_decodes_.end());
+  uint32_t shm_id = it->second.shm_id;
+  return static_cast<uint8_t*>(shm_buffers_[shm_id]->memory());
+}
+
+void PepperVideoDecoderHost::RequestTextures(
+    uint32 requested_num_of_buffers,
+    const gfx::Size& dimensions,
+    uint32 texture_target,
+    const std::vector<gpu::Mailbox>& mailboxes) {
+  host()->SendUnsolicitedReply(
+      pp_resource(),
+      PpapiPluginMsg_VideoDecoder_RequestTextures(
+          requested_num_of_buffers,
+          PP_MakeSize(dimensions.width(), dimensions.height()),
+          texture_target,
+          mailboxes));
 }
 
 }  // namespace content
diff --git a/content/renderer/pepper/pepper_video_decoder_host.h b/content/renderer/pepper/pepper_video_decoder_host.h
index bc44fe84..e76f95c4 100644
--- a/content/renderer/pepper/pepper_video_decoder_host.h
+++ b/content/renderer/pepper/pepper_video_decoder_host.h
@@ -10,11 +10,9 @@
 
 #include "base/basictypes.h"
 #include "base/containers/hash_tables.h"
-#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/scoped_vector.h"
 #include "content/common/content_export.h"
-#include "gpu/command_buffer/common/mailbox.h"
 #include "media/video/video_decode_accelerator.h"
 #include "ppapi/c/pp_codecs.h"
 #include "ppapi/host/host_message_context.h"
@@ -30,6 +28,7 @@
 class PPB_Graphics3D_Impl;
 class RendererPpapiHost;
 class RenderViewImpl;
+class VideoDecoderShim;
 
 class CONTENT_EXPORT PepperVideoDecoderHost
     : public ppapi::host::ResourceHost,
@@ -50,6 +49,8 @@
     const ppapi::host::ReplyMessageContext reply_context;
   };
 
+  friend class VideoDecoderShim;
+
   // ResourceHost implementation.
   virtual int32_t OnResourceMessageReceived(
       const IPC::Message& msg,
@@ -61,10 +62,10 @@
                                      uint32 texture_target) OVERRIDE;
   virtual void DismissPictureBuffer(int32 picture_buffer_id) OVERRIDE;
   virtual void PictureReady(const media::Picture& picture) OVERRIDE;
-  virtual void NotifyError(media::VideoDecodeAccelerator::Error error) OVERRIDE;
-  virtual void NotifyFlushDone() OVERRIDE;
   virtual void NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id) OVERRIDE;
+  virtual void NotifyFlushDone() OVERRIDE;
   virtual void NotifyResetDone() OVERRIDE;
+  virtual void NotifyError(media::VideoDecodeAccelerator::Error error) OVERRIDE;
 
   int32_t OnHostMsgInitialize(ppapi::host::HostMessageContext* context,
                               const ppapi::HostResource& graphics_context,
@@ -85,11 +86,19 @@
   int32_t OnHostMsgFlush(ppapi::host::HostMessageContext* context);
   int32_t OnHostMsgReset(ppapi::host::HostMessageContext* context);
 
+  // These methods are needed by VideoDecodeShim, to look like a
+  // VideoDecodeAccelerator.
+  void OnInitializeComplete(int32_t result);
+  const uint8_t* DecodeIdToAddress(uint32_t decode_id);
+  void RequestTextures(uint32 requested_num_of_buffers,
+                       const gfx::Size& dimensions,
+                       uint32 texture_target,
+                       const std::vector<gpu::Mailbox>& mailboxes);
+
   // Non-owning pointer.
   RendererPpapiHost* renderer_ppapi_host_;
 
   scoped_ptr<media::VideoDecodeAccelerator> decoder_;
-  scoped_refptr<PPB_Graphics3D_Impl> graphics3d_;
 
   // A vector holding our shm buffers, in sync with a similar vector in the
   // resource. We use a buffer's index in these vectors as its id on both sides
@@ -106,6 +115,8 @@
 
   ppapi::host::ReplyMessageContext flush_reply_context_;
   ppapi::host::ReplyMessageContext reset_reply_context_;
+  // Only used when in software fallback mode.
+  ppapi::host::ReplyMessageContext initialize_reply_context_;
 
   bool initialized_;
 
diff --git a/content/renderer/pepper/video_decoder_shim.cc b/content/renderer/pepper/video_decoder_shim.cc
new file mode 100644
index 0000000..eb833ff
--- /dev/null
+++ b/content/renderer/pepper/video_decoder_shim.cc
@@ -0,0 +1,576 @@
+// 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 "content/renderer/pepper/video_decoder_shim.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES2/gl2extchromium.h>
+
+#include "base/bind.h"
+#include "base/numerics/safe_conversions.h"
+#include "content/public/renderer/render_thread.h"
+#include "content/renderer/pepper/pepper_video_decoder_host.h"
+#include "content/renderer/render_thread_impl.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/limits.h"
+#include "media/base/video_decoder.h"
+#include "media/filters/ffmpeg_video_decoder.h"
+#include "media/video/picture.h"
+#include "media/video/video_decode_accelerator.h"
+#include "ppapi/c/pp_errors.h"
+#include "third_party/libyuv/include/libyuv.h"
+#include "webkit/common/gpu/context_provider_web_context.h"
+
+namespace content {
+
+struct VideoDecoderShim::PendingDecode {
+  PendingDecode(uint32_t decode_id,
+                const scoped_refptr<media::DecoderBuffer>& buffer);
+  ~PendingDecode();
+
+  const uint32_t decode_id;
+  const scoped_refptr<media::DecoderBuffer> buffer;
+};
+
+VideoDecoderShim::PendingDecode::PendingDecode(
+    uint32_t decode_id,
+    const scoped_refptr<media::DecoderBuffer>& buffer)
+    : decode_id(decode_id), buffer(buffer) {
+}
+
+VideoDecoderShim::PendingDecode::~PendingDecode() {
+}
+
+struct VideoDecoderShim::PendingFrame {
+  explicit PendingFrame(uint32_t decode_id);
+  PendingFrame(uint32_t decode_id, const gfx::Size& size);
+  ~PendingFrame();
+
+  const uint32_t decode_id;
+  const gfx::Size size;
+  std::vector<uint8_t> argb_pixels;
+
+ private:
+  // This could be expensive to copy, so guard against that.
+  DISALLOW_COPY_AND_ASSIGN(PendingFrame);
+};
+
+VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id)
+    : decode_id(decode_id) {
+}
+
+VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id,
+                                             const gfx::Size& size)
+    : decode_id(decode_id),
+      size(size),
+      argb_pixels(size.width() * size.height() * 4) {
+}
+
+VideoDecoderShim::PendingFrame::~PendingFrame() {
+}
+
+// DecoderImpl runs the underlying VideoDecoder on the media thread, receiving
+// calls from the VideoDecodeShim on the main thread and sending results back.
+// This class is constructed on the main thread, but used and destructed on the
+// media thread.
+class VideoDecoderShim::DecoderImpl {
+ public:
+  explicit DecoderImpl(const base::WeakPtr<VideoDecoderShim>& proxy);
+  ~DecoderImpl();
+
+  void Initialize(media::VideoDecoderConfig config);
+  void Decode(uint32_t decode_id, scoped_refptr<media::DecoderBuffer> buffer);
+  void Reset();
+  void Stop();
+
+ private:
+  void OnPipelineStatus(media::PipelineStatus status);
+  void DoDecode();
+  void OnDecodeComplete(uint32_t decode_id, media::VideoDecoder::Status status);
+  void OnOutputComplete(const scoped_refptr<media::VideoFrame>& frame);
+  void OnResetComplete();
+
+  // WeakPtr is bound to main_message_loop_. Use only in shim callbacks.
+  base::WeakPtr<VideoDecoderShim> shim_;
+  scoped_ptr<media::VideoDecoder> decoder_;
+  scoped_refptr<base::MessageLoopProxy> main_message_loop_;
+  // Queue of decodes waiting for the decoder.
+  typedef std::queue<PendingDecode> PendingDecodeQueue;
+  PendingDecodeQueue pending_decodes_;
+  int max_decodes_at_decoder_;
+  int num_decodes_at_decoder_;
+  // VideoDecoder returns pictures without information about the decode buffer
+  // that generated it. Save the decode_id from the last decode that completed,
+  // which is close for most decoders, which only decode one buffer at a time.
+  uint32_t decode_id_;
+};
+
+VideoDecoderShim::DecoderImpl::DecoderImpl(
+    const base::WeakPtr<VideoDecoderShim>& proxy)
+    : shim_(proxy),
+      main_message_loop_(base::MessageLoopProxy::current()),
+      max_decodes_at_decoder_(0),
+      num_decodes_at_decoder_(0),
+      decode_id_(0) {
+}
+
+VideoDecoderShim::DecoderImpl::~DecoderImpl() {
+  DCHECK(pending_decodes_.empty());
+}
+
+void VideoDecoderShim::DecoderImpl::Initialize(
+    media::VideoDecoderConfig config) {
+  DCHECK(!decoder_);
+  scoped_ptr<media::FFmpegVideoDecoder> ffmpeg_video_decoder(
+      new media::FFmpegVideoDecoder(base::MessageLoopProxy::current()));
+  ffmpeg_video_decoder->set_decode_nalus(true);
+  decoder_ = ffmpeg_video_decoder.Pass();
+  max_decodes_at_decoder_ = decoder_->GetMaxDecodeRequests();
+  // We can use base::Unretained() safely in decoder callbacks because we call
+  // VideoDecoder::Stop() before deletion. Stop() guarantees there will be no
+  // outstanding callbacks after it returns.
+  decoder_->Initialize(
+      config,
+      true /* low_delay */,
+      base::Bind(&VideoDecoderShim::DecoderImpl::OnPipelineStatus,
+                 base::Unretained(this)),
+      base::Bind(&VideoDecoderShim::DecoderImpl::OnOutputComplete,
+                 base::Unretained(this)));
+}
+
+void VideoDecoderShim::DecoderImpl::Decode(
+    uint32_t decode_id,
+    scoped_refptr<media::DecoderBuffer> buffer) {
+  DCHECK(decoder_);
+  pending_decodes_.push(PendingDecode(decode_id, buffer));
+  DoDecode();
+}
+
+void VideoDecoderShim::DecoderImpl::Reset() {
+  DCHECK(decoder_);
+  // Abort all pending decodes.
+  while (!pending_decodes_.empty()) {
+    const PendingDecode& decode = pending_decodes_.front();
+    scoped_ptr<PendingFrame> pending_frame(new PendingFrame(decode.decode_id));
+    main_message_loop_->PostTask(FROM_HERE,
+                                 base::Bind(&VideoDecoderShim::OnDecodeComplete,
+                                            shim_,
+                                            media::VideoDecoder::kAborted,
+                                            decode.decode_id));
+    pending_decodes_.pop();
+  }
+  decoder_->Reset(base::Bind(&VideoDecoderShim::DecoderImpl::OnResetComplete,
+                             base::Unretained(this)));
+}
+
+void VideoDecoderShim::DecoderImpl::Stop() {
+  DCHECK(decoder_);
+  // Clear pending decodes now. We don't want OnDecodeComplete to call DoDecode
+  // again.
+  while (!pending_decodes_.empty())
+    pending_decodes_.pop();
+  decoder_->Stop();
+  // This instance is deleted once we exit this scope.
+}
+
+void VideoDecoderShim::DecoderImpl::OnPipelineStatus(
+    media::PipelineStatus status) {
+  int32_t result;
+  switch (status) {
+    case media::PIPELINE_OK:
+      result = PP_OK;
+      break;
+    case media::DECODER_ERROR_NOT_SUPPORTED:
+      result = PP_ERROR_NOTSUPPORTED;
+      break;
+    default:
+      result = PP_ERROR_FAILED;
+      break;
+  }
+
+  // Calculate how many textures the shim should create.
+  uint32_t shim_texture_pool_size =
+      max_decodes_at_decoder_ + media::limits::kMaxVideoFrames;
+  main_message_loop_->PostTask(
+      FROM_HERE,
+      base::Bind(&VideoDecoderShim::OnInitializeComplete,
+                 shim_,
+                 result,
+                 shim_texture_pool_size));
+}
+
+void VideoDecoderShim::DecoderImpl::DoDecode() {
+  while (!pending_decodes_.empty() &&
+         num_decodes_at_decoder_ < max_decodes_at_decoder_) {
+    num_decodes_at_decoder_++;
+    const PendingDecode& decode = pending_decodes_.front();
+    decoder_->Decode(
+        decode.buffer,
+        base::Bind(&VideoDecoderShim::DecoderImpl::OnDecodeComplete,
+                   base::Unretained(this),
+                   decode.decode_id));
+    pending_decodes_.pop();
+  }
+}
+
+void VideoDecoderShim::DecoderImpl::OnDecodeComplete(
+    uint32_t decode_id,
+    media::VideoDecoder::Status status) {
+  num_decodes_at_decoder_--;
+  decode_id_ = decode_id;
+
+  int32_t result;
+  switch (status) {
+    case media::VideoDecoder::kOk:
+    case media::VideoDecoder::kAborted:
+      result = PP_OK;
+      break;
+    case media::VideoDecoder::kDecodeError:
+      result = PP_ERROR_RESOURCE_FAILED;
+      break;
+    default:
+      NOTREACHED();
+      result = PP_ERROR_FAILED;
+      break;
+  }
+
+  main_message_loop_->PostTask(
+      FROM_HERE,
+      base::Bind(
+          &VideoDecoderShim::OnDecodeComplete, shim_, result, decode_id));
+
+  DoDecode();
+}
+
+void VideoDecoderShim::DecoderImpl::OnOutputComplete(
+    const scoped_refptr<media::VideoFrame>& frame) {
+  scoped_ptr<PendingFrame> pending_frame;
+  if (!frame->end_of_stream()) {
+    pending_frame.reset(new PendingFrame(decode_id_, frame->coded_size()));
+    // Convert the VideoFrame pixels to ARGB.
+    libyuv::I420ToARGB(frame->data(media::VideoFrame::kYPlane),
+                       frame->stride(media::VideoFrame::kYPlane),
+                       frame->data(media::VideoFrame::kUPlane),
+                       frame->stride(media::VideoFrame::kUPlane),
+                       frame->data(media::VideoFrame::kVPlane),
+                       frame->stride(media::VideoFrame::kVPlane),
+                       &pending_frame->argb_pixels.front(),
+                       frame->coded_size().width() * 4,
+                       frame->coded_size().width(),
+                       frame->coded_size().height());
+  } else {
+    pending_frame.reset(new PendingFrame(decode_id_));
+  }
+
+  main_message_loop_->PostTask(FROM_HERE,
+                               base::Bind(&VideoDecoderShim::OnOutputComplete,
+                                          shim_,
+                                          base::Passed(&pending_frame)));
+}
+
+void VideoDecoderShim::DecoderImpl::OnResetComplete() {
+  main_message_loop_->PostTask(
+      FROM_HERE, base::Bind(&VideoDecoderShim::OnResetComplete, shim_));
+}
+
+VideoDecoderShim::VideoDecoderShim(PepperVideoDecoderHost* host)
+    : state_(UNINITIALIZED),
+      host_(host),
+      media_message_loop_(
+          RenderThreadImpl::current()->GetMediaThreadMessageLoopProxy()),
+      context_provider_(
+          RenderThreadImpl::current()->SharedMainThreadContextProvider()),
+      texture_pool_size_(0),
+      num_pending_decodes_(0),
+      weak_ptr_factory_(this) {
+  DCHECK(host_);
+  DCHECK(media_message_loop_);
+  DCHECK(context_provider_);
+  decoder_impl_.reset(new DecoderImpl(weak_ptr_factory_.GetWeakPtr()));
+}
+
+VideoDecoderShim::~VideoDecoderShim() {
+  DCHECK(RenderThreadImpl::current());
+  // Delete any remaining textures.
+  TextureIdMap::iterator it = texture_id_map_.begin();
+  for (; it != texture_id_map_.end(); ++it)
+    DeleteTexture(it->second);
+  texture_id_map_.clear();
+
+  FlushCommandBuffer();
+
+  weak_ptr_factory_.InvalidateWeakPtrs();
+  // No more callbacks from the delegate will be received now.
+
+  // The callback now holds the only reference to the DecoderImpl, which will be
+  // deleted when Stop completes.
+  media_message_loop_->PostTask(
+      FROM_HERE,
+      base::Bind(&VideoDecoderShim::DecoderImpl::Stop,
+                 base::Owned(decoder_impl_.release())));
+}
+
+bool VideoDecoderShim::Initialize(
+    media::VideoCodecProfile profile,
+    media::VideoDecodeAccelerator::Client* client) {
+  DCHECK_EQ(client, host_);
+  DCHECK(RenderThreadImpl::current());
+  DCHECK_EQ(state_, UNINITIALIZED);
+  media::VideoCodec codec = media::kUnknownVideoCodec;
+  if (profile <= media::H264PROFILE_MAX)
+    codec = media::kCodecH264;
+  else if (profile <= media::VP8PROFILE_MAX)
+    codec = media::kCodecVP8;
+  DCHECK_NE(codec, media::kUnknownVideoCodec);
+
+  media::VideoDecoderConfig config(
+      codec,
+      profile,
+      media::VideoFrame::YV12,
+      gfx::Size(32, 24),  // Small sizes that won't fail.
+      gfx::Rect(32, 24),
+      gfx::Size(32, 24),
+      NULL /* extra_data */,  // TODO(bbudge) Verify this isn't needed.
+      0 /* extra_data_size */,
+      false /* decryption */);
+
+  media_message_loop_->PostTask(
+      FROM_HERE,
+      base::Bind(&VideoDecoderShim::DecoderImpl::Initialize,
+                 base::Unretained(decoder_impl_.get()),
+                 config));
+  // Return success, even though we are asynchronous, to mimic
+  // media::VideoDecodeAccelerator.
+  return true;
+}
+
+void VideoDecoderShim::Decode(const media::BitstreamBuffer& bitstream_buffer) {
+  DCHECK(RenderThreadImpl::current());
+  DCHECK_EQ(state_, DECODING);
+
+  // We need the address of the shared memory, so we can copy the buffer.
+  const uint8_t* buffer = host_->DecodeIdToAddress(bitstream_buffer.id());
+  DCHECK(buffer);
+
+  media_message_loop_->PostTask(
+      FROM_HERE,
+      base::Bind(
+          &VideoDecoderShim::DecoderImpl::Decode,
+          base::Unretained(decoder_impl_.get()),
+          bitstream_buffer.id(),
+          media::DecoderBuffer::CopyFrom(buffer, bitstream_buffer.size())));
+  num_pending_decodes_++;
+}
+
+void VideoDecoderShim::AssignPictureBuffers(
+    const std::vector<media::PictureBuffer>& buffers) {
+  DCHECK(RenderThreadImpl::current());
+  DCHECK_EQ(state_, DECODING);
+  if (buffers.empty()) {
+    NOTREACHED();
+    return;
+  }
+  DCHECK_EQ(buffers.size(), pending_texture_mailboxes_.size());
+  GLuint num_textures = base::checked_cast<GLuint>(buffers.size());
+  std::vector<uint32_t> local_texture_ids(num_textures);
+  gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
+  gles2->GenTextures(num_textures, &local_texture_ids.front());
+  for (uint32_t i = 0; i < num_textures; i++) {
+    gles2->ActiveTexture(GL_TEXTURE0);
+    gles2->BindTexture(GL_TEXTURE_2D, local_texture_ids[i]);
+    gles2->ConsumeTextureCHROMIUM(GL_TEXTURE_2D,
+                                  pending_texture_mailboxes_[i].name);
+    // Map the plugin texture id to the local texture id.
+    uint32_t plugin_texture_id = buffers[i].texture_id();
+    texture_id_map_[plugin_texture_id] = local_texture_ids[i];
+    available_textures_.push_back(plugin_texture_id);
+  }
+  pending_texture_mailboxes_.clear();
+  SendPictures();
+}
+
+void VideoDecoderShim::ReusePictureBuffer(int32 picture_buffer_id) {
+  DCHECK(RenderThreadImpl::current());
+  uint32_t texture_id = static_cast<uint32_t>(picture_buffer_id);
+  if (textures_to_dismiss_.find(texture_id) != textures_to_dismiss_.end()) {
+    DismissTexture(texture_id);
+  } else if (texture_id_map_.find(texture_id) != texture_id_map_.end()) {
+    available_textures_.push_back(texture_id);
+    SendPictures();
+  } else {
+    NOTREACHED();
+  }
+}
+
+void VideoDecoderShim::Flush() {
+  DCHECK(RenderThreadImpl::current());
+  DCHECK_EQ(state_, DECODING);
+  state_ = FLUSHING;
+}
+
+void VideoDecoderShim::Reset() {
+  DCHECK(RenderThreadImpl::current());
+  DCHECK_EQ(state_, DECODING);
+  state_ = RESETTING;
+  media_message_loop_->PostTask(
+      FROM_HERE,
+      base::Bind(&VideoDecoderShim::DecoderImpl::Reset,
+                 base::Unretained(decoder_impl_.get())));
+}
+
+void VideoDecoderShim::Destroy() {
+  // This will be called, but our destructor does the actual work.
+}
+
+void VideoDecoderShim::OnInitializeComplete(int32_t result,
+                                            uint32_t texture_pool_size) {
+  DCHECK(RenderThreadImpl::current());
+  DCHECK(host_);
+
+  if (result == PP_OK) {
+    state_ = DECODING;
+    texture_pool_size_ = texture_pool_size;
+  }
+
+  host_->OnInitializeComplete(result);
+}
+
+void VideoDecoderShim::OnDecodeComplete(int32_t result, uint32_t decode_id) {
+  DCHECK(RenderThreadImpl::current());
+  DCHECK(host_);
+
+  num_pending_decodes_--;
+  completed_decodes_.push(decode_id);
+
+  if (result == PP_OK) {
+    // If frames are being queued because we're out of textures, don't notify
+    // the host that decode has completed. This exerts "back pressure" to keep
+    // the host from sending buffers that will cause pending_frames_ to grow.
+    if (pending_frames_.empty())
+      NotifyCompletedDecodes();
+  } else if (result == PP_ERROR_RESOURCE_FAILED) {
+    host_->NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE);
+  }
+}
+
+void VideoDecoderShim::OnOutputComplete(scoped_ptr<PendingFrame> frame) {
+  DCHECK(RenderThreadImpl::current());
+  DCHECK(host_);
+
+  if (!frame->argb_pixels.empty()) {
+    if (texture_size_ != frame->size) {
+      // If the size has changed, all current textures must be dismissed. Add
+      // all textures to |textures_to_dismiss_| and dismiss any that aren't in
+      // use by the plugin. We will dismiss the rest as they are recycled.
+      for (TextureIdMap::const_iterator it = texture_id_map_.begin();
+           it != texture_id_map_.end();
+           ++it) {
+        textures_to_dismiss_.insert(it->second);
+      }
+      for (std::vector<uint32_t>::const_iterator it =
+               available_textures_.begin();
+           it != available_textures_.end();
+           ++it) {
+        DismissTexture(*it);
+      }
+      available_textures_.clear();
+      FlushCommandBuffer();
+
+      DCHECK(pending_texture_mailboxes_.empty());
+      for (uint32_t i = 0; i < texture_pool_size_; i++)
+        pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate());
+
+      host_->RequestTextures(texture_pool_size_,
+                             frame->size,
+                             GL_TEXTURE_2D,
+                             pending_texture_mailboxes_);
+      texture_size_ = frame->size;
+    }
+
+    pending_frames_.push(linked_ptr<PendingFrame>(frame.release()));
+    SendPictures();
+  }
+}
+
+void VideoDecoderShim::SendPictures() {
+  DCHECK(RenderThreadImpl::current());
+  DCHECK(host_);
+  while (!pending_frames_.empty() && !available_textures_.empty()) {
+    const linked_ptr<PendingFrame>& frame = pending_frames_.front();
+
+    uint32_t texture_id = available_textures_.back();
+    available_textures_.pop_back();
+
+    uint32_t local_texture_id = texture_id_map_[texture_id];
+    gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
+    gles2->ActiveTexture(GL_TEXTURE0);
+    gles2->BindTexture(GL_TEXTURE_2D, local_texture_id);
+    gles2->TexImage2D(GL_TEXTURE_2D,
+                      0,
+                      GL_RGBA,
+                      texture_size_.width(),
+                      texture_size_.height(),
+                      0,
+                      GL_RGBA,
+                      GL_UNSIGNED_BYTE,
+                      &frame->argb_pixels.front());
+
+    host_->PictureReady(media::Picture(texture_id, frame->decode_id));
+    pending_frames_.pop();
+  }
+
+  FlushCommandBuffer();
+
+  if (pending_frames_.empty()) {
+    // If frames aren't backing up, notify the host of any completed decodes so
+    // it can send more buffers.
+    NotifyCompletedDecodes();
+
+    if (state_ == FLUSHING && !num_pending_decodes_) {
+      state_ = DECODING;
+      host_->NotifyFlushDone();
+    }
+  }
+}
+
+void VideoDecoderShim::OnResetComplete() {
+  DCHECK(RenderThreadImpl::current());
+  DCHECK(host_);
+
+  while (!pending_frames_.empty())
+    pending_frames_.pop();
+  NotifyCompletedDecodes();
+
+  state_ = DECODING;
+  host_->NotifyResetDone();
+}
+
+void VideoDecoderShim::NotifyCompletedDecodes() {
+  while (!completed_decodes_.empty()) {
+    host_->NotifyEndOfBitstreamBuffer(completed_decodes_.front());
+    completed_decodes_.pop();
+  }
+}
+
+void VideoDecoderShim::DismissTexture(uint32_t texture_id) {
+  DCHECK(host_);
+  textures_to_dismiss_.erase(texture_id);
+  DCHECK(texture_id_map_.find(texture_id) != texture_id_map_.end());
+  DeleteTexture(texture_id_map_[texture_id]);
+  texture_id_map_.erase(texture_id);
+  host_->DismissPictureBuffer(texture_id);
+}
+
+void VideoDecoderShim::DeleteTexture(uint32_t texture_id) {
+  gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
+  gles2->DeleteTextures(1, &texture_id);
+}
+
+void VideoDecoderShim::FlushCommandBuffer() {
+  context_provider_->ContextGL()->Flush();
+}
+
+}  // namespace content
diff --git a/content/renderer/pepper/video_decoder_shim.h b/content/renderer/pepper/video_decoder_shim.h
new file mode 100644
index 0000000..ca4ba1b4
--- /dev/null
+++ b/content/renderer/pepper/video_decoder_shim.h
@@ -0,0 +1,129 @@
+// 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.
+
+#ifndef CONTENT_RENDERER_PEPPER_VIDEO_DECODER_SHIM_H_
+#define CONTENT_RENDERER_PEPPER_VIDEO_DECODER_SHIM_H_
+
+#include <queue>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "media/base/video_decoder_config.h"
+#include "media/video/video_decode_accelerator.h"
+
+#include "ppapi/c/pp_codecs.h"
+
+namespace gpu {
+namespace gles2 {
+class GLES2Interface;
+}
+}
+
+namespace media {
+class DecoderBuffer;
+}
+
+namespace webkit {
+namespace gpu {
+class ContextProviderWebContext;
+}
+}
+
+namespace content {
+
+class PepperVideoDecoderHost;
+
+// This class is a shim to wrap a media::VideoDecoder so that it can be used
+// by PepperVideoDecoderHost in place of a media::VideoDecodeAccelerator.
+// This class should be constructed, used, and destructed on the main (render)
+// thread.
+class VideoDecoderShim : public media::VideoDecodeAccelerator {
+ public:
+  explicit VideoDecoderShim(PepperVideoDecoderHost* host);
+  virtual ~VideoDecoderShim();
+
+  // media::VideoDecodeAccelerator implementation.
+  virtual bool Initialize(
+      media::VideoCodecProfile profile,
+      media::VideoDecodeAccelerator::Client* client) OVERRIDE;
+  virtual void Decode(const media::BitstreamBuffer& bitstream_buffer) OVERRIDE;
+  virtual void AssignPictureBuffers(
+      const std::vector<media::PictureBuffer>& buffers) OVERRIDE;
+  virtual void ReusePictureBuffer(int32 picture_buffer_id) OVERRIDE;
+  virtual void Flush() OVERRIDE;
+  virtual void Reset() OVERRIDE;
+  virtual void Destroy() OVERRIDE;
+
+ private:
+  enum State {
+    UNINITIALIZED,
+    DECODING,
+    FLUSHING,
+    RESETTING,
+  };
+
+  struct PendingDecode;
+  struct PendingFrame;
+  class DecoderImpl;
+
+  void OnInitializeComplete(int32_t result, uint32_t texture_pool_size);
+  void OnDecodeComplete(int32_t result, uint32_t decode_id);
+  void OnOutputComplete(scoped_ptr<PendingFrame> frame);
+  void SendPictures();
+  void OnResetComplete();
+  void NotifyCompletedDecodes();
+  void DismissTexture(uint32_t texture_id);
+  void DeleteTexture(uint32_t texture_id);
+  // Call this whenever we change GL state that the plugin relies on, such as
+  // creating picture textures.
+  void FlushCommandBuffer();
+
+  scoped_ptr<DecoderImpl> decoder_impl_;
+  State state_;
+
+  PepperVideoDecoderHost* host_;
+  scoped_refptr<base::MessageLoopProxy> media_message_loop_;
+  scoped_refptr<webkit::gpu::ContextProviderWebContext> context_provider_;
+
+  // The current decoded frame size.
+  gfx::Size texture_size_;
+  // Map that takes the plugin's GL texture id to the renderer's GL texture id.
+  typedef base::hash_map<uint32_t, uint32_t> TextureIdMap;
+  TextureIdMap texture_id_map_;
+  // Available textures (these are plugin ids.)
+  std::vector<uint32_t> available_textures_;
+  // Track textures that are no longer needed (these are plugin ids.)
+  typedef base::hash_set<uint32_t> TextureIdSet;
+  TextureIdSet textures_to_dismiss_;
+  // Mailboxes for pending texture requests, to write to plugin's textures.
+  std::vector<gpu::Mailbox> pending_texture_mailboxes_;
+
+  // Queue of completed decode ids, for notifying the host.
+  typedef std::queue<uint32_t> CompletedDecodeQueue;
+  CompletedDecodeQueue completed_decodes_;
+
+  // Queue of decoded frames that have been converted to RGB and await upload to
+  // a GL texture.
+  typedef std::queue<linked_ptr<PendingFrame> > PendingFrameQueue;
+  PendingFrameQueue pending_frames_;
+
+  // The optimal number of textures to allocate for decoder_impl_.
+  uint32_t texture_pool_size_;
+
+  uint32_t num_pending_decodes_;
+
+  base::WeakPtrFactory<VideoDecoderShim> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(VideoDecoderShim);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_PEPPER_VIDEO_DECODER_SHIM_H_
diff --git a/content/test/ppapi/ppapi_browsertest.cc b/content/test/ppapi/ppapi_browsertest.cc
index 0dd7047d..95947be 100644
--- a/content/test/ppapi/ppapi_browsertest.cc
+++ b/content/test/ppapi/ppapi_browsertest.cc
@@ -141,6 +141,8 @@
 TEST_PPAPI_IN_PROCESS(VarResource)
 TEST_PPAPI_OUT_OF_PROCESS(VarResource)
 
+TEST_PPAPI_OUT_OF_PROCESS(VideoDecoder)
+
 TEST_PPAPI_IN_PROCESS(VideoDecoderDev)
 TEST_PPAPI_OUT_OF_PROCESS(VideoDecoderDev)
 
diff --git a/gpu/command_buffer/common/cmd_buffer_common.cc b/gpu/command_buffer/common/cmd_buffer_common.cc
index 80a1e16..f337e86 100644
--- a/gpu/command_buffer/common/cmd_buffer_common.cc
+++ b/gpu/command_buffer/common/cmd_buffer_common.cc
@@ -31,6 +31,7 @@
 
 }  // namespace cmd
 
+#if !defined(NACL_WIN64)
 // TODO(apatrick): this is a temporary optimization while skia is calling
 // RendererGLContext::MakeCurrent prior to every GL call. It saves returning 6
 // ints redundantly when only the error is needed for the CommandBufferProxy
@@ -38,6 +39,7 @@
 error::Error CommandBuffer::GetLastError() {
   return GetLastState().error;
 }
+#endif
 
 }  // namespace gpu
 
diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp
index 77318251..7d0695e 100644
--- a/gpu/gpu.gyp
+++ b/gpu/gpu.gyp
@@ -567,6 +567,28 @@
     ['disable_nacl!=1 and OS=="win" and target_arch=="ia32"', {
       'targets': [
         {
+          'target_name': 'command_buffer_common_win64',
+          'type': 'static_library',
+          'variables': {
+            'nacl_win64_target': 1,
+          },
+          'includes': [
+            'command_buffer_common.gypi',
+          ],
+          'dependencies': [
+            '../base/base.gyp:base_win64',
+          ],
+          'defines': [
+            '<@(nacl_win64_defines)',
+            'GPU_IMPLEMENTATION',
+          ],
+          'configurations': {
+            'Common_Base': {
+              'msvs_target_platform': 'x64',
+            },
+          },
+        },
+        {
           'target_name': 'gpu_ipc_win64',
           'type': 'static_library',
           'variables': {
@@ -578,6 +600,7 @@
           'dependencies': [
             '../base/base.gyp:base_win64',
             '../ipc/ipc.gyp:ipc_win64',
+            'command_buffer_common_win64',
           ],
           'defines': [
             '<@(nacl_win64_defines)',
diff --git a/ppapi/examples/video_decode/video_decode.cc b/ppapi/examples/video_decode/video_decode.cc
index de9bb8df..0e47dcfe 100644
--- a/ppapi/examples/video_decode/video_decode.cc
+++ b/ppapi/examples/video_decode/video_decode.cc
@@ -20,7 +20,12 @@
 #include "ppapi/cpp/rect.h"
 #include "ppapi/cpp/var.h"
 #include "ppapi/cpp/video_decoder.h"
+
+// VP8 is more likely to work on different versions of Chrome. Undefine this
+// to decode H264.
+#define USE_VP8_TESTDATA_INSTEAD_OF_H264
 #include "ppapi/examples/video_decode/testdata.h"
+
 #include "ppapi/lib/gl/include/GLES2/gl2.h"
 #include "ppapi/lib/gl/include/GLES2/gl2ext.h"
 #include "ppapi/utility/completion_callback_factory.h"
@@ -56,6 +61,7 @@
   // pp::Instance implementation.
   virtual void DidChangeView(const pp::Rect& position,
                              const pp::Rect& clip_ignored);
+  virtual bool HandleInputEvent(const pp::InputEvent& event);
 
   // pp::Graphics3DClient implementation.
   virtual void Graphics3DContextLost() {
@@ -139,12 +145,12 @@
   int id() const { return id_; }
   bool decoding() const { return !flushing_ && !resetting_; }
 
-  void Seek(int frame);
+  void Reset();
   void RecyclePicture(const PP_VideoPicture& picture);
 
  private:
   void InitializeDone(int32_t result);
-  void Start(int frame);
+  void Start();
   void DecodeNextFrame();
   void DecodeDone(int32_t result);
   void PictureReady(int32_t result, PP_VideoPicture picture);
@@ -159,11 +165,29 @@
 
   size_t encoded_data_next_pos_to_decode_;
   int next_picture_id_;
-  int seek_frame_;
   bool flushing_;
   bool resetting_;
 };
 
+#if defined USE_VP8_TESTDATA_INSTEAD_OF_H264
+
+// VP8 is stored in an IVF container.
+// Helpful description: http://wiki.multimedia.cx/index.php?title=IVF
+
+static void GetNextFrame(size_t* start_pos, size_t* end_pos) {
+  size_t current_pos = *start_pos;
+  if (current_pos == 0)
+    current_pos = 32;  // Skip stream header.
+  uint32_t frame_size = kData[current_pos] + (kData[current_pos + 1] << 8) +
+                        (kData[current_pos + 2] << 16) +
+                        (kData[current_pos + 3] << 24);
+  current_pos += 12;  // Skip frame header.
+  *start_pos = current_pos;
+  *end_pos = current_pos + frame_size;
+}
+
+#else  // !USE_VP8_TESTDATA_INSTEAD_OF_H264
+
 // Returns true if the current position is at the start of a NAL unit.
 static bool LookingAtNAL(const unsigned char* encoded, size_t pos) {
   // H264 frames start with 0, 0, 0, 1 in our test data.
@@ -171,7 +195,6 @@
          encoded[pos + 2] == 0 && encoded[pos + 3] == 1;
 }
 
-// Find the start and end of the next frame.
 static void GetNextFrame(size_t* start_pos, size_t* end_pos) {
   assert(LookingAtNAL(kData, *start_pos));
   *end_pos = *start_pos;
@@ -181,6 +204,8 @@
   }
 }
 
+#endif  // USE_VP8_TESTDATA_INSTEAD_OF_H264
+
 Decoder::Decoder(MyInstance* instance,
                  int id,
                  const pp::Graphics3D& graphics_3d)
@@ -190,14 +215,19 @@
       callback_factory_(this),
       encoded_data_next_pos_to_decode_(0),
       next_picture_id_(0),
-      seek_frame_(0),
       flushing_(false),
       resetting_(false) {
+// TODO(bbudge) Remove this for final patch.
+#if defined USE_VP8_TESTDATA_INSTEAD_OF_H264
+  const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_VP8MAIN;
+#else
+  const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_H264MAIN;
+#endif
+
   assert(!decoder_->is_null());
-  const PP_VideoProfile profile = PP_VIDEOPROFILE_H264MAIN;
   decoder_->Initialize(graphics_3d,
-                       profile,
-                       PP_FALSE /* allow_software_fallback */,
+                       kBitstreamProfile,
+                       PP_TRUE /* allow_software_fallback */,
                        callback_factory_.NewCallback(&Decoder::InitializeDone));
 }
 
@@ -209,18 +239,13 @@
   assert(decoder_);
   assert(result == PP_OK);
   assert(decoding());
-  Start(0);
+  Start();
 }
 
-void Decoder::Start(int frame) {
+void Decoder::Start() {
   assert(decoder_);
 
-  // Skip to |frame|.
-  size_t start_pos = 0;
-  size_t end_pos = 0;
-  for (int i = 0; i < frame; i++)
-    GetNextFrame(&start_pos, &end_pos);
-  encoded_data_next_pos_to_decode_ = end_pos;
+  encoded_data_next_pos_to_decode_ = 0;
 
   // Register callback to get the first picture. We call GetPicture again in
   // PictureReady to continuously receive pictures as they're decoded.
@@ -231,9 +256,8 @@
   DecodeNextFrame();
 }
 
-void Decoder::Seek(int frame) {
+void Decoder::Reset() {
   assert(decoder_);
-  seek_frame_ = frame;
   resetting_ = true;
   decoder_->Reset(callback_factory_.NewCallback(&Decoder::ResetDone));
 }
@@ -299,6 +323,8 @@
   assert(decoder_);
   assert(result == PP_OK);
   resetting_ = false;
+
+  Start();
 }
 
 MyInstance::MyInstance(PP_Instance instance, pp::Module* module)
@@ -353,6 +379,25 @@
   InitializeDecoders();
 }
 
+bool MyInstance::HandleInputEvent(const pp::InputEvent& event) {
+  switch (event.GetType()) {
+    case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
+      pp::MouseInputEvent mouse_event(event);
+      // Reset all decoders on mouse down.
+      if (mouse_event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_LEFT) {
+        for (size_t i = 0; i < video_decoders_.size(); i++) {
+          if (video_decoders_[i]->decoding())
+            video_decoders_[i]->Reset();
+        }
+      }
+      return true;
+    }
+
+    default:
+      return false;
+  }
+}
+
 void MyInstance::InitializeDecoders() {
   assert(video_decoders_.empty());
   // Create two decoders with ids 0 and 1.
diff --git a/ppapi/ppapi_sources.gypi b/ppapi/ppapi_sources.gypi
index 3ce2288..03609b03 100644
--- a/ppapi/ppapi_sources.gypi
+++ b/ppapi/ppapi_sources.gypi
@@ -508,6 +508,8 @@
       'tests/test_var.h',
       'tests/test_var_resource.cc',
       'tests/test_var_resource.h',
+      'tests/test_video_decoder.cc',
+      'tests/test_video_decoder.h',
       'tests/test_video_destination.cc',
       'tests/test_video_destination.h',
       'tests/test_video_source.cc',
diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h
index 2e18fd4ea..41b40ce 100644
--- a/ppapi/proxy/ppapi_messages.h
+++ b/ppapi/proxy/ppapi_messages.h
@@ -1861,10 +1861,11 @@
                      int32_t /* decode_id */)
 IPC_MESSAGE_CONTROL1(PpapiPluginMsg_VideoDecoder_DecodeReply,
                      uint32_t /* shm_id */)
-IPC_MESSAGE_CONTROL3(PpapiPluginMsg_VideoDecoder_RequestTextures,
+IPC_MESSAGE_CONTROL4(PpapiPluginMsg_VideoDecoder_RequestTextures,
                      uint32_t /* num_textures */,
                      PP_Size /* size */,
-                     uint32_t /* texture_target */)
+                     uint32_t /* texture_target */,
+                     std::vector<gpu::Mailbox> /* mailboxes*/)
 IPC_MESSAGE_CONTROL2(PpapiHostMsg_VideoDecoder_AssignTextures,
                      PP_Size /* size */,
                      std::vector<uint32_t> /* texture_ids */)
diff --git a/ppapi/proxy/video_decoder_resource.cc b/ppapi/proxy/video_decoder_resource.cc
index 95365e8b..2ca5ce2 100644
--- a/ppapi/proxy/video_decoder_resource.cc
+++ b/ppapi/proxy/video_decoder_resource.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "gpu/command_buffer/client/gles2_cmd_helper.h"
 #include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/common/mailbox.h"
 #include "ipc/ipc_message.h"
 #include "ppapi/c/pp_errors.h"
 #include "ppapi/c/ppb_opengles2.h"
@@ -112,7 +113,7 @@
                          enter_create.functions()->CreateGraphics3D(
                              pp_instance(), graphics_context, attrib_list));
     EnterResourceNoLock<PPB_Graphics3D_API> enter_graphics(graphics3d_.get(),
-                                                           true);
+                                                           false);
     if (enter_graphics.failed())
       return PP_ERROR_BADRESOURCE;
 
@@ -332,8 +333,10 @@
     const ResourceMessageReplyParams& params,
     uint32_t num_textures,
     const PP_Size& size,
-    uint32_t texture_target) {
+    uint32_t texture_target,
+    const std::vector<gpu::Mailbox>& mailboxes) {
   DCHECK(num_textures);
+  DCHECK(mailboxes.empty() || mailboxes.size() == num_textures);
   std::vector<uint32_t> texture_ids(num_textures);
   if (gles2_impl_) {
     gles2_impl_->GenTextures(num_textures, &texture_ids.front());
@@ -360,6 +363,10 @@
                                 GL_UNSIGNED_BYTE,
                                 NULL);
       }
+      if (!mailboxes.empty()) {
+        gles2_impl_->ProduceTextureCHROMIUM(
+            GL_TEXTURE_2D, reinterpret_cast<const GLbyte*>(mailboxes[i].name));
+      }
 
       textures_.insert(
           std::make_pair(texture_ids[i], Texture(texture_target, size)));
diff --git a/ppapi/proxy/video_decoder_resource.h b/ppapi/proxy/video_decoder_resource.h
index 19ccffb..a466eb2 100644
--- a/ppapi/proxy/video_decoder_resource.h
+++ b/ppapi/proxy/video_decoder_resource.h
@@ -19,6 +19,7 @@
 #include "ppapi/thunk/ppb_video_decoder_api.h"
 
 namespace gpu {
+struct Mailbox;
 namespace gles2 {
 class GLES2Implementation;
 }
@@ -107,7 +108,8 @@
   void OnPluginMsgRequestTextures(const ResourceMessageReplyParams& params,
                                   uint32_t num_textures,
                                   const PP_Size& size,
-                                  uint32_t texture_target);
+                                  uint32_t texture_target,
+                                  const std::vector<gpu::Mailbox>& mailboxes);
   void OnPluginMsgPictureReady(const ResourceMessageReplyParams& params,
                                int32_t decode_id,
                                uint32_t texture_id);
diff --git a/ppapi/proxy/video_decoder_resource_unittest.cc b/ppapi/proxy/video_decoder_resource_unittest.cc
index 228da923..b9f0e79 100644
--- a/ppapi/proxy/video_decoder_resource_unittest.cc
+++ b/ppapi/proxy/video_decoder_resource_unittest.cc
@@ -236,7 +236,10 @@
     SendReply(params,
               PP_OK,
               PpapiPluginMsg_VideoDecoder_RequestTextures(
-                  kNumRequestedTextures, PP_MakeSize(320, 240), GL_TEXTURE_2D));
+                  kNumRequestedTextures,
+                  PP_MakeSize(320, 240),
+                  GL_TEXTURE_2D,
+                  std::vector<gpu::Mailbox>()));
   }
 
   void SendNotifyError(const ResourceMessageCallParams& params, int32_t error) {
diff --git a/ppapi/tests/test_video_decoder.cc b/ppapi/tests/test_video_decoder.cc
new file mode 100644
index 0000000..e801e05
--- /dev/null
+++ b/ppapi/tests/test_video_decoder.cc
@@ -0,0 +1,70 @@
+// 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/tests/test_video_decoder.h"
+
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/cpp/video_decoder.h"
+#include "ppapi/lib/gl/gles2/gl2ext_ppapi.h"
+#include "ppapi/tests/testing_instance.h"
+
+REGISTER_TEST_CASE(VideoDecoder);
+
+static const bool kAllowSoftwareFallback = true;
+
+bool TestVideoDecoder::Init() {
+  video_decoder_interface_ = static_cast<const PPB_VideoDecoder_0_1*>(
+      pp::Module::Get()->GetBrowserInterface(PPB_VIDEODECODER_INTERFACE_0_1));
+  const int width = 16;
+  const int height = 16;
+  const int32_t attribs[] = {PP_GRAPHICS3DATTRIB_WIDTH, width,
+                             PP_GRAPHICS3DATTRIB_HEIGHT, height,
+                             PP_GRAPHICS3DATTRIB_NONE};
+  graphics_3d_ = pp::Graphics3D(instance_, attribs);
+  return video_decoder_interface_ && CheckTestingInterface();
+}
+
+void TestVideoDecoder::RunTests(const std::string& filter) {
+  RUN_CALLBACK_TEST(TestVideoDecoder, Create, filter);
+}
+
+std::string TestVideoDecoder::TestCreate() {
+  // Test that Initialize fails with a bad Graphics3D resource.
+  {
+    pp::VideoDecoder video_decoder(instance_);
+    ASSERT_FALSE(video_decoder.is_null());
+
+    TestCompletionCallback callback(instance_->pp_instance(), callback_type());
+    pp::Graphics3D null_graphics_3d;
+    callback.WaitForResult(video_decoder.Initialize(null_graphics_3d,
+                                                    PP_VIDEOPROFILE_VP8MAIN,
+                                                    kAllowSoftwareFallback,
+                                                    callback.GetCallback()));
+    ASSERT_EQ(PP_ERROR_BADRESOURCE, callback.result());
+  }
+  // Test that Initialize fails with a bad profile enum value.
+  {
+    pp::VideoDecoder video_decoder(instance_);
+    TestCompletionCallback callback(instance_->pp_instance(), callback_type());
+    const PP_VideoProfile kInvalidProfile = static_cast<PP_VideoProfile>(-1);
+    callback.WaitForResult(video_decoder.Initialize(graphics_3d_,
+                                                    kInvalidProfile,
+                                                    kAllowSoftwareFallback,
+                                                    callback.GetCallback()));
+    ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
+  }
+  // Test that Initialize succeeds if we can create a Graphics3D resources and
+  // if we allow software fallback to VP8, which should always be supported.
+  if (!graphics_3d_.is_null()) {
+    pp::VideoDecoder video_decoder(instance_);
+    TestCompletionCallback callback(instance_->pp_instance(), callback_type());
+    callback.WaitForResult(video_decoder.Initialize(graphics_3d_,
+                                                    PP_VIDEOPROFILE_VP8MAIN,
+                                                    kAllowSoftwareFallback,
+                                                    callback.GetCallback()));
+    ASSERT_EQ(PP_OK, callback.result());
+  }
+
+  PASS();
+}
diff --git a/ppapi/tests/test_video_decoder.h b/ppapi/tests/test_video_decoder.h
new file mode 100644
index 0000000..cc6eb11
--- /dev/null
+++ b/ppapi/tests/test_video_decoder.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef PPAPI_TESTS_TEST_VIDEO_DECODER_H_
+#define PPAPI_TESTS_TEST_VIDEO_DECODER_H_
+
+#include <string>
+
+#include "ppapi/c/pp_stdint.h"
+#include "ppapi/c/ppb_video_decoder.h"
+#include "ppapi/cpp/graphics_3d.h"
+#include "ppapi/tests/test_case.h"
+
+class TestVideoDecoder : public TestCase {
+ public:
+  explicit TestVideoDecoder(TestingInstance* instance) : TestCase(instance) {}
+
+ private:
+  // TestCase implementation.
+  virtual bool Init();
+  virtual void RunTests(const std::string& filter);
+
+  std::string TestCreate();
+
+  // Used by the tests that access the C API directly.
+  const PPB_VideoDecoder_0_1* video_decoder_interface_;
+
+  pp::Graphics3D graphics_3d_;
+};
+
+#endif  // PPAPI_TESTS_TEST_VIDEO_DECODER_H_