| 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
|
|
|