OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/renderer/pepper/pepper_media_stream_video_track_host.h" | 5 #include "content/renderer/pepper/pepper_media_stream_video_track_host.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "media/base/yuv_convert.h" | |
8 #include "ppapi/c/pp_errors.h" | 9 #include "ppapi/c/pp_errors.h" |
9 #include "ppapi/c/ppb_video_frame.h" | 10 #include "ppapi/c/ppb_video_frame.h" |
11 #include "ppapi/host/dispatch_host_message.h" | |
12 #include "ppapi/host/host_message_context.h" | |
13 #include "ppapi/proxy/ppapi_messages.h" | |
10 #include "ppapi/shared_impl/media_stream_buffer.h" | 14 #include "ppapi/shared_impl/media_stream_buffer.h" |
15 #include "third_party/libyuv/include/libyuv/scale.h" | |
11 | 16 |
12 using media::VideoFrame; | 17 using media::VideoFrame; |
18 using ppapi::host::HostMessageContext; | |
19 using ppapi::MediaStreamVideoTrackShared; | |
13 | 20 |
14 namespace { | 21 namespace { |
15 | 22 |
16 // TODO(penghuang): make it configurable. | 23 const int32_t kDefaultNumberOfBuffers = 4; |
17 const int32_t kNumberOfFrames = 4; | 24 const int32_t kMaxNumberOfBuffers = 8; |
25 // Filer mode for scaling frames. | |
26 const libyuv::FilterMode kFilterMode = libyuv::kFilterBox; | |
18 | 27 |
19 PP_VideoFrame_Format ToPpapiFormat(VideoFrame::Format format) { | 28 PP_VideoFrame_Format ToPpapiFormat(VideoFrame::Format format) { |
20 switch (format) { | 29 switch (format) { |
21 case VideoFrame::YV12: | 30 case VideoFrame::YV12: |
22 return PP_VIDEOFRAME_FORMAT_YV12; | 31 return PP_VIDEOFRAME_FORMAT_YV12; |
23 case VideoFrame::YV16: | |
24 return PP_VIDEOFRAME_FORMAT_YV16; | |
25 case VideoFrame::I420: | 32 case VideoFrame::I420: |
26 return PP_VIDEOFRAME_FORMAT_I420; | 33 return PP_VIDEOFRAME_FORMAT_I420; |
27 case VideoFrame::YV12A: | |
28 return PP_VIDEOFRAME_FORMAT_YV12A; | |
29 case VideoFrame::YV12J: | |
30 return PP_VIDEOFRAME_FORMAT_YV12J; | |
31 default: | 34 default: |
32 DVLOG(1) << "Unsupported pixel format " << format; | 35 DVLOG(1) << "Unsupported pixel format " << format; |
33 return PP_VIDEOFRAME_FORMAT_UNKNOWN; | 36 return PP_VIDEOFRAME_FORMAT_UNKNOWN; |
34 } | 37 } |
35 } | 38 } |
36 | 39 |
40 VideoFrame::Format FromPpapiFormat(PP_VideoFrame_Format format) { | |
41 switch (format) { | |
42 case PP_VIDEOFRAME_FORMAT_YV12: | |
43 return VideoFrame::YV12; | |
44 case PP_VIDEOFRAME_FORMAT_I420: | |
45 return VideoFrame::I420; | |
46 default: | |
47 DVLOG(1) << "Unsupported pixel format " << format; | |
48 return VideoFrame::UNKNOWN; | |
49 } | |
50 } | |
51 | |
52 // Compute size base on the size of frame received from MediaStreamVideoSink | |
53 // and size specified by plugin. | |
54 gfx::Size GetTargetSize(const gfx::Size& source, | |
55 const gfx::Size& plugin) { | |
56 return gfx::Size(plugin.width() ? plugin.width() : source.width(), | |
57 plugin.height() ? plugin.height() : source.height()); | |
58 } | |
59 | |
60 // Compute format base on the format of frame received from MediaStreamVideoSink | |
61 // and format specified by plugin. | |
62 PP_VideoFrame_Format GetTargetFormat(PP_VideoFrame_Format source, | |
63 PP_VideoFrame_Format plugin) { | |
64 return plugin != PP_VIDEOFRAME_FORMAT_UNKNOWN ? plugin : source; | |
65 } | |
66 | |
67 void ConvertFromMediaVideoFrame(const scoped_refptr<media::VideoFrame>& src, | |
68 PP_VideoFrame_Format dst_format, | |
69 const gfx::Size& dst_size, | |
70 uint8_t* dst) { | |
71 CHECK(src->format() == VideoFrame::YV12 || | |
72 src->format() == VideoFrame::I420); | |
73 if (dst_format == PP_VIDEOFRAME_FORMAT_BGRA) { | |
74 if (src->coded_size() == dst_size) { | |
75 media::ConvertYUVToRGB32(src->data(VideoFrame::kYPlane), | |
76 src->data(VideoFrame::kUPlane), | |
77 src->data(VideoFrame::kVPlane), | |
78 dst, | |
79 dst_size.width(), | |
80 dst_size.height(), | |
81 src->stride(VideoFrame::kYPlane), | |
82 src->stride(VideoFrame::kUPlane), | |
83 dst_size.width() * 4, | |
84 media::YV12); | |
85 } else { | |
86 media::ScaleYUVToRGB32(src->data(VideoFrame::kYPlane), | |
87 src->data(VideoFrame::kUPlane), | |
88 src->data(VideoFrame::kVPlane), | |
89 dst, | |
90 src->coded_size().width(), | |
91 src->coded_size().height(), | |
92 dst_size.width(), | |
93 dst_size.height(), | |
94 src->stride(VideoFrame::kYPlane), | |
95 src->stride(VideoFrame::kUPlane), | |
96 dst_size.width() * 4, | |
97 media::YV12, | |
98 media::ROTATE_0, | |
99 media::FILTER_BILINEAR); | |
100 } | |
101 } else if (dst_format == PP_VIDEOFRAME_FORMAT_YV12 || | |
102 dst_format == PP_VIDEOFRAME_FORMAT_I420) { | |
103 static const size_t kPlanesOrder[][3] = { | |
104 { VideoFrame::kYPlane, VideoFrame::kVPlane, VideoFrame::kUPlane }, // YV12 | |
105 { VideoFrame::kYPlane, VideoFrame::kUPlane, VideoFrame::kVPlane }, // I420 | |
106 }; | |
107 const int plane_order = (dst_format == PP_VIDEOFRAME_FORMAT_YV12) ? 0 : 1; | |
108 int dst_width = dst_size.width(); | |
109 int dst_height = dst_size.height(); | |
110 libyuv::ScalePlane(src->data(kPlanesOrder[plane_order][0]), | |
111 src->stride(kPlanesOrder[plane_order][0]), | |
112 src->coded_size().width(), | |
113 src->coded_size().height(), | |
114 dst, dst_width, dst_width, dst_height, | |
115 kFilterMode); | |
116 dst += dst_width * dst_height; | |
117 const int src_halfwidth = (src->coded_size().width() + 1) >> 1; | |
118 const int src_halfheight = (src->coded_size().height() + 1) >> 1; | |
119 const int dst_halfwidth = (dst_width + 1) >> 1; | |
120 const int dst_halfheight = (dst_height + 1) >> 1; | |
121 libyuv::ScalePlane(src->data(kPlanesOrder[plane_order][1]), | |
122 src->stride(kPlanesOrder[plane_order][1]), | |
123 src_halfwidth, src_halfheight, | |
124 dst, dst_halfwidth, dst_halfwidth, dst_halfheight, | |
125 kFilterMode); | |
126 dst += dst_halfwidth * dst_halfheight; | |
127 libyuv::ScalePlane(src->data(kPlanesOrder[plane_order][2]), | |
128 src->stride(kPlanesOrder[plane_order][2]), | |
129 src_halfwidth, src_halfheight, | |
130 dst, dst_halfwidth, dst_halfwidth, dst_halfheight, | |
131 kFilterMode); | |
132 } else { | |
133 NOTREACHED(); | |
134 } | |
135 } | |
136 | |
37 } // namespace | 137 } // namespace |
38 | 138 |
39 namespace content { | 139 namespace content { |
40 | 140 |
41 PepperMediaStreamVideoTrackHost::PepperMediaStreamVideoTrackHost( | 141 PepperMediaStreamVideoTrackHost::PepperMediaStreamVideoTrackHost( |
42 RendererPpapiHost* host, | 142 RendererPpapiHost* host, |
43 PP_Instance instance, | 143 PP_Instance instance, |
44 PP_Resource resource, | 144 PP_Resource resource, |
45 const blink::WebMediaStreamTrack& track) | 145 const blink::WebMediaStreamTrack& track) |
46 : PepperMediaStreamTrackHostBase(host, instance, resource), | 146 : PepperMediaStreamTrackHostBase(host, instance, resource), |
47 track_(track), | 147 track_(track), |
48 connected_(false), | 148 connected_(false), |
49 frame_format_(VideoFrame::UNKNOWN), | 149 number_of_buffers_(kDefaultNumberOfBuffers), |
150 source_frame_format_(PP_VIDEOFRAME_FORMAT_UNKNOWN), | |
151 plugin_frame_format_(PP_VIDEOFRAME_FORMAT_UNKNOWN), | |
50 frame_data_size_(0) { | 152 frame_data_size_(0) { |
51 DCHECK(!track_.isNull()); | 153 DCHECK(!track_.isNull()); |
52 } | 154 } |
53 | 155 |
54 PepperMediaStreamVideoTrackHost::~PepperMediaStreamVideoTrackHost() { | 156 PepperMediaStreamVideoTrackHost::~PepperMediaStreamVideoTrackHost() { |
55 OnClose(); | 157 OnClose(); |
56 } | 158 } |
57 | 159 |
160 void PepperMediaStreamVideoTrackHost::InitBuffers() { | |
161 gfx::Size size = GetTargetSize(source_frame_size_, plugin_frame_size_); | |
162 DCHECK(!size.IsEmpty()); | |
163 | |
164 PP_VideoFrame_Format format = | |
165 GetTargetFormat(source_frame_format_, plugin_frame_format_); | |
166 DCHECK_NE(format, PP_VIDEOFRAME_FORMAT_UNKNOWN); | |
167 | |
168 if (format == PP_VIDEOFRAME_FORMAT_BGRA) { | |
169 frame_data_size_ = size.width() * size.height() * 4; | |
170 } else { | |
171 frame_data_size_ = VideoFrame::AllocationSize(FromPpapiFormat(format), | |
172 size); | |
173 } | |
174 | |
175 DCHECK_GT(frame_data_size_, 0U); | |
176 int32_t buffer_size = | |
177 sizeof(ppapi::MediaStreamBuffer::Video) + frame_data_size_; | |
178 bool result = PepperMediaStreamTrackHostBase::InitBuffers(number_of_buffers_, | |
179 buffer_size); | |
180 CHECK(result); | |
181 } | |
182 | |
58 void PepperMediaStreamVideoTrackHost::OnClose() { | 183 void PepperMediaStreamVideoTrackHost::OnClose() { |
59 if (connected_) { | 184 if (connected_) { |
60 MediaStreamVideoSink::RemoveFromVideoTrack(this, track_); | 185 MediaStreamVideoSink::RemoveFromVideoTrack(this, track_); |
61 connected_ = false; | 186 connected_ = false; |
62 } | 187 } |
63 } | 188 } |
64 | 189 |
65 void PepperMediaStreamVideoTrackHost::OnVideoFrame( | 190 void PepperMediaStreamVideoTrackHost::OnVideoFrame( |
66 const scoped_refptr<VideoFrame>& frame) { | 191 const scoped_refptr<VideoFrame>& frame) { |
67 DCHECK(frame); | 192 DCHECK(frame); |
68 // TODO(penghuang): Check |frame->end_of_stream()| and close the track. | 193 // TODO(penghuang): Check |frame->end_of_stream()| and close the track. |
69 PP_VideoFrame_Format ppformat = ToPpapiFormat(frame->format()); | 194 PP_VideoFrame_Format ppformat = ToPpapiFormat(frame->format()); |
70 if (ppformat == PP_VIDEOFRAME_FORMAT_UNKNOWN) | 195 if (ppformat == PP_VIDEOFRAME_FORMAT_UNKNOWN) |
71 return; | 196 return; |
72 | 197 |
73 if (frame_size_ != frame->coded_size() || frame_format_ != frame->format()) { | 198 if (source_frame_size_.IsEmpty()) { |
74 frame_size_ = frame->coded_size(); | 199 source_frame_size_ = frame->coded_size(); |
75 frame_format_ = frame->format(); | 200 source_frame_format_ = ppformat; |
76 // TODO(penghuang): Support changing |frame_size_| & |frame_format_| more | 201 InitBuffers(); |
77 // than once. | |
78 DCHECK(!frame_data_size_); | |
79 frame_data_size_ = VideoFrame::AllocationSize(frame_format_, frame_size_); | |
80 int32_t size = sizeof(ppapi::MediaStreamBuffer::Video) + frame_data_size_; | |
81 bool result = InitBuffers(kNumberOfFrames, size); | |
82 // TODO(penghuang): Send PP_ERROR_NOMEMORY to plugin. | |
83 CHECK(result); | |
84 } | 202 } |
85 | 203 |
86 int32_t index = buffer_manager()->DequeueBuffer(); | 204 int32_t index = buffer_manager()->DequeueBuffer(); |
87 // Drop frames if the underlying buffer is full. | 205 // Drop frames if the underlying buffer is full. |
88 if (index < 0) | 206 if (index < 0) { |
207 DVLOG(1) << "A frame is dropped."; | |
89 return; | 208 return; |
209 } | |
90 | 210 |
91 // TODO(penghuang): support format conversion and size scaling. | 211 CHECK(frame->coded_size() == source_frame_size_) << "Frame size is changed"; |
212 CHECK_EQ(ppformat, source_frame_format_) << "Frame format is changed."; | |
213 | |
214 gfx::Size size = GetTargetSize(source_frame_size_, plugin_frame_size_); | |
215 PP_VideoFrame_Format format = GetTargetFormat(source_frame_format_, | |
216 plugin_frame_format_); | |
92 ppapi::MediaStreamBuffer::Video* buffer = | 217 ppapi::MediaStreamBuffer::Video* buffer = |
93 &(buffer_manager()->GetBufferPointer(index)->video); | 218 &(buffer_manager()->GetBufferPointer(index)->video); |
94 buffer->header.size = buffer_manager()->buffer_size(); | 219 buffer->header.size = buffer_manager()->buffer_size(); |
95 buffer->header.type = ppapi::MediaStreamBuffer::TYPE_VIDEO; | 220 buffer->header.type = ppapi::MediaStreamBuffer::TYPE_VIDEO; |
96 buffer->timestamp = frame->GetTimestamp().InSecondsF(); | 221 buffer->timestamp = frame->GetTimestamp().InSecondsF(); |
97 buffer->format = ppformat; | 222 buffer->format = format; |
98 buffer->size.width = frame->coded_size().width(); | 223 buffer->size.width = size.width(); |
99 buffer->size.height = frame->coded_size().height(); | 224 buffer->size.height = size.height(); |
100 buffer->data_size = frame_data_size_; | 225 buffer->data_size = frame_data_size_; |
101 | 226 ConvertFromMediaVideoFrame(frame, format, size, buffer->data); |
102 COMPILE_ASSERT(VideoFrame::kYPlane == 0, y_plane_should_be_0); | |
103 COMPILE_ASSERT(VideoFrame::kUPlane == 1, u_plane_should_be_1); | |
104 COMPILE_ASSERT(VideoFrame::kVPlane == 2, v_plane_should_be_2); | |
105 | |
106 uint8_t* dst = buffer->data; | |
107 size_t num_planes = VideoFrame::NumPlanes(frame->format()); | |
108 for (size_t i = 0; i < num_planes; ++i) { | |
109 const uint8_t* src = frame->data(i); | |
110 const size_t row_bytes = frame->row_bytes(i); | |
111 const size_t src_stride = frame->stride(i); | |
112 int rows = frame->rows(i); | |
113 for (int j = 0; j < rows; ++j) { | |
114 memcpy(dst, src, row_bytes); | |
115 dst += row_bytes; | |
116 src += src_stride; | |
117 } | |
118 } | |
119 | |
120 SendEnqueueBufferMessageToPlugin(index); | 227 SendEnqueueBufferMessageToPlugin(index); |
121 } | 228 } |
122 | 229 |
123 void PepperMediaStreamVideoTrackHost::DidConnectPendingHostToResource() { | 230 void PepperMediaStreamVideoTrackHost::DidConnectPendingHostToResource() { |
124 if (!connected_) { | 231 if (!connected_) { |
125 MediaStreamVideoSink::AddToVideoTrack(this, track_); | 232 MediaStreamVideoSink::AddToVideoTrack(this, track_); |
126 connected_ = true; | 233 connected_ = true; |
127 } | 234 } |
128 } | 235 } |
129 | 236 |
237 int32_t PepperMediaStreamVideoTrackHost::OnResourceMessageReceived( | |
238 const IPC::Message& msg, | |
239 HostMessageContext* context) { | |
240 IPC_BEGIN_MESSAGE_MAP(PepperMediaStreamVideoTrackHost, msg) | |
241 PPAPI_DISPATCH_HOST_RESOURCE_CALL( | |
242 PpapiHostMsg_MediaStreamVideoTrack_Configure, | |
243 OnHostMsgConfigure) | |
244 IPC_END_MESSAGE_MAP() | |
245 return PepperMediaStreamTrackHostBase::OnResourceMessageReceived(msg, | |
246 context); | |
247 } | |
248 | |
249 int32_t PepperMediaStreamVideoTrackHost::OnHostMsgConfigure( | |
250 HostMessageContext* context, | |
251 const MediaStreamVideoTrackShared::Attributes& attributes) { | |
252 CHECK(MediaStreamVideoTrackShared::VerifyAttributes(attributes)); | |
253 | |
254 bool changed = false; | |
255 gfx::Size new_size(attributes.width, attributes.height); | |
256 if (GetTargetSize(source_frame_size_, plugin_frame_size_) != | |
257 GetTargetSize(source_frame_size_, new_size)) { | |
258 changed = true; | |
259 } | |
260 plugin_frame_size_ = new_size; | |
261 | |
262 int32_t buffers = attributes.buffers ? | |
263 std::min(kMaxNumberOfBuffers, attributes.buffers) : | |
264 kDefaultNumberOfBuffers; | |
265 if (buffers != number_of_buffers_) | |
266 changed = true; | |
267 number_of_buffers_ = buffers; | |
268 | |
269 if (plugin_frame_format_ != attributes.format) { | |
Ronghua Wu (Left Chromium)
2014/02/19 21:16:24
nit: remove this line or do the same for size
Peng
2014/02/19 22:57:01
Done.
| |
270 if (GetTargetFormat(source_frame_format_, plugin_frame_format_) != | |
271 GetTargetFormat(source_frame_format_, attributes.format)) { | |
272 changed = true; | |
273 } | |
274 plugin_frame_format_ = attributes.format; | |
275 } | |
276 | |
277 // If the first frame has been received, we will re-initialize buffers with | |
278 // new settings. Otherwise, we will initialize buffer when we receive | |
279 // the first frame, because plugin can only provide part of attributes | |
280 // which are not enough to initialize buffers. | |
281 if (changed && !source_frame_size_.IsEmpty()) | |
282 InitBuffers(); | |
283 | |
284 context->reply_msg = PpapiPluginMsg_MediaStreamVideoTrack_ConfigureReply(); | |
285 return PP_OK; | |
286 } | |
287 | |
130 } // namespace content | 288 } // namespace content |
OLD | NEW |