Index: ppapi/examples/video_encode/video_encode.cc |
diff --git a/ppapi/examples/video_encode/video_encode.cc b/ppapi/examples/video_encode/video_encode.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7937397bd5c875c18662739fdf5805043722cc66 |
--- /dev/null |
+++ b/ppapi/examples/video_encode/video_encode.cc |
@@ -0,0 +1,373 @@ |
+// 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 <math.h> |
+#include <stdio.h> |
+#include <string.h> |
+ |
+#include <iostream> |
+#include <sstream> |
+#include <vector> |
+ |
+#include "ppapi/c/pp_errors.h" |
+#include "ppapi/c/ppb_console.h" |
+#include "ppapi/cpp/input_event.h" |
+#include "ppapi/cpp/instance.h" |
+#include "ppapi/cpp/media_stream_video_track.h" |
+#include "ppapi/cpp/module.h" |
+#include "ppapi/cpp/rect.h" |
+#include "ppapi/cpp/var.h" |
+#include "ppapi/cpp/var_array_buffer.h" |
+#include "ppapi/cpp/var_dictionary.h" |
+#include "ppapi/cpp/video_encoder.h" |
+#include "ppapi/cpp/video_frame.h" |
+#include "ppapi/utility/completion_callback_factory.h" |
+ |
+// Use assert as a poor-man's CHECK, even in non-debug mode. |
+// Since <assert.h> redefines assert on every inclusion (it doesn't use |
+// include-guards), make sure this is the last file #include'd in this file. |
+#undef NDEBUG |
+#include <assert.h> |
+ |
+namespace { |
+ |
+// This object is the global object representing this plugin library as long |
+// as it is loaded. |
+class MediaStreamVideoEncoderModule : public pp::Module { |
+ public: |
+ MediaStreamVideoEncoderModule() : pp::Module() {} |
+ virtual ~MediaStreamVideoEncoderModule() {} |
+ |
+ virtual pp::Instance* CreateInstance(PP_Instance instance); |
+}; |
+ |
+class MediaStreamVideoEncoderInstance : public pp::Instance { |
+ public: |
+ MediaStreamVideoEncoderInstance(PP_Instance instance, pp::Module* module); |
+ virtual ~MediaStreamVideoEncoderInstance(); |
+ |
+ // pp::Instance implementation. |
+ virtual void DidChangeView(const pp::Rect& position, |
+ const pp::Rect& clip_ignored); |
+ virtual void HandleMessage(const pp::Var& var_message); |
+ |
+ private: |
+ void ConfigureTrack(); |
+ void OnConfiguredTrack(int32_t result); |
+ void InitializeEncoder(); |
+ void OnInitializedEncoder(int32_t result); |
+ void GetTrackFrame(); |
+ void OnGetTrackFrame(int32_t result, const pp::VideoFrame& frame); |
+ void OnGetEncoderFrame(int32_t result, |
+ pp::VideoFrame encoder_frame, |
+ pp::VideoFrame track_frame); |
+ void OnEncodeDone(int32_t result, pp::VideoFrame encoder_frame); |
+ void OnGetBitstreamBuffer(int32_t result, PP_BitstreamBuffer buffer); |
+ void StopEncode(); |
+ |
+ void LogError(int32_t error, std::string message); |
+ void LogWarning(std::string message); |
+ |
+ void PostDataMessage(const void* buffer, uint32_t size); |
+ void PostSignalMessage(const char* name); |
+ |
+ bool is_encoding_; |
+ |
+ pp::VideoEncoder video_encoder_; |
+ pp::MediaStreamVideoTrack video_track_; |
+ pp::CompletionCallbackFactory<MediaStreamVideoEncoderInstance> |
+ callback_factory_; |
+ std::vector<int32_t> attrib_list_; |
+ |
+ pp::VideoFrame last_track_frame_; |
+ |
+ PP_VideoFrame_Format frame_format_; |
+ |
+ pp::Size frame_size_; |
+ pp::Size plugin_size_; |
+ pp::Size encoder_size_; |
+ int32_t encoder_frames_; |
+}; |
+ |
+MediaStreamVideoEncoderInstance::MediaStreamVideoEncoderInstance( |
+ PP_Instance instance, |
+ pp::Module* module) |
+ : pp::Instance(instance), |
+ is_encoding_(false), |
+ callback_factory_(this), |
+ frame_format_(PP_VIDEOFRAME_FORMAT_I420) { |
+} |
+ |
+MediaStreamVideoEncoderInstance::~MediaStreamVideoEncoderInstance() { |
+} |
+ |
+// |
+// |
+// |
+ |
+void MediaStreamVideoEncoderInstance::DidChangeView( |
+ const pp::Rect& position, |
+ const pp::Rect& clip_ignored) { |
+ plugin_size_ = position.size(); |
+} |
+ |
+void MediaStreamVideoEncoderInstance::ConfigureTrack() { |
+ if (encoder_size_.IsEmpty()) |
+ frame_size_ = plugin_size_; |
+ else |
+ frame_size_ = encoder_size_; |
+ |
+ int32_t attrib_list[] = {PP_MEDIASTREAMVIDEOTRACK_ATTRIB_FORMAT, |
+ frame_format_, |
+ PP_MEDIASTREAMVIDEOTRACK_ATTRIB_WIDTH, |
+ frame_size_.width(), |
+ PP_MEDIASTREAMVIDEOTRACK_ATTRIB_HEIGHT, |
+ frame_size_.height(), |
+ PP_MEDIASTREAMVIDEOTRACK_ATTRIB_NONE}; |
+ |
+ pp::VarDictionary dict; |
+ dict.Set(pp::Var("status"), pp::Var("configuring video track")); |
+ dict.Set(pp::Var("width"), pp::Var(frame_size_.width())); |
+ dict.Set(pp::Var("height"), pp::Var(frame_size_.height())); |
+ PostMessage(dict); |
+ |
+ video_track_.Configure( |
+ attrib_list, callback_factory_.NewCallback( |
+ &MediaStreamVideoEncoderInstance::OnConfiguredTrack)); |
+} |
+ |
+void MediaStreamVideoEncoderInstance::OnConfiguredTrack(int32_t result) { |
+ if (result != PP_OK) { |
+ LogError(result, "Cannot configure track"); |
+ return; |
+ } |
+ |
+ InitializeEncoder(); |
+ GetTrackFrame(); |
+} |
+ |
+void MediaStreamVideoEncoderInstance::InitializeEncoder() { |
+ video_encoder_ = pp::VideoEncoder(this); |
+ encoder_frames_ = 0; |
+ |
+ pp::VarDictionary dict; |
+ dict.Set(pp::Var("status"), pp::Var("initializing encoder")); |
+ dict.Set(pp::Var("width"), pp::Var(encoder_size_.width())); |
+ dict.Set(pp::Var("height"), pp::Var(encoder_size_.height())); |
+ PostMessage(dict); |
+ |
+ int32_t error = video_encoder_.Initialize( |
+ frame_format_, frame_size_, PP_VIDEOPROFILE_H264MAIN, 2000000, |
+ PP_HARDWAREACCELERATION_ONLY, |
+ callback_factory_.NewCallback( |
+ &MediaStreamVideoEncoderInstance::OnInitializedEncoder)); |
+ if (error != PP_OK_COMPLETIONPENDING) { |
+ LogError(error, "Cannot initialize encoder"); |
+ return; |
+ } |
+ |
+ video_encoder_.GetBitstreamBuffer(callback_factory_.NewCallbackWithOutput( |
+ &MediaStreamVideoEncoderInstance::OnGetBitstreamBuffer)); |
+} |
+ |
+void MediaStreamVideoEncoderInstance::OnInitializedEncoder(int32_t result) { |
+ if (result != PP_OK) { |
+ LogError(result, "Encoder initialization failed"); |
+ return; |
+ } |
+ |
+ if (video_encoder_.GetFrameCodedSize(&encoder_size_) != PP_OK) { |
+ LogError(result, "Cannot get coded size from encoder"); |
+ return; |
+ } |
+ |
+ is_encoding_ = true; |
+ |
+ pp::VarDictionary dict; |
+ dict.Set(pp::Var("status"), pp::Var("encoder initialized")); |
+ dict.Set(pp::Var("width"), pp::Var(encoder_size_.width())); |
+ dict.Set(pp::Var("height"), pp::Var(encoder_size_.height())); |
+ PostMessage(dict); |
+ |
+ if (frame_size_ != encoder_size_) |
+ ConfigureTrack(); |
+ else |
+ GetTrackFrame(); |
+} |
+ |
+void MediaStreamVideoEncoderInstance::GetTrackFrame() { |
+ if (!is_encoding_) |
+ return; |
+ video_track_.GetFrame(callback_factory_.NewCallbackWithOutput( |
+ &MediaStreamVideoEncoderInstance::OnGetTrackFrame)); |
+} |
+ |
+void MediaStreamVideoEncoderInstance::OnGetTrackFrame( |
+ int32_t result, |
+ const pp::VideoFrame& frame) { |
+ if (result == PP_ERROR_ABORTED) |
+ return; |
+ if (result != PP_OK) { |
+ LogError(result, "Cannot get video frame from video track"); |
+ return; |
+ } |
+ |
+ frame.GetSize(&frame_size_); |
+ |
+ if (frame_size_ != encoder_size_) { |
+ LogError(PP_ERROR_FAILED, "MediaStreamVideoTrack frame size incorrect"); |
+ return; |
+ } |
+ |
+ if (encoder_frames_ >= 4) { |
+ LogWarning("Too many frames waiting to be encoded, dropping"); |
+ video_track_.RecycleFrame(frame); |
+ GetTrackFrame(); |
+ } else { |
+ video_encoder_.GetVideoFrame(callback_factory_.NewCallbackWithOutput( |
+ &MediaStreamVideoEncoderInstance::OnGetEncoderFrame, frame)); |
+ } |
+} |
+ |
+void MediaStreamVideoEncoderInstance::OnGetEncoderFrame( |
+ int32_t result, |
+ pp::VideoFrame encoder_frame, |
+ pp::VideoFrame track_frame) { |
+ if (result == PP_ERROR_ABORTED) |
+ return; |
+ if (result != PP_OK) { |
+ LogError(result, "Cannot get video frame from video encoder"); |
+ return; |
+ } |
+ |
+ if (encoder_frame.GetDataBufferSize() < track_frame.GetDataBufferSize()) { |
+ std::ostringstream oss; |
+ oss << "Incorrect encoder video frame buffer size : " |
+ << encoder_frame.GetDataBufferSize() << " < " |
+ << track_frame.GetDataBufferSize(); |
+ LogError(PP_ERROR_FAILED, oss.str()); |
+ return; |
+ } |
+ |
+ memcpy(encoder_frame.GetDataBuffer(), track_frame.GetDataBuffer(), |
+ track_frame.GetDataBufferSize()); |
+ |
+ video_track_.RecycleFrame(track_frame); |
+ |
+ video_encoder_.Encode( |
+ encoder_frame, PP_FALSE, |
+ callback_factory_.NewCallback( |
+ &MediaStreamVideoEncoderInstance::OnEncodeDone, encoder_frame)); |
+ encoder_frames_++; |
+ GetTrackFrame(); |
+} |
+ |
+void MediaStreamVideoEncoderInstance::OnEncodeDone( |
+ int32_t result, |
+ pp::VideoFrame encoder_frame) { |
+ encoder_frames_--; |
+} |
+ |
+void MediaStreamVideoEncoderInstance::OnGetBitstreamBuffer( |
+ int32_t result, |
+ PP_BitstreamBuffer buffer) { |
+ if (result == PP_ERROR_ABORTED) |
+ return; |
+ if (result != PP_OK) { |
+ LogError(result, "Cannot get bitstream buffer"); |
+ return; |
+ } |
+ |
+ PostDataMessage(buffer.buffer, buffer.size); |
+ video_encoder_.RecycleBitstreamBuffer(buffer); |
+ |
+ video_encoder_.GetBitstreamBuffer(callback_factory_.NewCallbackWithOutput( |
+ &MediaStreamVideoEncoderInstance::OnGetBitstreamBuffer)); |
+} |
+ |
+void MediaStreamVideoEncoderInstance::StopEncode() { |
+ video_track_.Close(); |
+ video_track_.detach(); |
+ is_encoding_ = false; |
+} |
+ |
+// |
+ |
+void MediaStreamVideoEncoderInstance::HandleMessage( |
+ const pp::Var& var_message) { |
+ if (!var_message.is_dictionary()) { |
+ LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Invalid message!")); |
+ return; |
+ } |
+ |
+ pp::VarDictionary var_dictionary_message(var_message); |
+ std::string command = var_dictionary_message.Get("command").AsString(); |
+ |
+ if (command == "start") { |
+ pp::Var var_track = var_dictionary_message.Get("track"); |
+ if (!var_track.is_resource()) { |
+ LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Given track is not a resource")); |
+ return; |
+ } |
+ pp::Resource resource_track = var_track.AsResource(); |
+ video_track_ = pp::MediaStreamVideoTrack(resource_track); |
+ ConfigureTrack(); |
+ } else if (command == "stop") { |
+ StopEncode(); |
+ PostSignalMessage("stopped"); |
+ } else { |
+ LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Invalid command!")); |
+ } |
+} |
+ |
+void MediaStreamVideoEncoderInstance::PostDataMessage(const void* buffer, |
+ uint32_t size) { |
+ pp::VarDictionary dictionary; |
+ |
+ dictionary.Set(pp::Var("name"), pp::Var("data")); |
+ |
+ pp::VarArrayBuffer array_buffer(size); |
+ void* data_ptr = array_buffer.Map(); |
+ memcpy(data_ptr, buffer, size); |
+ array_buffer.Unmap(); |
+ dictionary.Set(pp::Var("data"), array_buffer); |
+ |
+ PostMessage(dictionary); |
+} |
+ |
+void MediaStreamVideoEncoderInstance::PostSignalMessage(const char* name) { |
+ pp::VarDictionary dictionary; |
+ dictionary.Set(pp::Var("name"), pp::Var(name)); |
+ |
+ PostMessage(dictionary); |
+} |
+ |
+void MediaStreamVideoEncoderInstance::LogError(int32_t error, |
+ std::string message) { |
+ std::string msg("Error: "); |
+ msg.append(pp::Var(error).DebugString()); |
+ msg.append(" : "); |
+ msg.append(message); |
+ LogToConsole(PP_LOGLEVEL_ERROR, pp::Var(msg)); |
+} |
+ |
+void MediaStreamVideoEncoderInstance::LogWarning(std::string message) { |
+ std::string msg("Warning: "); |
+ msg.append(message); |
+ LogToConsole(PP_LOGLEVEL_WARNING, pp::Var(msg)); |
+} |
+ |
+pp::Instance* MediaStreamVideoEncoderModule::CreateInstance( |
+ PP_Instance instance) { |
+ return new MediaStreamVideoEncoderInstance(instance, this); |
+} |
+ |
+} // anonymous namespace |
+ |
+namespace pp { |
+// Factory function for your specialization of the Module object. |
+Module* CreateModule() { |
+ return new MediaStreamVideoEncoderModule(); |
+} |
+} // namespace pp |