Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / renderer / pepper / pepper_media_stream_video_track_host.cc
index 618d044..6e6a938 100644 (file)
@@ -4,14 +4,25 @@
 
 #include "content/renderer/pepper/pepper_media_stream_video_track_host.h"
 
+#include "base/base64.h"
 #include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/renderer/media/media_stream_video_track.h"
+#include "media/base/bind_to_current_loop.h"
 #include "media/base/yuv_convert.h"
 #include "ppapi/c/pp_errors.h"
+#include "ppapi/c/ppb_media_stream_video_track.h"
 #include "ppapi/c/ppb_video_frame.h"
 #include "ppapi/host/dispatch_host_message.h"
 #include "ppapi/host/host_message_context.h"
 #include "ppapi/proxy/ppapi_messages.h"
 #include "ppapi/shared_impl/media_stream_buffer.h"
+
+// IS_ALIGNED is also defined in
+// third_party/libjingle/overrides/talk/base/basictypes.h
+// TODO(ronghuawu): Avoid undef.
+#undef IS_ALIGNED
 #include "third_party/libyuv/include/libyuv.h"
 
 using media::VideoFrame;
@@ -25,6 +36,23 @@ const int32_t kMaxNumberOfBuffers = 8;
 // Filter mode for scaling frames.
 const libyuv::FilterMode kFilterMode = libyuv::kFilterBox;
 
