Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / ppapi / examples / video_decode / video_decode.cc
index d1f7181..ab2cead 100644 (file)
@@ -1,28 +1,31 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright (c) 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 <stdio.h>
 #include <string.h>
 
 #include <iostream>
+#include <queue>
 #include <sstream>
-#include <list>
-#include <map>
-#include <set>
-#include <vector>
 
 #include "ppapi/c/pp_errors.h"
 #include "ppapi/c/ppb_console.h"
 #include "ppapi/c/ppb_opengles2.h"
-#include "ppapi/cpp/dev/video_decoder_client_dev.h"
-#include "ppapi/cpp/dev/video_decoder_dev.h"
 #include "ppapi/cpp/graphics_3d.h"
 #include "ppapi/cpp/graphics_3d_client.h"
+#include "ppapi/cpp/input_event.h"
 #include "ppapi/cpp/instance.h"
 #include "ppapi/cpp/module.h"
 #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"
 
 // Assert |context_| isn't holding any GL Errors.  Done as a macro instead of a
 // function to preserve line number information in the failure message.
-#define assertNoGLError() \
-  assert(!gles2_if_->GetError(context_->pp_resource()));
+#define assertNoGLError() assert(!gles2_if_->GetError(context_->pp_resource()));
 
 namespace {
 
-struct PictureBufferInfo {
-  PP_PictureBuffer_Dev buffer;
-  GLenum texture_target;
-};
-
 struct Shader {
-  Shader() : program(0),
-             texcoord_scale_location(0) {}
+  Shader() : program(0), texcoord_scale_location(0) {}
+  ~Shader() {}
 
   GLuint program;
   GLint texcoord_scale_location;
 };
 
-class VideoDecodeDemoInstance : public pp::Instance,
-                                public pp::Graphics3DClient,
-                                public pp::VideoDecoderClient_Dev {
+class Decoder;
+class MyInstance;
+
+struct PendingPicture {
+  PendingPicture(Decoder* decoder, const PP_VideoPicture& picture)
+      : decoder(decoder), picture(picture) {}
+  ~PendingPicture() {}
+
+  Decoder* decoder;
+  PP_VideoPicture picture;
+};
+
+class MyInstance : public pp::Instance, public pp::Graphics3DClient {
  public:
-  VideoDecodeDemoInstance(PP_Instance instance, pp::Module* module);
-  virtual ~VideoDecodeDemoInstance();
+  MyInstance(PP_Instance instance, pp::Module* module);
+  virtual ~MyInstance();
 
-  // pp::Instance implementation (see PPP_Instance).
+  // 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() {
@@ -73,109 +81,56 @@ class VideoDecodeDemoInstance : public pp::Instance,
     assert(false && "Unexpectedly lost graphics context");
   }
 
-  // pp::VideoDecoderClient_Dev implementation.
-  virtual void ProvidePictureBuffers(
-      PP_Resource decoder,
-      uint32_t req_num_of_bufs,
-      const PP_Size& dimensions,
-      uint32_t texture_target);
-  virtual void DismissPictureBuffer(PP_Resource decoder,
-                                    int32_t picture_buffer_id);
-  virtual void PictureReady(PP_Resource decoder, const PP_Picture_Dev& picture);
-  virtual void NotifyError(PP_Resource decoder, PP_VideoDecodeError_Dev error);
+  void PaintPicture(Decoder* decoder, const PP_VideoPicture& picture);
 
  private:
-  enum { kNumConcurrentDecodes = 7,
-         kNumDecoders = 2 };  // Baked into viewport rendering.
-
-  // A single decoder's client interface.
-  class DecoderClient {
-   public:
-    DecoderClient(VideoDecodeDemoInstance* gles2,
-                  pp::VideoDecoder_Dev* decoder);
-    ~DecoderClient();
-
-    void DecodeNextNALUs();
-
-    // Per-decoder implementation of part of pp::VideoDecoderClient_Dev.
-    void ProvidePictureBuffers(
-        uint32_t req_num_of_bufs,
-        PP_Size dimensions,
-        uint32_t texture_target);
-    void DismissPictureBuffer(int32_t picture_buffer_id);
-
-    const PictureBufferInfo& GetPictureBufferInfoById(int id);
-    pp::VideoDecoder_Dev* decoder() { return decoder_; }
-
-   private:
-    void DecodeNextNALU();
-    static void GetNextNALUBoundary(size_t start_pos, size_t* end_pos);
-    void DecoderBitstreamDone(int32_t result, int bitstream_buffer_id);
-    void DecoderFlushDone(int32_t result);
-
-    VideoDecodeDemoInstance* gles2_;
-    pp::VideoDecoder_Dev* decoder_;
-    pp::CompletionCallbackFactory<DecoderClient> callback_factory_;
-    int next_picture_buffer_id_;
-    int next_bitstream_buffer_id_;
-    size_t encoded_data_next_pos_to_decode_;
-    std::set<int> bitstream_ids_at_decoder_;
-    // Map of texture buffers indexed by buffer id.
-    typedef std::map<int, PictureBufferInfo> PictureBufferMap;
-    PictureBufferMap picture_buffers_by_id_;
-    // Map of bitstream buffers indexed by id.
-    typedef std::map<int, pp::Buffer_Dev*> BitstreamBufferMap;
-    BitstreamBufferMap bitstream_buffers_by_id_;
-  };
-
-  // Initialize Video Decoders.
-  void InitializeDecoders();
-
-  // GL-related functions.
-  void InitGL();
-  GLuint CreateTexture(int32_t width, int32_t height, GLenum texture_target);
-  void CreateGLObjects();
-  void Create2DProgramOnce();
-  void CreateRectangleARBProgramOnce();
-  Shader CreateProgram(const char* vertex_shader,
-                       const char* fragment_shader);
-  void CreateShader(GLuint program, GLenum type, const char* source, int size);
-  void DeleteTexture(GLuint id);
-  void PaintFinished(int32_t result, PP_Resource decoder,
-                     int picture_buffer_id);
-
-  // Log an error to the developer console and stderr (though the latter may be
-  // closed due to sandboxing or blackholed for other reasons) by creating a
-  // temporary of this type and streaming to it.  Example usage:
+  // Log an error to the developer console and stderr by creating a temporary
+  // object of this type and streaming to it.  Example usage:
   // LogError(this).s() << "Hello world: " << 42;
   class LogError {
    public:
-    LogError(VideoDecodeDemoInstance* demo) : demo_(demo) {}
+    LogError(MyInstance* instance) : instance_(instance) {}
     ~LogError() {
       const std::string& msg = stream_.str();
-      demo_->console_if_->Log(demo_->pp_instance(), PP_LOGLEVEL_ERROR,
-                              pp::Var(msg).pp_var());
+      instance_->console_if_->Log(
+          instance_->pp_instance(), PP_LOGLEVEL_ERROR, pp::Var(msg).pp_var());
       std::cerr << msg << std::endl;
     }
     // Impl note: it would have been nicer to have LogError derive from
     // std::ostringstream so that it can be streamed to directly, but lookup
     // rules turn streamed string literals to hex pointers on output.
     std::ostringstream& s() { return stream_; }
+
    private:
-    VideoDecodeDemoInstance* demo_;  // Unowned.
+    MyInstance* instance_;
     std::ostringstream stream_;
   };
 
+  void InitializeDecoders();
+
+  // GL-related functions.
+  void InitGL();
+  void CreateGLObjects();
+  void Create2DProgramOnce();
+  void CreateRectangleARBProgramOnce();
+  void CreateExternalOESProgramOnce();
+  Shader CreateProgram(const char* vertex_shader, const char* fragment_shader);
+  void CreateShader(GLuint program, GLenum type, const char* source, int size);
+  void PaintNextPicture();
+  void PaintFinished(int32_t result);
+
   pp::Size plugin_size_;
   bool is_painting_;
   // When decode outpaces render, we queue up decoded pictures for later
-  // painting.  Elements are <decoder,picture>.
-  std::list<std::pair<PP_Resource, PP_Picture_Dev> > pictures_pending_paint_;
+  // painting.
+  typedef std::queue<PendingPicture> PendingPictureQueue;
+  PendingPictureQueue pending_pictures_;
+
   int num_frames_rendered_;
   PP_TimeTicks first_frame_delivered_ticks_;
   PP_TimeTicks last_swap_request_ticks_;
   PP_TimeTicks swap_ticks_;
-  pp::CompletionCallbackFactory<VideoDecodeDemoInstance> callback_factory_;
+  pp::CompletionCallbackFactory<MyInstance> callback_factory_;
 
   // Unowned pointers.
   const PPB_Console* console_if_;
@@ -184,316 +139,429 @@ class VideoDecodeDemoInstance : public pp::Instance,
 
   // Owned data.
   pp::Graphics3D* context_;
-  typedef std::map<int, DecoderClient*> Decoders;
-  Decoders video_decoders_;
+  typedef std::vector<Decoder*> DecoderList;
+  DecoderList video_decoders_;
 
   // Shader program to draw GL_TEXTURE_2D target.
   Shader shader_2d_;
   // Shader program to draw GL_TEXTURE_RECTANGLE_ARB target.
   Shader shader_rectangle_arb_;
+  // Shader program to draw GL_TEXTURE_EXTERNAL_OES target.
+  Shader shader_external_oes_;
 };
 
-VideoDecodeDemoInstance::DecoderClient::DecoderClient(
-      VideoDecodeDemoInstance* gles2, pp::VideoDecoder_Dev* decoder)
-    : gles2_(gles2), decoder_(decoder), callback_factory_(this),
-      next_picture_buffer_id_(0),
-      next_bitstream_buffer_id_(0), encoded_data_next_pos_to_decode_(0) {
-}
+class Decoder {
+ public:
+  Decoder(MyInstance* instance, int id, const pp::Graphics3D& graphics_3d);
+  ~Decoder();
 
-VideoDecodeDemoInstance::DecoderClient::~DecoderClient() {
-  delete decoder_;
-  decoder_ = NULL;
+  int id() const { return id_; }
+  bool flushing() const { return flushing_; }
+  bool resetting() const { return resetting_; }
+
+  void Reset();
+  void RecyclePicture(const PP_VideoPicture& picture);
 
-  for (BitstreamBufferMap::iterator it = bitstream_buffers_by_id_.begin();
-       it != bitstream_buffers_by_id_.end(); ++it) {
-    delete it->second;
+  PP_TimeTicks GetAverageLatency() {
+    return num_pictures_ ? total_latency_ / num_pictures_ : 0;
   }
-  bitstream_buffers_by_id_.clear();
 
-  for (PictureBufferMap::iterator it = picture_buffers_by_id_.begin();
-       it != picture_buffers_by_id_.end(); ++it) {
-    gles2_->DeleteTexture(it->second.buffer.texture_id);
+ private:
+  void InitializeDone(int32_t result);
+  void Start();
+  void DecodeNextFrame();
+  void DecodeDone(int32_t result);
+  void PictureReady(int32_t result, PP_VideoPicture picture);
+  void FlushDone(int32_t result);
+  void ResetDone(int32_t result);
+
+  MyInstance* instance_;
+  int id_;
+
+  pp::VideoDecoder* decoder_;
+  pp::CompletionCallbackFactory<Decoder> callback_factory_;
+
+  size_t encoded_data_next_pos_to_decode_;
+  int next_picture_id_;
+  bool flushing_;
+  bool resetting_;
+
+  const PPB_Core* core_if_;
+  static const int kMaxDecodeDelay = 128;
+  PP_TimeTicks decode_time_[kMaxDecodeDelay];
+  PP_TimeTicks total_latency_;
+  int num_pictures_;
+};
+
+#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.
+  return pos + 3 < kDataLen && encoded[pos] == 0 && encoded[pos + 1] == 0 &&
+         encoded[pos + 2] == 0 && encoded[pos + 3] == 1;
+}
+
+static void GetNextFrame(size_t* start_pos, size_t* end_pos) {
+  assert(LookingAtNAL(kData, *start_pos));
+  *end_pos = *start_pos;
+  *end_pos += 4;
+  while (*end_pos < kDataLen && !LookingAtNAL(kData, *end_pos)) {
+    ++*end_pos;
   }
-  picture_buffers_by_id_.clear();
 }
 
-VideoDecodeDemoInstance::VideoDecodeDemoInstance(PP_Instance instance,
-                                                 pp::Module* module)
-    : pp::Instance(instance), pp::Graphics3DClient(this),
-      pp::VideoDecoderClient_Dev(this),
-      is_painting_(false),
-      num_frames_rendered_(0),
-      first_frame_delivered_ticks_(-1),
-      swap_ticks_(0),
+#endif  // USE_VP8_TESTDATA_INSTEAD_OF_H264
+
+Decoder::Decoder(MyInstance* instance,
+                 int id,
+                 const pp::Graphics3D& graphics_3d)
+    : instance_(instance),
+      id_(id),
+      decoder_(new pp::VideoDecoder(instance)),
       callback_factory_(this),
-      context_(NULL) {
-  assert((console_if_ = static_cast<const PPB_Console*>(
-      module->GetBrowserInterface(PPB_CONSOLE_INTERFACE))));
-  assert((core_if_ = static_cast<const PPB_Core*>(
-      module->GetBrowserInterface(PPB_CORE_INTERFACE))));
-  assert((gles2_if_ = static_cast<const PPB_OpenGLES2*>(
-      module->GetBrowserInterface(PPB_OPENGLES2_INTERFACE))));
+      encoded_data_next_pos_to_decode_(0),
+      next_picture_id_(0),
+      flushing_(false),
+      resetting_(false),
+      total_latency_(0.0),
+      num_pictures_(0) {
+  core_if_ = static_cast<const PPB_Core*>(
+      pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE));
+
+#if defined USE_VP8_TESTDATA_INSTEAD_OF_H264
+  const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_VP8_ANY;
+#else
+  const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_H264MAIN;
+#endif
+
+  assert(!decoder_->is_null());
+  decoder_->Initialize(graphics_3d,
+                       kBitstreamProfile,
+                       PP_TRUE /* allow_software_fallback */,
+                       callback_factory_.NewCallback(&Decoder::InitializeDone));
 }
 
-VideoDecodeDemoInstance::~VideoDecodeDemoInstance() {
-  if (shader_2d_.program)
-    gles2_if_->DeleteProgram(context_->pp_resource(), shader_2d_.program);
-  if (shader_rectangle_arb_.program) {
-    gles2_if_->DeleteProgram(
-        context_->pp_resource(), shader_rectangle_arb_.program);
-  }
+Decoder::~Decoder() {
+  delete decoder_;
+}
 
-  for (Decoders::iterator it = video_decoders_.begin();
-       it != video_decoders_.end(); ++it) {
-    delete it->second;
-  }
-  video_decoders_.clear();
-  delete context_;
+void Decoder::InitializeDone(int32_t result) {
+  assert(decoder_);
+  assert(result == PP_OK);
+  Start();
 }
 
-void VideoDecodeDemoInstance::DidChangeView(
-    const pp::Rect& position, const pp::Rect& clip_ignored) {
-  if (position.width() == 0 || position.height() == 0)
-    return;
-  if (plugin_size_.width()) {
-    assert(position.size() == plugin_size_);
-    return;
-  }
-  plugin_size_ = position.size();
+void Decoder::Start() {
+  assert(decoder_);
 
-  // Initialize graphics.
-  InitGL();
-  InitializeDecoders();
+  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.
+  decoder_->GetPicture(
+      callback_factory_.NewCallbackWithOutput(&Decoder::PictureReady));
+
+  // Start the decode loop.
+  DecodeNextFrame();
 }
 
-void VideoDecodeDemoInstance::InitializeDecoders() {
-  assert(video_decoders_.empty());
-  for (int i = 0; i < kNumDecoders; ++i) {
-    DecoderClient* client = new DecoderClient(
-        this, new pp::VideoDecoder_Dev(
-            this, *context_, PP_VIDEODECODER_H264PROFILE_MAIN));
-    assert(!client->decoder()->is_null());
-    assert(video_decoders_.insert(std::make_pair(
-        client->decoder()->pp_resource(), client)).second);
-    client->DecodeNextNALUs();
+void Decoder::Reset() {
+  assert(decoder_);
+  assert(!resetting_);
+  resetting_ = true;
+  decoder_->Reset(callback_factory_.NewCallback(&Decoder::ResetDone));
+}
+
+void Decoder::RecyclePicture(const PP_VideoPicture& picture) {
+  assert(decoder_);
+  decoder_->RecyclePicture(picture);
+}
+
+void Decoder::DecodeNextFrame() {
+  assert(decoder_);
+  if (encoded_data_next_pos_to_decode_ <= kDataLen) {
+    // If we've just reached the end of the bitstream, flush and wait.
+    if (!flushing_ && encoded_data_next_pos_to_decode_ == kDataLen) {
+      flushing_ = true;
+      decoder_->Flush(callback_factory_.NewCallback(&Decoder::FlushDone));
+      return;
+    }
+
+    // Find the start of the next frame.
+    size_t start_pos = encoded_data_next_pos_to_decode_;
+    size_t end_pos;
+    GetNextFrame(&start_pos, &end_pos);
+    encoded_data_next_pos_to_decode_ = end_pos;
+    // Decode the frame. On completion, DecodeDone will call DecodeNextFrame
+    // to implement a decode loop.
+    uint32_t size = static_cast<uint32_t>(end_pos - start_pos);
+    decode_time_[next_picture_id_ % kMaxDecodeDelay] = core_if_->GetTimeTicks();
+    decoder_->Decode(next_picture_id_++,
+                     size,
+                     kData + start_pos,
+                     callback_factory_.NewCallback(&Decoder::DecodeDone));
   }
 }
 
-void VideoDecodeDemoInstance::DecoderClient::DecoderBitstreamDone(
-    int32_t result, int bitstream_buffer_id) {
-  assert(bitstream_ids_at_decoder_.erase(bitstream_buffer_id) == 1);
-  BitstreamBufferMap::iterator it =
-      bitstream_buffers_by_id_.find(bitstream_buffer_id);
-  assert(it != bitstream_buffers_by_id_.end());
-  delete it->second;
-  bitstream_buffers_by_id_.erase(it);
-  DecodeNextNALUs();
+void Decoder::DecodeDone(int32_t result) {
+  assert(decoder_);
+  // Break out of the decode loop on abort.
+  if (result == PP_ERROR_ABORTED)
+    return;
+  assert(result == PP_OK);
+  if (!flushing_ && !resetting_)
+    DecodeNextFrame();
 }
 
-void VideoDecodeDemoInstance::DecoderClient::DecoderFlushDone(int32_t result) {
+void Decoder::PictureReady(int32_t result, PP_VideoPicture picture) {
+  assert(decoder_);
+  // Break out of the get picture loop on abort.
+  if (result == PP_ERROR_ABORTED)
+    return;
   assert(result == PP_OK);
-  // Check that each bitstream buffer ID we handed to the decoder got handed
-  // back to us.
-  assert(bitstream_ids_at_decoder_.empty());
-  delete decoder_;
-  decoder_ = NULL;
+
+  num_pictures_++;
+  PP_TimeTicks latency = core_if_->GetTimeTicks() -
+                         decode_time_[picture.decode_id % kMaxDecodeDelay];
+  total_latency_ += latency;
+
+  decoder_->GetPicture(
+      callback_factory_.NewCallbackWithOutput(&Decoder::PictureReady));
+  instance_->PaintPicture(this, picture);
 }
 
-static bool LookingAtNAL(const unsigned char* encoded, size_t pos) {
-  return pos + 3 < kDataLen &&
-      encoded[pos] == 0 && encoded[pos + 1] == 0 &&
-      encoded[pos + 2] == 0 && encoded[pos + 3] == 1;
+void Decoder::FlushDone(int32_t result) {
+  assert(decoder_);
+  assert(result == PP_OK || result == PP_ERROR_ABORTED);
+  assert(flushing_);
+  flushing_ = false;
 }
 
-void VideoDecodeDemoInstance::DecoderClient::GetNextNALUBoundary(
-    size_t start_pos, size_t* end_pos) {
-  assert(LookingAtNAL(kData, start_pos));
-  *end_pos = start_pos;
-  *end_pos += 4;
-  while (*end_pos + 3 < kDataLen &&
-         !LookingAtNAL(kData, *end_pos)) {
-    ++*end_pos;
-  }
-  if (*end_pos + 3 >= kDataLen) {
-    *end_pos = kDataLen;
-    return;
-  }
+void Decoder::ResetDone(int32_t result) {
+  assert(decoder_);
+  assert(result == PP_OK);
+  assert(resetting_);
+  resetting_ = false;
+
+  Start();
 }
 
-void VideoDecodeDemoInstance::DecoderClient::DecodeNextNALUs() {
-  while (encoded_data_next_pos_to_decode_ <= kDataLen &&
-         bitstream_ids_at_decoder_.size() < kNumConcurrentDecodes) {
-    DecodeNextNALU();
-  }
+MyInstance::MyInstance(PP_Instance instance, pp::Module* module)
+    : pp::Instance(instance),
+      pp::Graphics3DClient(this),
+      is_painting_(false),
+      num_frames_rendered_(0),
+      first_frame_delivered_ticks_(-1),
+      last_swap_request_ticks_(-1),
+      swap_ticks_(0),
+      callback_factory_(this),
+      context_(NULL) {
+  console_if_ = static_cast<const PPB_Console*>(
+      pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_INTERFACE));
+  core_if_ = static_cast<const PPB_Core*>(
+      pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE));
+  gles2_if_ = static_cast<const PPB_OpenGLES2*>(
+      pp::Module::Get()->GetBrowserInterface(PPB_OPENGLES2_INTERFACE));
+
+  RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
 }
 
-void VideoDecodeDemoInstance::DecoderClient::DecodeNextNALU() {
-  if (encoded_data_next_pos_to_decode_ == kDataLen) {
-    ++encoded_data_next_pos_to_decode_;
-    pp::CompletionCallback cb = callback_factory_.NewCallback(
-        &VideoDecodeDemoInstance::DecoderClient::DecoderFlushDone);
-    decoder_->Flush(cb);
+MyInstance::~MyInstance() {
+  if (!context_)
     return;
-  }
-  size_t start_pos = encoded_data_next_pos_to_decode_;
-  size_t end_pos;
-  GetNextNALUBoundary(start_pos, &end_pos);
-  pp::Buffer_Dev* buffer = new pp::Buffer_Dev(gles2_, end_pos - start_pos);
-  PP_VideoBitstreamBuffer_Dev bitstream_buffer;
-  int id = ++next_bitstream_buffer_id_;
-  bitstream_buffer.id = id;
-  bitstream_buffer.size = end_pos - start_pos;
-  bitstream_buffer.data = buffer->pp_resource();
-  memcpy(buffer->data(), kData + start_pos, end_pos - start_pos);
-  assert(bitstream_buffers_by_id_.insert(std::make_pair(id, buffer)).second);
-
-  pp::CompletionCallback cb =
-      callback_factory_.NewCallback(
-          &VideoDecodeDemoInstance::DecoderClient::DecoderBitstreamDone, id);
-  assert(bitstream_ids_at_decoder_.insert(id).second);
-  encoded_data_next_pos_to_decode_ = end_pos;
-  decoder_->Decode(bitstream_buffer, cb);
-}
 
-void VideoDecodeDemoInstance::ProvidePictureBuffers(PP_Resource decoder,
-                                                    uint32_t req_num_of_bufs,
-                                                    const PP_Size& dimensions,
-                                                    uint32_t texture_target) {
-  DecoderClient* client = video_decoders_[decoder];
-  assert(client);
-  client->ProvidePictureBuffers(req_num_of_bufs, dimensions, texture_target);
+  PP_Resource graphics_3d = context_->pp_resource();
+  if (shader_2d_.program)
+    gles2_if_->DeleteProgram(graphics_3d, shader_2d_.program);
+  if (shader_rectangle_arb_.program)
+    gles2_if_->DeleteProgram(graphics_3d, shader_rectangle_arb_.program);
+  if (shader_external_oes_.program)
+    gles2_if_->DeleteProgram(graphics_3d, shader_external_oes_.program);
+
+  for (DecoderList::iterator it = video_decoders_.begin();
+       it != video_decoders_.end();
+       ++it)
+    delete *it;
+
+  delete context_;
 }
 
-void VideoDecodeDemoInstance::DecoderClient::ProvidePictureBuffers(
-    uint32_t req_num_of_bufs,
-    PP_Size dimensions,
-    uint32_t texture_target) {
-  std::vector<PP_PictureBuffer_Dev> buffers;
-  for (uint32_t i = 0; i < req_num_of_bufs; ++i) {
-    PictureBufferInfo info;
-    info.buffer.size = dimensions;
-    info.texture_target = texture_target;
-    info.buffer.texture_id = gles2_->CreateTexture(
-        dimensions.width, dimensions.height, info.texture_target);
-    int id = ++next_picture_buffer_id_;
-    info.buffer.id = id;
-    buffers.push_back(info.buffer);
-    assert(picture_buffers_by_id_.insert(std::make_pair(id, info)).second);
+void MyInstance::DidChangeView(const pp::Rect& position,
+                               const pp::Rect& clip_ignored) {
+  if (position.width() == 0 || position.height() == 0)
+    return;
+  if (plugin_size_.width()) {
+    assert(position.size() == plugin_size_);
+    return;
   }
-  decoder_->AssignPictureBuffers(buffers);
-}
+  plugin_size_ = position.size();
 
-const PictureBufferInfo&
-VideoDecodeDemoInstance::DecoderClient::GetPictureBufferInfoById(
-    int id) {
-  PictureBufferMap::iterator it = picture_buffers_by_id_.find(id);
-  assert(it != picture_buffers_by_id_.end());
-  return it->second;
+  // Initialize graphics.
+  InitGL();
+  InitializeDecoders();
 }
 
-void VideoDecodeDemoInstance::DismissPictureBuffer(PP_Resource decoder,
-                                             int32_t picture_buffer_id) {
-  DecoderClient* client = video_decoders_[decoder];
-  assert(client);
-  client->DismissPictureBuffer(picture_buffer_id);
+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) {
+        // Reset decoders.
+        for (size_t i = 0; i < video_decoders_.size(); i++) {
+          if (!video_decoders_[i]->resetting())
+            video_decoders_[i]->Reset();
+        }
+      }
+      return true;
+    }
+
+    default:
+      return false;
+  }
 }
 
-void VideoDecodeDemoInstance::DecoderClient::DismissPictureBuffer(
-    int32_t picture_buffer_id) {
-  gles2_->DeleteTexture(GetPictureBufferInfoById(
-      picture_buffer_id).buffer.texture_id);
-  picture_buffers_by_id_.erase(picture_buffer_id);
+void MyInstance::InitializeDecoders() {
+  assert(video_decoders_.empty());
+  // Create two decoders with ids 0 and 1.
+  video_decoders_.push_back(new Decoder(this, 0, *context_));
+  video_decoders_.push_back(new Decoder(this, 1, *context_));
 }
 
-void VideoDecodeDemoInstance::PictureReady(PP_Resource decoder,
-                                     const PP_Picture_Dev& picture) {
+void MyInstance::PaintPicture(Decoder* decoder,
+                              const PP_VideoPicture& picture) {
   if (first_frame_delivered_ticks_ == -1)
     assert((first_frame_delivered_ticks_ = core_if_->GetTimeTicks()) != -1);
-  if (is_painting_) {
-    pictures_pending_paint_.push_back(std::make_pair(decoder, picture));
-    return;
-  }
-  DecoderClient* client = video_decoders_[decoder];
-  assert(client);
-  const PictureBufferInfo& info =
-      client->GetPictureBufferInfoById(picture.picture_buffer_id);
+
+  pending_pictures_.push(PendingPicture(decoder, picture));
+  if (!is_painting_)
+    PaintNextPicture();
+}
+
+void MyInstance::PaintNextPicture() {
   assert(!is_painting_);
   is_painting_ = true;
+
+  const PendingPicture& next = pending_pictures_.front();
+  Decoder* decoder = next.decoder;
+  const PP_VideoPicture& picture = next.picture;
+
   int x = 0;
   int y = 0;
-  if (client != video_decoders_.begin()->second) {
-    x = plugin_size_.width() / kNumDecoders;
-    y = plugin_size_.height() / kNumDecoders;
+  int half_width = plugin_size_.width() / 2;
+  int half_height = plugin_size_.height() / 2;
+  if (decoder->id() != 0) {
+    x = half_width;
+    y = half_height;
   }
 
-  if (info.texture_target == GL_TEXTURE_2D) {
+  PP_Resource graphics_3d = context_->pp_resource();
+  if (picture.texture_target == GL_TEXTURE_2D) {
     Create2DProgramOnce();
-    gles2_if_->UseProgram(context_->pp_resource(), shader_2d_.program);
+    gles2_if_->UseProgram(graphics_3d, shader_2d_.program);
     gles2_if_->Uniform2f(
-        context_->pp_resource(), shader_2d_.texcoord_scale_location, 1.0, 1.0);
-  } else {
-    assert(info.texture_target == GL_TEXTURE_RECTANGLE_ARB);
+        graphics_3d, shader_2d_.texcoord_scale_location, 1.0, 1.0);
+  } else if (picture.texture_target == GL_TEXTURE_RECTANGLE_ARB) {
     CreateRectangleARBProgramOnce();
-    gles2_if_->UseProgram(
-        context_->pp_resource(), shader_rectangle_arb_.program);
-    gles2_if_->Uniform2f(context_->pp_resource(),
+    gles2_if_->UseProgram(graphics_3d, shader_rectangle_arb_.program);
+    gles2_if_->Uniform2f(graphics_3d,
                          shader_rectangle_arb_.texcoord_scale_location,
-                         info.buffer.size.width,
-                         info.buffer.size.height);
+                         picture.texture_size.width,
+                         picture.texture_size.height);
+  } else {
+    assert(picture.texture_target == GL_TEXTURE_EXTERNAL_OES);
+    CreateExternalOESProgramOnce();
+    gles2_if_->UseProgram(graphics_3d, shader_external_oes_.program);
+    gles2_if_->Uniform2f(
+        graphics_3d, shader_external_oes_.texcoord_scale_location, 1.0, 1.0);
   }
 
-  gles2_if_->Viewport(context_->pp_resource(), x, y,
-                      plugin_size_.width() / kNumDecoders,
-                      plugin_size_.height() / kNumDecoders);
-  gles2_if_->ActiveTexture(context_->pp_resource(), GL_TEXTURE0);
+  gles2_if_->Viewport(graphics_3d, x, y, half_width, half_height);
+  gles2_if_->ActiveTexture(graphics_3d, GL_TEXTURE0);
   gles2_if_->BindTexture(
-      context_->pp_resource(), info.texture_target, info.buffer.texture_id);
-  gles2_if_->DrawArrays(context_->pp_resource(), GL_TRIANGLE_STRIP, 0, 4);
+      graphics_3d, picture.texture_target, picture.texture_id);
+  gles2_if_->DrawArrays(graphics_3d, GL_TRIANGLE_STRIP, 0, 4);
 
-  gles2_if_->UseProgram(context_->pp_resource(), 0);
+  gles2_if_->UseProgram(graphics_3d, 0);
 
-  pp::CompletionCallback cb =
-      callback_factory_.NewCallback(
-          &VideoDecodeDemoInstance::PaintFinished, decoder, info.buffer.id);
   last_swap_request_ticks_ = core_if_->GetTimeTicks();
-  assert(context_->SwapBuffers(cb) == PP_OK_COMPLETIONPENDING);
+  context_->SwapBuffers(
+      callback_factory_.NewCallback(&MyInstance::PaintFinished));
 }
 
-void VideoDecodeDemoInstance::NotifyError(PP_Resource decoder,
-                                          PP_VideoDecodeError_Dev error) {
-  LogError(this).s() << "Received error: " << error;
-  assert(false && "Unexpected error; see stderr for details");
-}
+void MyInstance::PaintFinished(int32_t result) {
+  assert(result == PP_OK);
+  swap_ticks_ += core_if_->GetTimeTicks() - last_swap_request_ticks_;
+  is_painting_ = false;
+  ++num_frames_rendered_;
+  if (num_frames_rendered_ % 50 == 0) {
+    double elapsed = core_if_->GetTimeTicks() - first_frame_delivered_ticks_;
+    double fps = (elapsed > 0) ? num_frames_rendered_ / elapsed : 1000;
+    double ms_per_swap = (swap_ticks_ * 1e3) / num_frames_rendered_;
+    double secs_average_latency = 0;
+    for (DecoderList::iterator it = video_decoders_.begin();
+         it != video_decoders_.end();
+         ++it)
+      secs_average_latency += (*it)->GetAverageLatency();
+    secs_average_latency /= video_decoders_.size();
+    double ms_average_latency = 1000 * secs_average_latency;
+    LogError(this).s() << "Rendered frames: " << num_frames_rendered_
+                       << ", fps: " << fps
+                       << ", with average ms/swap of: " << ms_per_swap
+                       << ", with average latency (ms) of: "
+                       << ms_average_latency;
+  }
 
-// This object is the global object representing this plugin library as long
-// as it is loaded.
-class VideoDecodeDemoModule : public pp::Module {
- public:
-  VideoDecodeDemoModule() : pp::Module() {}
-  virtual ~VideoDecodeDemoModule() {}
+  // If the decoders were reset, this will be empty.
+  if (pending_pictures_.empty())
+    return;
 
-  virtual pp::Instance* CreateInstance(PP_Instance instance) {
-    return new VideoDecodeDemoInstance(instance, this);
-  }
-};
+  const PendingPicture& next = pending_pictures_.front();
+  Decoder* decoder = next.decoder;
+  const PP_VideoPicture& picture = next.picture;
+  decoder->RecyclePicture(picture);
+  pending_pictures_.pop();
 
-void VideoDecodeDemoInstance::InitGL() {
+  // Keep painting as long as we have pictures.
+  if (!pending_pictures_.empty())
+    PaintNextPicture();
+}
+
+void MyInstance::InitGL() {
   assert(plugin_size_.width() && plugin_size_.height());
   is_painting_ = false;
 
   assert(!context_);
   int32_t context_attributes[] = {
-    PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
-    PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8,
-    PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8,
-    PP_GRAPHICS3DATTRIB_RED_SIZE, 8,
-    PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0,
-    PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0,
-    PP_GRAPHICS3DATTRIB_SAMPLES, 0,
-    PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
-    PP_GRAPHICS3DATTRIB_WIDTH, plugin_size_.width(),
-    PP_GRAPHICS3DATTRIB_HEIGHT, plugin_size_.height(),
-    PP_GRAPHICS3DATTRIB_NONE,
+      PP_GRAPHICS3DATTRIB_ALPHA_SIZE,     8,
+      PP_GRAPHICS3DATTRIB_BLUE_SIZE,      8,
+      PP_GRAPHICS3DATTRIB_GREEN_SIZE,     8,
+      PP_GRAPHICS3DATTRIB_RED_SIZE,       8,
+      PP_GRAPHICS3DATTRIB_DEPTH_SIZE,     0,
+      PP_GRAPHICS3DATTRIB_STENCIL_SIZE,   0,
+      PP_GRAPHICS3DATTRIB_SAMPLES,        0,
+      PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
+      PP_GRAPHICS3DATTRIB_WIDTH,          plugin_size_.width(),
+      PP_GRAPHICS3DATTRIB_HEIGHT,         plugin_size_.height(),
+      PP_GRAPHICS3DATTRIB_NONE,
   };
   context_ = new pp::Graphics3D(this, context_attributes);
   assert(!context_->is_null());
@@ -508,80 +576,23 @@ void VideoDecodeDemoInstance::InitGL() {
   CreateGLObjects();
 }
 
-void VideoDecodeDemoInstance::PaintFinished(int32_t result, PP_Resource decoder,
-                                      int picture_buffer_id) {
-  assert(result == PP_OK);
-  swap_ticks_ += core_if_->GetTimeTicks() - last_swap_request_ticks_;
-  is_painting_ = false;
-  ++num_frames_rendered_;
-  if (num_frames_rendered_ % 50 == 0) {
-    double elapsed = core_if_->GetTimeTicks() - first_frame_delivered_ticks_;
-    double fps = (elapsed > 0) ? num_frames_rendered_ / elapsed : 1000;
-    double ms_per_swap = (swap_ticks_ * 1e3) / num_frames_rendered_;
-    LogError(this).s() << "Rendered frames: " << num_frames_rendered_
-                       << ", fps: " << fps << ", with average ms/swap of: "
-                       << ms_per_swap;
-  }
-  DecoderClient* client = video_decoders_[decoder];
-  if (client && client->decoder())
-    client->decoder()->ReusePictureBuffer(picture_buffer_id);
-  if (!pictures_pending_paint_.empty()) {
-    std::pair<PP_Resource, PP_Picture_Dev> decoder_picture =
-        pictures_pending_paint_.front();
-    pictures_pending_paint_.pop_front();
-    PictureReady(decoder_picture.first, decoder_picture.second);
-  }
-}
-
-GLuint VideoDecodeDemoInstance::CreateTexture(int32_t width,
-                                              int32_t height,
-                                              GLenum texture_target) {
-  GLuint texture_id;
-  gles2_if_->GenTextures(context_->pp_resource(), 1, &texture_id);
-  assertNoGLError();
-  // Assign parameters.
-  gles2_if_->ActiveTexture(context_->pp_resource(), GL_TEXTURE0);
-  gles2_if_->BindTexture(context_->pp_resource(), texture_target, texture_id);
-  gles2_if_->TexParameteri(
-      context_->pp_resource(), texture_target, GL_TEXTURE_MIN_FILTER,
-      GL_NEAREST);
-  gles2_if_->TexParameteri(
-      context_->pp_resource(), texture_target, GL_TEXTURE_MAG_FILTER,
-      GL_NEAREST);
-  gles2_if_->TexParameterf(
-      context_->pp_resource(), texture_target, GL_TEXTURE_WRAP_S,
-      GL_CLAMP_TO_EDGE);
-  gles2_if_->TexParameterf(
-      context_->pp_resource(), texture_target, GL_TEXTURE_WRAP_T,
-      GL_CLAMP_TO_EDGE);
-
-  if (texture_target == GL_TEXTURE_2D) {
-    gles2_if_->TexImage2D(
-        context_->pp_resource(), texture_target, 0, GL_RGBA, width, height, 0,
-        GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-  }
-  assertNoGLError();
-  return texture_id;
-}
-
-void VideoDecodeDemoInstance::DeleteTexture(GLuint id) {
-  gles2_if_->DeleteTextures(context_->pp_resource(), 1, &id);
-}
-
-void VideoDecodeDemoInstance::CreateGLObjects() {
+void MyInstance::CreateGLObjects() {
   // Assign vertex positions and texture coordinates to buffers for use in
   // shader program.
   static const float kVertices[] = {
-    -1, 1, -1, -1, 1, 1, 1, -1,  // Position coordinates.
-    0, 1, 0, 0, 1, 1, 1, 0,      // Texture coordinates.
+      -1, -1, -1, 1, 1, -1, 1, 1,  // Position coordinates.
+      0,  1,  0,  0, 1, 1,  1, 0,  // Texture coordinates.
   };
 
   GLuint buffer;
   gles2_if_->GenBuffers(context_->pp_resource(), 1, &buffer);
   gles2_if_->BindBuffer(context_->pp_resource(), GL_ARRAY_BUFFER, buffer);
 
-  gles2_if_->BufferData(context_->pp_resource(), GL_ARRAY_BUFFER,
-                        sizeof(kVertices), kVertices, GL_STATIC_DRAW);
+  gles2_if_->BufferData(context_->pp_resource(),
+                        GL_ARRAY_BUFFER,
+                        sizeof(kVertices),
+                        kVertices,
+                        GL_STATIC_DRAW);
   assertNoGLError();
 }
 
@@ -596,7 +607,7 @@ static const char kVertexShader[] =
     "    gl_Position = a_position;       \n"
     "}";
 
-void VideoDecodeDemoInstance::Create2DProgramOnce() {
+void MyInstance::Create2DProgramOnce() {
   if (shader_2d_.program)
     return;
   static const char kFragmentShader2D[] =
@@ -611,7 +622,7 @@ void VideoDecodeDemoInstance::Create2DProgramOnce() {
   assertNoGLError();
 }
 
-void VideoDecodeDemoInstance::CreateRectangleARBProgramOnce() {
+void MyInstance::CreateRectangleARBProgramOnce() {
   if (shader_rectangle_arb_.program)
     return;
   static const char kFragmentShaderRectangle[] =
@@ -625,24 +636,44 @@ void VideoDecodeDemoInstance::CreateRectangleARBProgramOnce() {
       "}";
   shader_rectangle_arb_ =
       CreateProgram(kVertexShader, kFragmentShaderRectangle);
+  assertNoGLError();
 }
 
-Shader VideoDecodeDemoInstance::CreateProgram(const char* vertex_shader,
-                                              const char* fragment_shader) {
+void MyInstance::CreateExternalOESProgramOnce() {
+  if (shader_external_oes_.program)
+    return;
+  static const char kFragmentShaderExternal[] =
+      "#extension GL_OES_EGL_image_external : require\n"
+      "precision mediump float;            \n"
+      "varying vec2 v_texCoord;            \n"
+      "uniform samplerExternalOES s_texture; \n"
+      "void main()                         \n"
+      "{"
+      "    gl_FragColor = texture2D(s_texture, v_texCoord); \n"
+      "}";
+  shader_external_oes_ = CreateProgram(kVertexShader, kFragmentShaderExternal);
+  assertNoGLError();
+}
+
+Shader MyInstance::CreateProgram(const char* vertex_shader,
+                                 const char* fragment_shader) {
   Shader shader;
 
   // Create shader program.
   shader.program = gles2_if_->CreateProgram(context_->pp_resource());
-  CreateShader(shader.program, GL_VERTEX_SHADER, vertex_shader,
-               strlen(vertex_shader));
-  CreateShader(shader.program, GL_FRAGMENT_SHADER, fragment_shader,
+  CreateShader(
+      shader.program, GL_VERTEX_SHADER, vertex_shader, strlen(vertex_shader));
+  CreateShader(shader.program,
+               GL_FRAGMENT_SHADER,
+               fragment_shader,
                strlen(fragment_shader));
   gles2_if_->LinkProgram(context_->pp_resource(), shader.program);
   gles2_if_->UseProgram(context_->pp_resource(), shader.program);
   gles2_if_->Uniform1i(
       context_->pp_resource(),
       gles2_if_->GetUniformLocation(
-          context_->pp_resource(), shader.program, "s_texture"), 0);
+          context_->pp_resource(), shader.program, "s_texture"),
+      0);
   assertNoGLError();
 
   shader.texcoord_scale_location = gles2_if_->GetUniformLocation(
@@ -655,11 +686,16 @@ Shader VideoDecodeDemoInstance::CreateProgram(const char* vertex_shader,
   assertNoGLError();
 
   gles2_if_->EnableVertexAttribArray(context_->pp_resource(), pos_location);
-  gles2_if_->VertexAttribPointer(context_->pp_resource(), pos_location, 2,
-                                 GL_FLOAT, GL_FALSE, 0, 0);
+  gles2_if_->VertexAttribPointer(
+      context_->pp_resource(), pos_location, 2, GL_FLOAT, GL_FALSE, 0, 0);
   gles2_if_->EnableVertexAttribArray(context_->pp_resource(), tc_location);
   gles2_if_->VertexAttribPointer(
-      context_->pp_resource(), tc_location, 2, GL_FLOAT, GL_FALSE, 0,
+      context_->pp_resource(),
+      tc_location,
+      2,
+      GL_FLOAT,
+      GL_FALSE,
+      0,
       static_cast<float*>(0) + 8);  // Skip position coordinates.
 
   gles2_if_->UseProgram(context_->pp_resource(), 0);
@@ -667,19 +703,34 @@ Shader VideoDecodeDemoInstance::CreateProgram(const char* vertex_shader,
   return shader;
 }
 
-void VideoDecodeDemoInstance::CreateShader(
-    GLuint program, GLenum type, const char* source, int size) {
+void MyInstance::CreateShader(GLuint program,
+                              GLenum type,
+                              const char* source,
+                              int size) {
   GLuint shader = gles2_if_->CreateShader(context_->pp_resource(), type);
   gles2_if_->ShaderSource(context_->pp_resource(), shader, 1, &source, &size);
   gles2_if_->CompileShader(context_->pp_resource(), shader);
   gles2_if_->AttachShader(context_->pp_resource(), program, shader);
   gles2_if_->DeleteShader(context_->pp_resource(), shader);
 }
+
+// This object is the global object representing this plugin library as long
+// as it is loaded.
+class MyModule : public pp::Module {
+ public:
+  MyModule() : pp::Module() {}
+  virtual ~MyModule() {}
+
+  virtual pp::Instance* CreateInstance(PP_Instance instance) {
+    return new MyInstance(instance, this);
+  }
+};
+
 }  // anonymous namespace
 
 namespace pp {
 // Factory function for your specialization of the Module object.
 Module* CreateModule() {
-  return new VideoDecodeDemoModule();
+  return new MyModule();
 }
 }  // namespace pp