| // Copyright (c) 2013 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 "content/renderer/media/pepper_to_video_track_adapter.h" |
| |
| #include <string> |
| |
| #include "base/base64.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/rand_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/trace_event/trace_event.h" |
| #include "content/renderer/media/media_stream.h" |
| #include "content/renderer/media/media_stream_registry_interface.h" |
| #include "content/renderer/media/media_stream_video_source.h" |
| #include "content/renderer/media/media_stream_video_track.h" |
| #include "content/renderer/pepper/ppb_image_data_impl.h" |
| #include "content/renderer/render_thread_impl.h" |
| #include "media/base/video_frame_pool.h" |
| #include "media/capture/video_capture_types.h" |
| #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" |
| #include "third_party/WebKit/public/platform/WebURL.h" |
| #include "third_party/WebKit/public/web/WebMediaStreamRegistry.h" |
| #include "third_party/libyuv/include/libyuv/convert.h" |
| #include "url/gurl.h" |
| |
| namespace content { |
| |
| // PpFrameWriter implements MediaStreamVideoSource and can therefore provide |
| // video frames to MediaStreamVideoTracks. It also implements |
| // FrameWriterInterface, which will be used by Pepper plugins (notably the |
| // Effects plugin) to inject the processed frame. |
| class PpFrameWriter : public MediaStreamVideoSource, |
| public FrameWriterInterface, |
| public base::SupportsWeakPtr<PpFrameWriter> { |
| public: |
| PpFrameWriter(); |
| ~PpFrameWriter() override; |
| |
| // FrameWriterInterface implementation. |
| // This method will be called by the Pepper host from render thread. |
| void PutFrame(PPB_ImageData_Impl* image_data, int64_t time_stamp_ns) override; |
| |
| protected: |
| // MediaStreamVideoSource implementation. |
| void GetCurrentSupportedFormats( |
| int max_requested_width, |
| int max_requested_height, |
| double max_requested_frame_rate, |
| const VideoCaptureDeviceFormatsCB& callback) override; |
| void StartSourceImpl( |
| const media::VideoCaptureFormat& format, |
| const blink::WebMediaConstraints& constraints, |
| const VideoCaptureDeliverFrameCB& frame_callback) override; |
| void StopSourceImpl() override; |
| |
| private: |
| media::VideoFramePool frame_pool_; |
| |
| class FrameWriterDelegate; |
| scoped_refptr<FrameWriterDelegate> delegate_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PpFrameWriter); |
| }; |
| |
| class PpFrameWriter::FrameWriterDelegate |
| : public base::RefCountedThreadSafe<FrameWriterDelegate> { |
| public: |
| FrameWriterDelegate( |
| scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, |
| const VideoCaptureDeliverFrameCB& new_frame_callback); |
| |
| void DeliverFrame(const scoped_refptr<media::VideoFrame>& frame); |
| private: |
| friend class base::RefCountedThreadSafe<FrameWriterDelegate>; |
| virtual ~FrameWriterDelegate(); |
| |
| void DeliverFrameOnIO(const scoped_refptr<media::VideoFrame>& frame); |
| |
| scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; |
| VideoCaptureDeliverFrameCB new_frame_callback_; |
| }; |
| |
| PpFrameWriter::FrameWriterDelegate::FrameWriterDelegate( |
| scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, |
| const VideoCaptureDeliverFrameCB& new_frame_callback) |
| : io_task_runner_(io_task_runner), new_frame_callback_(new_frame_callback) { |
| } |
| |
| PpFrameWriter::FrameWriterDelegate::~FrameWriterDelegate() { |
| } |
| |
| void PpFrameWriter::FrameWriterDelegate::DeliverFrame( |
| const scoped_refptr<media::VideoFrame>& frame) { |
| io_task_runner_->PostTask( |
| FROM_HERE, |
| base::Bind(&FrameWriterDelegate::DeliverFrameOnIO, this, frame)); |
| } |
| |
| void PpFrameWriter::FrameWriterDelegate::DeliverFrameOnIO( |
| const scoped_refptr<media::VideoFrame>& frame) { |
| DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| // The local time when this frame is generated is unknown so give a null |
| // value to |estimated_capture_time|. |
| new_frame_callback_.Run(frame, base::TimeTicks()); |
| } |
| |
| PpFrameWriter::PpFrameWriter() { |
| DVLOG(3) << "PpFrameWriter ctor"; |
| } |
| |
| PpFrameWriter::~PpFrameWriter() { |
| DVLOG(3) << "PpFrameWriter dtor"; |
| } |
| |
| void PpFrameWriter::GetCurrentSupportedFormats( |
| int max_requested_width, |
| int max_requested_height, |
| double max_requested_frame_rate, |
| const VideoCaptureDeviceFormatsCB& callback) { |
| DCHECK(CalledOnValidThread()); |
| DVLOG(3) << "PpFrameWriter::GetCurrentSupportedFormats()"; |
| // Since the input is free to change the resolution at any point in time |
| // the supported formats are unknown. |
| media::VideoCaptureFormats formats; |
| callback.Run(formats); |
| } |
| |
| void PpFrameWriter::StartSourceImpl( |
| const media::VideoCaptureFormat& format, |
| const blink::WebMediaConstraints& constraints, |
| const VideoCaptureDeliverFrameCB& frame_callback) { |
| DCHECK(CalledOnValidThread()); |
| DCHECK(!delegate_.get()); |
| DVLOG(3) << "PpFrameWriter::StartSourceImpl()"; |
| delegate_ = new FrameWriterDelegate(io_task_runner(), frame_callback); |
| OnStartDone(MEDIA_DEVICE_OK); |
| } |
| |
| void PpFrameWriter::StopSourceImpl() { |
| DCHECK(CalledOnValidThread()); |
| } |
| |
| // Note: PutFrame must copy or process image_data directly in this function, |
| // because it may be overwritten as soon as we return from this function. |
| void PpFrameWriter::PutFrame(PPB_ImageData_Impl* image_data, |
| int64_t time_stamp_ns) { |
| DCHECK(CalledOnValidThread()); |
| TRACE_EVENT0("video", "PpFrameWriter::PutFrame"); |
| DVLOG(3) << "PpFrameWriter::PutFrame()"; |
| |
| if (!image_data) { |
| LOG(ERROR) << "PpFrameWriter::PutFrame - Called with NULL image_data."; |
| return; |
| } |
| ImageDataAutoMapper mapper(image_data); |
| if (!mapper.is_valid()) { |
| LOG(ERROR) << "PpFrameWriter::PutFrame - " |
| << "The image could not be mapped and is unusable."; |
| return; |
| } |
| SkBitmap bitmap(image_data->GetMappedBitmap()); |
| if (bitmap.empty()) { |
| LOG(ERROR) << "PpFrameWriter::PutFrame - " |
| << "The image_data's mapped bitmap failed."; |
| return; |
| } |
| |
| SkAutoLockPixels src_lock(bitmap); |
| const uint8_t* src_data = static_cast<uint8_t*>(bitmap.getPixels()); |
| const int src_stride = static_cast<int>(bitmap.rowBytes()); |
| const int width = bitmap.width(); |
| const int height = bitmap.height(); |
| |
| // We only support PP_IMAGEDATAFORMAT_BGRA_PREMUL at the moment. |
| DCHECK(image_data->format() == PP_IMAGEDATAFORMAT_BGRA_PREMUL); |
| |
| const gfx::Size frame_size(width, height); |
| |
| if (state() != MediaStreamVideoSource::STARTED) |
| return; |
| |
| const base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds( |
| time_stamp_ns / base::Time::kNanosecondsPerMicrosecond); |
| |
| scoped_refptr<media::VideoFrame> new_frame = |
| frame_pool_.CreateFrame(media::PIXEL_FORMAT_YV12, frame_size, |
| gfx::Rect(frame_size), frame_size, timestamp); |
| |
| libyuv::ARGBToI420(src_data, |
| src_stride, |
| new_frame->data(media::VideoFrame::kYPlane), |
| new_frame->stride(media::VideoFrame::kYPlane), |
| new_frame->data(media::VideoFrame::kUPlane), |
| new_frame->stride(media::VideoFrame::kUPlane), |
| new_frame->data(media::VideoFrame::kVPlane), |
| new_frame->stride(media::VideoFrame::kVPlane), |
| width, |
| height); |
| |
| delegate_->DeliverFrame(new_frame); |
| } |
| |
| // PpFrameWriterProxy is a helper class to make sure the user won't use |
| // PpFrameWriter after it is released (IOW its owner - WebMediaStreamSource - |
| // is released). |
| class PpFrameWriterProxy : public FrameWriterInterface { |
| public: |
| explicit PpFrameWriterProxy(const base::WeakPtr<PpFrameWriter>& writer) |
| : writer_(writer) { |
| DCHECK(writer_); |
| } |
| |
| ~PpFrameWriterProxy() override {} |
| |
| void PutFrame(PPB_ImageData_Impl* image_data, |
| int64_t time_stamp_ns) override { |
| writer_->PutFrame(image_data, time_stamp_ns); |
| } |
| |
| private: |
| base::WeakPtr<PpFrameWriter> writer_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PpFrameWriterProxy); |
| }; |
| |
| bool PepperToVideoTrackAdapter::Open(MediaStreamRegistryInterface* registry, |
| const std::string& url, |
| FrameWriterInterface** frame_writer) { |
| DVLOG(3) << "PepperToVideoTrackAdapter::Open"; |
| blink::WebMediaStream stream; |
| if (registry) { |
| stream = registry->GetMediaStream(url); |
| } else { |
| stream = |
| blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url)); |
| } |
| if (stream.isNull()) { |
| LOG(ERROR) << "PepperToVideoTrackAdapter::Open - invalid url: " << url; |
| return false; |
| } |
| |
| // Create a new native video track and add it to |stream|. |
| std::string track_id; |
| // According to spec, a media stream source's id should be unique per |
| // application. There's no easy way to strictly achieve that. The id |
| // generated with this method should be unique for most of the cases but |
| // theoretically it's possible we can get an id that's duplicated with the |
| // existing sources. |
| base::Base64Encode(base::RandBytesAsString(64), &track_id); |
| |
| PpFrameWriter* writer = new PpFrameWriter(); |
| |
| // Create a new webkit video track. |
| blink::WebMediaStreamSource webkit_source; |
| blink::WebMediaStreamSource::Type type = |
| blink::WebMediaStreamSource::TypeVideo; |
| blink::WebString webkit_track_id = blink::WebString::fromUTF8(track_id); |
| webkit_source.initialize(webkit_track_id, type, webkit_track_id, |
| false /* remote */); |
| webkit_source.setExtraData(writer); |
| |
| blink::WebMediaConstraints constraints; |
| constraints.initialize(); |
| bool track_enabled = true; |
| |
| stream.addTrack(MediaStreamVideoTrack::CreateVideoTrack( |
| writer, constraints, MediaStreamVideoSource::ConstraintsCallback(), |
| track_enabled)); |
| |
| *frame_writer = new PpFrameWriterProxy(writer->AsWeakPtr()); |
| return true; |
| } |
| |
| } // namespace content |