+const char kPepperVideoSourceName[] = "PepperVideoSourceName";
+
+// Default config for output mode.
+const int kDefaultOutputFrameRate = 30;
+
+media::VideoPixelFormat ToPixelFormat(PP_VideoFrame_Format format) {
+  switch (format) {
+    case PP_VIDEOFRAME_FORMAT_YV12:
+      return media::PIXEL_FORMAT_YV12;
+    case PP_VIDEOFRAME_FORMAT_I420:
+      return media::PIXEL_FORMAT_I420;
+    default:
+      DVLOG(1) << "Unsupported pixel format " << format;
+      return media::PIXEL_FORMAT_UNKNOWN;
+  }
+}
+
 PP_VideoFrame_Format ToPpapiFormat(VideoFrame::Format format) {
   switch (format) {
     case VideoFrame::YV12:
@@ -51,8 +79,7 @@ VideoFrame::Format FromPpapiFormat(PP_VideoFrame_Format format) {
 
 // Compute size base on the size of frame received from MediaStreamVideoSink
 // and size specified by plugin.
-gfx::Size GetTargetSize(const gfx::Size& source,
-                        const gfx::Size& plugin) {
+gfx::Size GetTargetSize(const gfx::Size& source, const gfx::Size& plugin) {
   return gfx::Size(plugin.width() ? plugin.width() : source.width(),
                    plugin.height() ? plugin.height() : source.height());
 }
@@ -68,8 +95,7 @@ void ConvertFromMediaVideoFrame(const scoped_refptr<media::VideoFrame>& src,
                                 PP_VideoFrame_Format dst_format,
                                 const gfx::Size& dst_size,
                                 uint8_t* dst) {
-  CHECK(src->format() == VideoFrame::YV12 ||
-        src->format() == VideoFrame::I420);
+  CHECK(src->format() == VideoFrame::YV12 || src->format() == VideoFrame::I420);
   if (dst_format == PP_VIDEOFRAME_FORMAT_BGRA) {
     if (src->coded_size() == dst_size) {
       libyuv::I420ToARGB(src->data(VideoFrame::kYPlane),
@@ -101,8 +127,10 @@ void ConvertFromMediaVideoFrame(const scoped_refptr<media::VideoFrame>& src,
   } else if (dst_format == PP_VIDEOFRAME_FORMAT_YV12 ||
              dst_format == PP_VIDEOFRAME_FORMAT_I420) {
     static const size_t kPlanesOrder[][3] = {
-      { VideoFrame::kYPlane, VideoFrame::kVPlane, VideoFrame::kUPlane }, // YV12
-      { VideoFrame::kYPlane, VideoFrame::kUPlane, VideoFrame::kVPlane }, // I420
+        {VideoFrame::kYPlane, VideoFrame::kVPlane,
+         VideoFrame::kUPlane},  // YV12
+        {VideoFrame::kYPlane, VideoFrame::kUPlane,
+         VideoFrame::kVPlane},  // I420
     };
     const int plane_order = (dst_format == PP_VIDEOFRAME_FORMAT_YV12) ? 0 : 1;
     int dst_width = dst_size.width();
@@ -111,7 +139,10 @@ void ConvertFromMediaVideoFrame(const scoped_refptr<media::VideoFrame>& src,
                        src->stride(kPlanesOrder[plane_order][0]),
                        src->coded_size().width(),
                        src->coded_size().height(),
-                       dst, dst_width, dst_width, dst_height,
+                       dst,
+                       dst_width,
+                       dst_width,
+                       dst_height,
                        kFilterMode);
     dst += dst_width * dst_height;
     const int src_halfwidth = (src->coded_size().width() + 1) >> 1;
@@ -120,14 +151,22 @@ void ConvertFromMediaVideoFrame(const scoped_refptr<media::VideoFrame>& src,
     const int dst_halfheight = (dst_height + 1) >> 1;
     libyuv::ScalePlane(src->data(kPlanesOrder[plane_order][1]),
                        src->stride(kPlanesOrder[plane_order][1]),
-                       src_halfwidth, src_halfheight,
-                       dst, dst_halfwidth, dst_halfwidth, dst_halfheight,
+                       src_halfwidth,
+                       src_halfheight,
+                       dst,
+                       dst_halfwidth,
+                       dst_halfwidth,
+                       dst_halfheight,
                        kFilterMode);
     dst += dst_halfwidth * dst_halfheight;
     libyuv::ScalePlane(src->data(kPlanesOrder[plane_order][2]),
                        src->stride(kPlanesOrder[plane_order][2]),
-                       src_halfwidth, src_halfheight,
-                       dst, dst_halfwidth, dst_halfwidth, dst_halfheight,
+                       src_halfwidth,
+                       src_halfheight,
+                       dst,
+                       dst_halfwidth,
+                       dst_halfwidth,
+                       dst_halfheight,
                        kFilterMode);
   } else {
     NOTREACHED();
@@ -138,6 +177,57 @@ void ConvertFromMediaVideoFrame(const scoped_refptr<media::VideoFrame>& src,
 
 namespace content {
 
+// Internal class used for delivering video frames on the IO-thread to
+// the MediaStreamVideoSource implementation.
+class PepperMediaStreamVideoTrackHost::FrameDeliverer
+    : public base::RefCountedThreadSafe<FrameDeliverer> {
+ public:
+  FrameDeliverer(
+      const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy,
+      const VideoCaptureDeliverFrameCB& new_frame_callback);
+
+  void DeliverVideoFrame(const scoped_refptr<media::VideoFrame>& frame,
+                         const media::VideoCaptureFormat& format);
+
+ private:
+  friend class base::RefCountedThreadSafe<FrameDeliverer>;
+  virtual ~FrameDeliverer();
+
+  void DeliverFrameOnIO(const scoped_refptr<media::VideoFrame>& frame,
+                        const media::VideoCaptureFormat& format);
+
+  scoped_refptr<base::MessageLoopProxy> io_message_loop_;
+  VideoCaptureDeliverFrameCB new_frame_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(FrameDeliverer);
+};
+
+PepperMediaStreamVideoTrackHost::FrameDeliverer::FrameDeliverer(
+    const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy,
+    const VideoCaptureDeliverFrameCB& new_frame_callback)
+    : io_message_loop_(io_message_loop_proxy),
+      new_frame_callback_(new_frame_callback) {
+}
+
+PepperMediaStreamVideoTrackHost::FrameDeliverer::~FrameDeliverer() {
+}
+
+void PepperMediaStreamVideoTrackHost::FrameDeliverer::DeliverVideoFrame(
+    const scoped_refptr<media::VideoFrame>& frame,
+    const media::VideoCaptureFormat& format) {
+  io_message_loop_->PostTask(
+      FROM_HERE,
+      base::Bind(&FrameDeliverer::DeliverFrameOnIO,
+                 this, frame, format));
+}
+
+void PepperMediaStreamVideoTrackHost::FrameDeliverer::DeliverFrameOnIO(
+     const scoped_refptr<media::VideoFrame>& frame,
+     const media::VideoCaptureFormat& format) {
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
+  new_frame_callback_.Run(frame, format);
+}
+
 PepperMediaStreamVideoTrackHost::PepperMediaStreamVideoTrackHost(
     RendererPpapiHost* host,
     PP_Instance instance,
@@ -149,10 +239,34 @@ PepperMediaStreamVideoTrackHost::PepperMediaStreamVideoTrackHost(
       number_of_buffers_(kDefaultNumberOfBuffers),
       source_frame_format_(PP_VIDEOFRAME_FORMAT_UNKNOWN),
       plugin_frame_format_(PP_VIDEOFRAME_FORMAT_UNKNOWN),
-      frame_data_size_(0) {
+      frame_data_size_(0),
+      type_(kRead),
+      output_started_(false),
+      weak_factory_(this) {
   DCHECK(!track_.isNull());
 }
 
+PepperMediaStreamVideoTrackHost::PepperMediaStreamVideoTrackHost(
+    RendererPpapiHost* host,
+    PP_Instance instance,
+    PP_Resource resource)
+    : PepperMediaStreamTrackHostBase(host, instance, resource),
+      connected_(false),
+      number_of_buffers_(kDefaultNumberOfBuffers),
+      source_frame_format_(PP_VIDEOFRAME_FORMAT_UNKNOWN),
+      plugin_frame_format_(PP_VIDEOFRAME_FORMAT_UNKNOWN),
+      frame_data_size_(0),
+      type_(kWrite),
+      output_started_(false),
+      weak_factory_(this) {
+  InitBlinkTrack();
+  DCHECK(!track_.isNull());
+}
+
+bool PepperMediaStreamVideoTrackHost::IsMediaStreamVideoTrackHost() {
+  return true;
+}
+
 PepperMediaStreamVideoTrackHost::~PepperMediaStreamVideoTrackHost() {
   OnClose();
 }
@@ -162,33 +276,112 @@ void PepperMediaStreamVideoTrackHost::InitBuffers() {
   DCHECK(!size.IsEmpty());
 
   PP_VideoFrame_Format format =
-    GetTargetFormat(source_frame_format_, plugin_frame_format_);
+      GetTargetFormat(source_frame_format_, plugin_frame_format_);
   DCHECK_NE(format, PP_VIDEOFRAME_FORMAT_UNKNOWN);
 
   if (format == PP_VIDEOFRAME_FORMAT_BGRA) {
     frame_data_size_ = size.width() * size.height() * 4;
   } else {
-    frame_data_size_ = VideoFrame::AllocationSize(FromPpapiFormat(format),
-                                                  size);
+    frame_data_size_ =
+        VideoFrame::AllocationSize(FromPpapiFormat(format), size);
   }
 
   DCHECK_GT(frame_data_size_, 0U);
   int32_t buffer_size =
       sizeof(ppapi::MediaStreamBuffer::Video) + frame_data_size_;
   bool result = PepperMediaStreamTrackHostBase::InitBuffers(number_of_buffers_,
-                                                            buffer_size);
+                                                            buffer_size,
+                                                            type_);
   CHECK(result);
+
+  if (type_ == kWrite) {
+    for (int32_t i = 0; i < buffer_manager()->number_of_buffers(); ++i) {
+      ppapi::MediaStreamBuffer::Video* buffer =
+          &(buffer_manager()->GetBufferPointer(i)->video);
+      buffer->header.size = buffer_manager()->buffer_size();
+      buffer->header.type = ppapi::MediaStreamBuffer::TYPE_VIDEO;
+      buffer->format = format;
+      buffer->size.width = size.width();
+      buffer->size.height = size.height();
+      buffer->data_size = frame_data_size_;
+    }
+
+    // Make all the frames avaiable to the plugin.
+    std::vector<int32_t> indices = buffer_manager()->DequeueBuffers();
+    SendEnqueueBuffersMessageToPlugin(indices);
+  }
 }
 
 void PepperMediaStreamVideoTrackHost::OnClose() {
   if (connected_) {
     MediaStreamVideoSink::RemoveFromVideoTrack(this, track_);
+    weak_factory_.InvalidateWeakPtrs();
     connected_ = false;
   }
 }
 
+int32_t PepperMediaStreamVideoTrackHost::OnHostMsgEnqueueBuffer(
+    ppapi::host::HostMessageContext* context, int32_t index) {
+  if (type_ == kRead) {
+    return PepperMediaStreamTrackHostBase::OnHostMsgEnqueueBuffer(context,
+                                                                  index);
+  } else {
+    return SendFrameToTrack(index);
+  }
+}
+
+int32_t PepperMediaStreamVideoTrackHost::SendFrameToTrack(int32_t index) {
+  DCHECK_EQ(type_, kWrite);
+
+  if (output_started_) {
+    // Sends the frame to blink video track.
+    ppapi::MediaStreamBuffer::Video* pp_frame =
+        &(buffer_manager()->GetBufferPointer(index)->video);
+
+    int32 y_stride = plugin_frame_size_.width();
+    int32 uv_stride = (plugin_frame_size_.width() + 1) / 2;
+    uint8* y_data = static_cast<uint8*>(pp_frame->data);
+    // Default to I420
+    uint8* u_data = y_data + plugin_frame_size_.GetArea();
+    uint8* v_data = y_data + (plugin_frame_size_.GetArea() * 5 / 4);
+    if (plugin_frame_format_ == PP_VIDEOFRAME_FORMAT_YV12) {
+      // Swap u and v for YV12.
+      uint8* tmp = u_data;
+      u_data = v_data;
+      v_data = tmp;
+    }
+
+    int64 ts_ms = static_cast<int64>(pp_frame->timestamp *
+                                     base::Time::kMillisecondsPerSecond);
+    scoped_refptr<VideoFrame> frame = media::VideoFrame::WrapExternalYuvData(
+        FromPpapiFormat(plugin_frame_format_),
+        plugin_frame_size_,
+        gfx::Rect(plugin_frame_size_),
+        plugin_frame_size_,
+        y_stride,
+        uv_stride,
+        uv_stride,
+        y_data,
+        u_data,
+        v_data,
+        base::TimeDelta::FromMilliseconds(ts_ms),
+        base::Closure());
+
+    frame_deliverer_->DeliverVideoFrame(
+        frame,
+        media::VideoCaptureFormat(plugin_frame_size_,
+                                  kDefaultOutputFrameRate,
+                                  ToPixelFormat(plugin_frame_format_)));
+  }
+
+  // Makes the frame available again for plugin.
+  SendEnqueueBufferMessageToPlugin(index);
+  return PP_OK;
+}
+
 void PepperMediaStreamVideoTrackHost::OnVideoFrame(
-    const scoped_refptr<VideoFrame>& frame) {
+    const scoped_refptr<VideoFrame>& frame,
+    const media::VideoCaptureFormat& format) {
   DCHECK(frame);
   // TODO(penghuang): Check |frame->end_of_stream()| and close the track.
   PP_VideoFrame_Format ppformat = ToPpapiFormat(frame->format());
@@ -212,24 +405,60 @@ void PepperMediaStreamVideoTrackHost::OnVideoFrame(
   CHECK_EQ(ppformat, source_frame_format_) << "Frame format is changed.";
 
   gfx::Size size = GetTargetSize(source_frame_size_, plugin_frame_size_);
-  PP_VideoFrame_Format format = GetTargetFormat(source_frame_format_,
-                                                plugin_frame_format_);
+  ppformat =
+      GetTargetFormat(source_frame_format_, plugin_frame_format_);
   ppapi::MediaStreamBuffer::Video* buffer =
       &(buffer_manager()->GetBufferPointer(index)->video);
   buffer->header.size = buffer_manager()->buffer_size();
   buffer->header.type = ppapi::MediaStreamBuffer::TYPE_VIDEO;
-  buffer->timestamp = frame->GetTimestamp().InSecondsF();
-  buffer->format = format;
+  buffer->timestamp = frame->timestamp().InSecondsF();
+  buffer->format = ppformat;
   buffer->size.width = size.width();
   buffer->size.height = size.height();
   buffer->data_size = frame_data_size_;
-  ConvertFromMediaVideoFrame(frame, format, size, buffer->data);
+  ConvertFromMediaVideoFrame(frame, ppformat, size, buffer->data);
+
   SendEnqueueBufferMessageToPlugin(index);
 }
 
+void PepperMediaStreamVideoTrackHost::GetCurrentSupportedFormats(
+    int max_requested_width, int max_requested_height,
+    const VideoCaptureDeviceFormatsCB& callback) {
+  if (type_ != kWrite) {
+    DVLOG(1) << "GetCurrentSupportedFormats is only supported in output mode.";
+    callback.Run(media::VideoCaptureFormats());
+    return;
+  }
+
+  media::VideoCaptureFormats formats;
+  formats.push_back(
+      media::VideoCaptureFormat(plugin_frame_size_,
+                                kDefaultOutputFrameRate,
+                                ToPixelFormat(plugin_frame_format_)));
+  callback.Run(formats);
+}
+
+void PepperMediaStreamVideoTrackHost::StartSourceImpl(
+    const media::VideoCaptureParams& params,
+    const VideoCaptureDeliverFrameCB& frame_callback) {
+  output_started_ = true;
+  frame_deliverer_ = new FrameDeliverer(io_message_loop(), frame_callback);
+}
+
+void PepperMediaStreamVideoTrackHost::StopSourceImpl() {
+  output_started_ = false;
+  frame_deliverer_ = NULL;
+}
+
 void PepperMediaStreamVideoTrackHost::DidConnectPendingHostToResource() {
   if (!connected_) {
-    MediaStreamVideoSink::AddToVideoTrack(this, track_);
+    MediaStreamVideoSink::AddToVideoTrack(
+        this,
+        media::BindToCurrentLoop(
+            base::Bind(
+                &PepperMediaStreamVideoTrackHost::OnVideoFrame,
+                weak_factory_.GetWeakPtr())),
+        track_);
     connected_ = true;
   }
 }
@@ -238,9 +467,8 @@ int32_t PepperMediaStreamVideoTrackHost::OnResourceMessageReceived(
     const IPC::Message& msg,
     HostMessageContext* context) {
   IPC_BEGIN_MESSAGE_MAP(PepperMediaStreamVideoTrackHost, msg)
-    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
-        PpapiHostMsg_MediaStreamVideoTrack_Configure,
-        OnHostMsgConfigure)
+  PPAPI_DISPATCH_HOST_RESOURCE_CALL(
+      PpapiHostMsg_MediaStreamVideoTrack_Configure, OnHostMsgConfigure)
   IPC_END_MESSAGE_MAP()
   return PepperMediaStreamTrackHostBase::OnResourceMessageReceived(msg,
                                                                    context);
@@ -255,13 +483,13 @@ int32_t PepperMediaStreamVideoTrackHost::OnHostMsgConfigure(
   gfx::Size new_size(attributes.width, attributes.height);
   if (GetTargetSize(source_frame_size_, plugin_frame_size_) !=
       GetTargetSize(source_frame_size_, new_size)) {
-      changed = true;
+    changed = true;
   }
   plugin_frame_size_ = new_size;
 
-  int32_t buffers = attributes.buffers ?
-      std::min(kMaxNumberOfBuffers, attributes.buffers) :
-      kDefaultNumberOfBuffers;
+  int32_t buffers = attributes.buffers
+                        ? std::min(kMaxNumberOfBuffers, attributes.buffers)
+                        : kDefaultNumberOfBuffers;
   if (buffers != number_of_buffers_)
     changed = true;
   number_of_buffers_ = buffers;
@@ -276,11 +504,39 @@ int32_t PepperMediaStreamVideoTrackHost::OnHostMsgConfigure(
   // new settings. Otherwise, we will initialize buffer when we receive
   // the first frame, because plugin can only provide part of attributes
   // which are not enough to initialize buffers.
-  if (changed && !source_frame_size_.IsEmpty())
+  if (changed && (type_ == kWrite || !source_frame_size_.IsEmpty()))
     InitBuffers();
 
-  context->reply_msg = PpapiPluginMsg_MediaStreamVideoTrack_ConfigureReply();
+  // TODO(ronghuawu): Ask the owner of DOMMediaStreamTrackToResource why
+  // source id instead of track id is used there.
+  const std::string id = track_.source().id().utf8();
+  context->reply_msg = PpapiPluginMsg_MediaStreamVideoTrack_ConfigureReply(id);
   return PP_OK;
 }
 
+void PepperMediaStreamVideoTrackHost::InitBlinkTrack() {
+  std::string source_id;
+  base::Base64Encode(base::RandBytesAsString(64), &source_id);
+  blink::WebMediaStreamSource webkit_source;
+  webkit_source.initialize(base::UTF8ToUTF16(source_id),
+                           blink::WebMediaStreamSource::TypeVideo,
+                           base::UTF8ToUTF16(kPepperVideoSourceName));
+  webkit_source.setExtraData(this);
+
+  const bool enabled = true;
+  blink::WebMediaConstraints constraints;
+  constraints.initialize();
+  track_ = MediaStreamVideoTrack::CreateVideoTrack(
+       this, constraints,
+       base::Bind(
+           &PepperMediaStreamVideoTrackHost::OnTrackStarted,
+           base::Unretained(this)),
+       enabled);
+}
+
+void PepperMediaStreamVideoTrackHost::OnTrackStarted(
+    MediaStreamSource* source, bool success) {
+  DVLOG(3) << "OnTrackStarted result: " << success;
+}
+
 }  // namespace content