Index: ppapi/examples/gles2/gles2.cc |
diff --git a/ppapi/examples/gles2/gles2.cc b/ppapi/examples/gles2/gles2.cc |
index 70c6596fa84acf4163f7f1b6fbe4cf2b7f0721ae..1a3a09bd5ebaca855c0fe96e3e03a2ae04c8f9ac 100644 |
--- a/ppapi/examples/gles2/gles2.cc |
+++ b/ppapi/examples/gles2/gles2.cc |
@@ -71,22 +71,48 @@ class GLES2DemoInstance : public pp::Instance, |
virtual void NotifyError(PP_Resource decoder, PP_VideoDecodeError_Dev error); |
private: |
- enum { kNumConcurrentDecodes = 7 }; |
+ enum { kNumConcurrentDecodes = 7, |
+ kNumDecoders = 2 }; // Baked into viewport rendering. |
- // Initialize Video Decoder. |
- void InitializeDecoder(); |
+ // A single decoder's client interface. |
+ class DecoderClient { |
+ public: |
+ DecoderClient(GLES2DemoInstance* gles2, pp::VideoDecoder_Dev* decoder); |
+ ~DecoderClient(); |
+ |
+ void DecodeNextNALUs(); |
- // Callbacks passed into pp:VideoDecoder_Dev functions. |
- void DecoderInitDone(int32_t result); |
- void DecoderBitstreamDone(int32_t result, int bitstream_buffer_id); |
- void DecoderFlushDone(int32_t result); |
+ // Per-decoder implementation of part of pp::VideoDecoderClient_Dev. |
+ void ProvidePictureBuffers(uint32_t req_num_of_bufs, |
+ PP_Size dimensions); |
+ void DismissPictureBuffer(int32_t picture_buffer_id); |
- // Decode helpers. |
- void DecodeNextNALUs(); |
- void DecodeNextNALU(); |
- void GetNextNALUBoundary(size_t start_pos, size_t* end_pos); |
- void PaintStart(const PP_Picture_Dev& picture); |
- void DeleteOutstandingBitstreamBuffers(); |
+ const PP_PictureBuffer_Dev& GetPictureBufferById(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); |
+ |
+ GLES2DemoInstance* 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, PP_PictureBuffer_Dev> 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(); |
@@ -94,8 +120,8 @@ class GLES2DemoInstance : public pp::Instance, |
void CreateGLObjects(); |
void CreateShader(GLuint program, GLenum type, const char* source, int size); |
void DeleteTexture(GLuint id); |
- void DeleteOutstandingTextures(); |
- void PaintFinished(int32_t result, int picture_buffer_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 |
@@ -119,27 +145,16 @@ class GLES2DemoInstance : public pp::Instance, |
std::ostringstream stream_; |
}; |
- pp::Size position_size_; |
- int next_picture_buffer_id_; |
- int next_bitstream_buffer_id_; |
+ pp::Size plugin_size_; |
bool is_painting_; |
- pp::CompletionCallbackFactory<GLES2DemoInstance> callback_factory_; |
- size_t encoded_data_next_pos_to_decode_; |
- std::set<int> bitstream_ids_at_decoder_; |
// When decode outpaces render, we queue up decoded pictures for later |
- // painting. |
- std::list<PP_Picture_Dev> pictures_pending_paint_; |
+ // painting. Elements are <decoder,picture>. |
+ std::list<std::pair<PP_Resource, PP_Picture_Dev> > pictures_pending_paint_; |
int num_frames_rendered_; |
- |
- // Map of texture buffers indexed by buffer id. |
- typedef std::map<int, PP_PictureBuffer_Dev> PictureBufferMap; |
- PictureBufferMap buffers_by_id_; |
- // Map of bitstream buffers indexed by id. |
- typedef std::map<int, pp::Buffer_Dev*> BitstreamBufferMap; |
- BitstreamBufferMap bitstream_buffers_by_id_; |
PP_TimeTicks first_frame_delivered_ticks_; |
PP_TimeTicks last_swap_request_ticks_; |
PP_TimeTicks swap_ticks_; |
+ pp::CompletionCallbackFactory<GLES2DemoInstance> callback_factory_; |
// Unowned pointers. |
const struct PPB_Console_Dev* console_if_; |
@@ -149,22 +164,43 @@ class GLES2DemoInstance : public pp::Instance, |
// Owned data. |
pp::Context3D_Dev* context_; |
pp::Surface3D_Dev* surface_; |
- pp::VideoDecoder_Dev* video_decoder_; |
+ typedef std::map<int, DecoderClient*> Decoders; |
+ Decoders video_decoders_; |
+}; |
+ |
+GLES2DemoInstance::DecoderClient::DecoderClient(GLES2DemoInstance* 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) { |
}; |
+GLES2DemoInstance::DecoderClient::~DecoderClient() { |
+ delete decoder_; |
+ decoder_ = NULL; |
+ |
+ for (BitstreamBufferMap::iterator it = bitstream_buffers_by_id_.begin(); |
+ it != bitstream_buffers_by_id_.end(); ++it) { |
+ delete it->second; |
+ } |
+ 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.texture_id); |
+ } |
+ picture_buffers_by_id_.clear(); |
+} |
+ |
GLES2DemoInstance::GLES2DemoInstance(PP_Instance instance, pp::Module* module) |
: pp::Instance(instance), pp::Graphics3DClient_Dev(this), |
pp::VideoDecoderClient_Dev(this), |
- next_picture_buffer_id_(0), |
- next_bitstream_buffer_id_(0), |
- callback_factory_(this), |
- encoded_data_next_pos_to_decode_(0), |
num_frames_rendered_(0), |
first_frame_delivered_ticks_(-1), |
swap_ticks_(0), |
+ callback_factory_(this), |
context_(NULL), |
- surface_(NULL), |
- video_decoder_(NULL) { |
+ surface_(NULL) { |
assert((console_if_ = static_cast<const struct PPB_Console_Dev*>( |
module->GetBrowserInterface(PPB_CONSOLE_DEV_INTERFACE)))); |
assert((core_if_ = static_cast<const struct PPB_Core*>( |
@@ -174,55 +210,45 @@ GLES2DemoInstance::GLES2DemoInstance(PP_Instance instance, pp::Module* module) |
} |
GLES2DemoInstance::~GLES2DemoInstance() { |
- delete video_decoder_; // May be NULL, which is fine. |
- DeleteOutstandingBitstreamBuffers(); |
- DeleteOutstandingTextures(); |
- delete surface_; |
- delete context_; |
-} |
- |
-void GLES2DemoInstance::DeleteOutstandingTextures() { |
- for (PictureBufferMap::iterator it = buffers_by_id_.begin(); |
- it != buffers_by_id_.end(); ++it) { |
- DeleteTexture(it->second.texture_id); |
- } |
- buffers_by_id_.clear(); |
-} |
- |
-void GLES2DemoInstance::DeleteOutstandingBitstreamBuffers() { |
- for (BitstreamBufferMap::iterator it = bitstream_buffers_by_id_.begin(); |
- it != bitstream_buffers_by_id_.end(); ++it) { |
+ for (Decoders::iterator it = video_decoders_.begin(); |
+ it != video_decoders_.end(); ++it) { |
delete it->second; |
} |
- bitstream_buffers_by_id_.clear(); |
+ video_decoders_.clear(); |
+ delete surface_; |
+ delete context_; |
} |
void GLES2DemoInstance::DidChangeView( |
const pp::Rect& position, const pp::Rect& clip_ignored) { |
if (position.width() == 0 || position.height() == 0) |
return; |
- if (position_size_.width()) { |
- assert(position.size() == position_size_); |
+ if (plugin_size_.width()) { |
+ assert(position.size() == plugin_size_); |
return; |
} |
- position_size_ = position.size(); |
+ plugin_size_ = position.size(); |
// Initialize graphics. |
InitGL(); |
- InitializeDecoder(); |
+ InitializeDecoders(); |
} |
-void GLES2DemoInstance::InitializeDecoder() { |
+void GLES2DemoInstance::InitializeDecoders() { |
PP_VideoConfigElement configs = PP_VIDEOATTR_DICTIONARY_TERMINATOR; |
- assert(!video_decoder_); |
- video_decoder_ = new pp::VideoDecoder_Dev(*this, *context_, &configs); |
- assert(!video_decoder_->is_null()); |
- |
- DecodeNextNALUs(); |
+ assert(video_decoders_.empty()); |
+ for (int i = 0; i < kNumDecoders; ++i) { |
+ DecoderClient* client = new DecoderClient( |
+ this, new pp::VideoDecoder_Dev(*this, *context_, &configs)); |
+ assert(!client->decoder()->is_null()); |
+ assert(video_decoders_.insert(std::make_pair( |
+ client->decoder()->pp_resource(), client)).second); |
+ client->DecodeNextNALUs(); |
+ } |
} |
-void GLES2DemoInstance::DecoderBitstreamDone( |
+void GLES2DemoInstance::DecoderClient::DecoderBitstreamDone( |
int32_t result, int bitstream_buffer_id) { |
assert(bitstream_ids_at_decoder_.erase(bitstream_buffer_id) == 1); |
BitstreamBufferMap::iterator it = |
@@ -233,12 +259,13 @@ void GLES2DemoInstance::DecoderBitstreamDone( |
DecodeNextNALUs(); |
} |
-void GLES2DemoInstance::DecoderFlushDone(int32_t result) { |
+void GLES2DemoInstance::DecoderClient::DecoderFlushDone(int32_t result) { |
+ 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 video_decoder_; |
- video_decoder_ = NULL; |
+ delete decoder_; |
+ decoder_ = NULL; |
} |
static bool LookingAtNAL(const unsigned char* encoded, size_t pos) { |
@@ -247,7 +274,7 @@ static bool LookingAtNAL(const unsigned char* encoded, size_t pos) { |
encoded[pos + 2] == 0 && encoded[pos + 3] == 1; |
} |
-void GLES2DemoInstance::GetNextNALUBoundary( |
+void GLES2DemoInstance::DecoderClient::GetNextNALUBoundary( |
size_t start_pos, size_t* end_pos) { |
assert(LookingAtNAL(kData, start_pos)); |
*end_pos = start_pos; |
@@ -262,25 +289,25 @@ void GLES2DemoInstance::GetNextNALUBoundary( |
} |
} |
-void GLES2DemoInstance::DecodeNextNALUs() { |
+void GLES2DemoInstance::DecoderClient::DecodeNextNALUs() { |
while (encoded_data_next_pos_to_decode_ <= kDataLen && |
bitstream_ids_at_decoder_.size() < kNumConcurrentDecodes) { |
DecodeNextNALU(); |
} |
} |
-void GLES2DemoInstance::DecodeNextNALU() { |
+void GLES2DemoInstance::DecoderClient::DecodeNextNALU() { |
if (encoded_data_next_pos_to_decode_ == kDataLen) { |
++encoded_data_next_pos_to_decode_; |
- pp::CompletionCallback cb = |
- callback_factory_.NewCallback(&GLES2DemoInstance::DecoderFlushDone); |
- video_decoder_->Flush(cb); |
+ pp::CompletionCallback cb = callback_factory_.NewCallback( |
+ &GLES2DemoInstance::DecoderClient::DecoderFlushDone); |
+ decoder_->Flush(cb); |
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(this, end_pos - start_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; |
@@ -291,73 +318,95 @@ void GLES2DemoInstance::DecodeNextNALU() { |
pp::CompletionCallback cb = |
callback_factory_.NewCallback( |
- &GLES2DemoInstance::DecoderBitstreamDone, id); |
+ &GLES2DemoInstance::DecoderClient::DecoderBitstreamDone, id); |
assert(bitstream_ids_at_decoder_.insert(id).second); |
- video_decoder_->Decode(bitstream_buffer, cb); |
encoded_data_next_pos_to_decode_ = end_pos; |
+ decoder_->Decode(bitstream_buffer, cb); |
} |
void GLES2DemoInstance::ProvidePictureBuffers( |
PP_Resource decoder, uint32_t req_num_of_bufs, PP_Size dimensions) { |
- assert(decoder == video_decoder_->pp_resource()); |
+ DecoderClient* client = video_decoders_[decoder]; |
+ assert(client); |
+ client->ProvidePictureBuffers(req_num_of_bufs, dimensions); |
+} |
+ |
+void GLES2DemoInstance::DecoderClient::ProvidePictureBuffers( |
+ uint32_t req_num_of_bufs, PP_Size dimensions) { |
std::vector<PP_PictureBuffer_Dev> buffers; |
- for (uint32_t i = 0; i < req_num_of_bufs; i++) { |
+ for (uint32_t i = 0; i < req_num_of_bufs; ++i) { |
PP_PictureBuffer_Dev buffer; |
- buffer.texture_id = CreateTexture(dimensions.width, dimensions.height); |
+ buffer.size = dimensions; |
+ buffer.texture_id = |
+ gles2_->CreateTexture(dimensions.width, dimensions.height); |
int id = ++next_picture_buffer_id_; |
buffer.id = id; |
buffers.push_back(buffer); |
- assert(buffers_by_id_.insert(std::make_pair(id, buffer)).second); |
+ assert(picture_buffers_by_id_.insert(std::make_pair(id, buffer)).second); |
} |
- video_decoder_->AssignPictureBuffers(buffers); |
+ decoder_->AssignPictureBuffers(buffers); |
+} |
+ |
+const PP_PictureBuffer_Dev& |
+GLES2DemoInstance::DecoderClient::GetPictureBufferById( |
+ int id) { |
+ PictureBufferMap::iterator it = picture_buffers_by_id_.find(id); |
+ assert(it != picture_buffers_by_id_.end()); |
+ return it->second; |
} |
void GLES2DemoInstance::DismissPictureBuffer(PP_Resource decoder, |
int32_t picture_buffer_id) { |
- assert(decoder == video_decoder_->pp_resource()); |
- PictureBufferMap::iterator it = buffers_by_id_.find(picture_buffer_id); |
- assert(it != buffers_by_id_.end()); |
- DeleteTexture(it->second.texture_id); |
- buffers_by_id_.erase(it); |
+ DecoderClient* client = video_decoders_[decoder]; |
+ assert(client); |
+ client->DismissPictureBuffer(picture_buffer_id); |
} |
-void GLES2DemoInstance::PictureReady(PP_Resource decoder, |
- const PP_Picture_Dev& picture) { |
- assert(decoder == video_decoder_->pp_resource()); |
- PaintStart(picture); |
+void GLES2DemoInstance::DecoderClient::DismissPictureBuffer( |
+ int32_t picture_buffer_id) { |
+ gles2_->DeleteTexture(GetPictureBufferById(picture_buffer_id).texture_id); |
+ picture_buffers_by_id_.erase(picture_buffer_id); |
} |
-void GLES2DemoInstance::PaintStart(const PP_Picture_Dev& picture) { |
+void GLES2DemoInstance::PictureReady(PP_Resource decoder, |
+ const PP_Picture_Dev& picture) { |
if (first_frame_delivered_ticks_ == -1) |
assert((first_frame_delivered_ticks_ = core_if_->GetTimeTicks()) != -1); |
if (is_painting_) { |
- pictures_pending_paint_.push_back(picture); |
+ pictures_pending_paint_.push_back(std::make_pair(decoder, picture)); |
return; |
} |
- PictureBufferMap::iterator it = |
- buffers_by_id_.find(picture.picture_buffer_id); |
- assert(it != buffers_by_id_.end()); |
- const PP_PictureBuffer_Dev& buffer = it->second; |
+ DecoderClient* client = video_decoders_[decoder]; |
+ assert(client); |
+ const PP_PictureBuffer_Dev& buffer = |
+ client->GetPictureBufferById(picture.picture_buffer_id); |
assert(!is_painting_); |
is_painting_ = true; |
+ int x = 0; |
+ int y = 0; |
+ if (client != video_decoders_.begin()->second) { |
+ x = plugin_size_.width() / kNumDecoders; |
+ y = plugin_size_.height() / kNumDecoders; |
+ } |
+ 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_->BindTexture( |
context_->pp_resource(), GL_TEXTURE_2D, buffer.texture_id); |
gles2_if_->DrawArrays(context_->pp_resource(), GL_TRIANGLE_STRIP, 0, 4); |
pp::CompletionCallback cb = |
callback_factory_.NewCallback( |
- &GLES2DemoInstance::PaintFinished, buffer.id); |
+ &GLES2DemoInstance::PaintFinished, decoder, buffer.id); |
last_swap_request_ticks_ = core_if_->GetTimeTicks(); |
assert(surface_->SwapBuffers(cb) == PP_OK_COMPLETIONPENDING); |
} |
void GLES2DemoInstance::EndOfStream(PP_Resource decoder) { |
- assert(decoder == video_decoder_->pp_resource()); |
} |
void GLES2DemoInstance::NotifyError(PP_Resource decoder, |
PP_VideoDecodeError_Dev error) { |
- assert(decoder == video_decoder_->pp_resource()); |
LogError(this).s() << "Received error: " << error; |
assert(!"Unexpected error; see stderr for details"); |
} |
@@ -375,7 +424,7 @@ class GLES2DemoModule : public pp::Module { |
}; |
void GLES2DemoInstance::InitGL() { |
- assert(position_size_.width() && position_size_.height()); |
+ assert(plugin_size_.width() && plugin_size_.height()); |
is_painting_ = false; |
assert(!context_ && !surface_); |
@@ -383,8 +432,8 @@ void GLES2DemoInstance::InitGL() { |
assert(!context_->is_null()); |
int32_t surface_attributes[] = { |
- PP_GRAPHICS3DATTRIB_WIDTH, position_size_.width(), |
- PP_GRAPHICS3DATTRIB_HEIGHT, position_size_.height(), |
+ PP_GRAPHICS3DATTRIB_WIDTH, plugin_size_.width(), |
+ PP_GRAPHICS3DATTRIB_HEIGHT, plugin_size_.height(), |
PP_GRAPHICS3DATTRIB_NONE |
}; |
surface_ = new pp::Surface3D_Dev(*this, 0, surface_attributes); |
@@ -392,10 +441,8 @@ void GLES2DemoInstance::InitGL() { |
assert(!context_->BindSurfaces(*surface_, *surface_)); |
- // Set viewport window size and clear color bit. |
+ // Clear color bit. |
gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT); |
- gles2_if_->Viewport(context_->pp_resource(), 0, 0, |
- position_size_.width(), position_size_.height()); |
assert(BindGraphics(*surface_)); |
assertNoGLError(); |
@@ -403,7 +450,8 @@ void GLES2DemoInstance::InitGL() { |
CreateGLObjects(); |
} |
-void GLES2DemoInstance::PaintFinished(int32_t result, int picture_buffer_id) { |
+void GLES2DemoInstance::PaintFinished(int32_t result, PP_Resource decoder, |
+ int picture_buffer_id) { |
swap_ticks_ += core_if_->GetTimeTicks() - last_swap_request_ticks_; |
is_painting_ = false; |
++num_frames_rendered_; |
@@ -415,12 +463,14 @@ void GLES2DemoInstance::PaintFinished(int32_t result, int picture_buffer_id) { |
<< ", fps: " << fps << ", with average ms/swap of: " |
<< ms_per_swap; |
} |
- if (video_decoder_) |
- video_decoder_->ReusePictureBuffer(picture_buffer_id); |
+ DecoderClient* client = video_decoders_[decoder]; |
+ if (client && client->decoder()) |
+ client->decoder()->ReusePictureBuffer(picture_buffer_id); |
if (!pictures_pending_paint_.empty()) { |
- PP_Picture_Dev picture = pictures_pending_paint_.front(); |
+ std::pair<PP_Resource, PP_Picture_Dev> decoder_picture = |
+ pictures_pending_paint_.front(); |
pictures_pending_paint_.pop_front(); |
- PaintStart(picture); |
+ PictureReady(decoder_picture.first, decoder_picture.second); |
} |
} |