OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "media/cast/sender/performance_metrics_overlay.h" | 5 #include "media/cast/sender/performance_metrics_overlay.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 | 9 |
10 #include <algorithm> | 10 #include <algorithm> |
11 #include <string> | 11 #include <string> |
12 | 12 |
| 13 #include "base/bind.h" |
13 #include "base/logging.h" | 14 #include "base/logging.h" |
14 #include "base/numerics/safe_conversions.h" | 15 #include "base/numerics/safe_conversions.h" |
15 #include "base/strings/stringprintf.h" | 16 #include "base/strings/stringprintf.h" |
16 #include "media/base/video_frame.h" | 17 #include "media/base/video_frame.h" |
17 | 18 |
18 namespace media { | 19 namespace media { |
19 namespace cast { | 20 namespace cast { |
20 | 21 |
21 namespace { | 22 namespace { |
22 | 23 |
23 const int kScale = 4; // Physical pixels per one logical pixel. | 24 constexpr int kScale = 4; // Physical pixels per one logical pixel. |
24 const int kCharacterWidth = 3; // Logical pixel width of one character. | 25 constexpr int kCharacterWidth = 3; // Logical pixel width of one character. |
25 const int kCharacterHeight = 5; // Logical pixel height of one character. | 26 constexpr int kCharacterHeight = 5; // Logical pixel height of one character. |
26 const int kCharacterSpacing = 1; // Logical pixels between each character. | 27 constexpr int kCharacterSpacing = 1; // Logical pixels between each character. |
27 const int kLineSpacing = 2; // Logical pixels between each line of characters. | 28 constexpr int kLineSpacing = 2; // Logical pixels between each line of chars. |
28 const int kPlane = 0; // Y-plane in YUV formats. | 29 constexpr int kPlane = 0; // Y-plane in YUV formats. |
29 | 30 |
30 // For each pixel in the |rect| (logical coordinates), either decrease the | 31 // For each pixel in the |rect| (logical coordinates), either decrease the |
31 // intensity or increase it so that the resulting pixel has a perceivably | 32 // intensity or increase it so that the resulting pixel has a perceivably |
32 // different value than it did before. |p_ul| is a pointer to the pixel at | 33 // different value than it did before. |p_ul| is a pointer to the pixel at |
33 // coordinate (0,0) in a single-channel 8bpp bitmap. |stride| is the number of | 34 // coordinate (0,0) in a single-channel 8bpp bitmap. |stride| is the number of |
34 // bytes per row in the output bitmap. | 35 // bytes per row in the output bitmap. |
35 void DivergePixels(const gfx::Rect& rect, uint8_t* p_ul, int stride) { | 36 void DivergePixels(const gfx::Rect& rect, uint8_t* p_ul, int stride) { |
36 DCHECK(p_ul); | 37 DCHECK(p_ul); |
37 DCHECK_GT(stride, 0); | 38 DCHECK_GT(stride, 0); |
38 | 39 |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
207 break; | 208 break; |
208 case ' ': | 209 case ' ': |
209 default: | 210 default: |
210 break; | 211 break; |
211 } | 212 } |
212 } | 213 } |
213 } | 214 } |
214 | 215 |
215 } // namespace | 216 } // namespace |
216 | 217 |
217 void MaybeRenderPerformanceMetricsOverlay(base::TimeDelta target_playout_delay, | 218 scoped_refptr<VideoFrame> MaybeRenderPerformanceMetricsOverlay( |
218 bool in_low_latency_mode, | 219 base::TimeDelta target_playout_delay, |
219 int target_bitrate, | 220 bool in_low_latency_mode, |
220 int frames_ago, | 221 int target_bitrate, |
221 double encoder_utilization, | 222 int frames_ago, |
222 double lossy_utilization, | 223 double encoder_utilization, |
223 VideoFrame* frame) { | 224 double lossy_utilization, |
224 if (VideoFrame::PlaneHorizontalBitsPerPixel(frame->format(), kPlane) != 8) { | 225 scoped_refptr<VideoFrame> source) { |
| 226 if (!VLOG_IS_ON(1)) |
| 227 return source; |
| 228 |
| 229 if (VideoFrame::PlaneHorizontalBitsPerPixel(source->format(), kPlane) != 8) { |
225 DLOG(WARNING) << "Cannot render overlay: Plane " << kPlane << " not 8bpp."; | 230 DLOG(WARNING) << "Cannot render overlay: Plane " << kPlane << " not 8bpp."; |
226 return; | 231 return source; |
227 } | 232 } |
228 | 233 |
229 // Can't render to unmappable memory (DmaBuf, CVPixelBuffer). | 234 // Can't read from unmappable memory (DmaBuf, CVPixelBuffer). |
230 if (!frame->IsMappable()) { | 235 if (!source->IsMappable()) { |
231 DVLOG(2) << "Cannot render overlay: frame uses unmappable memory."; | 236 DVLOG(2) << "Cannot render overlay: frame uses unmappable memory."; |
232 return; | 237 return source; |
233 } | 238 } |
234 | 239 |
235 // Compute the physical pixel top row for the bottom-most line of text. | 240 // Compute the physical pixel top row for the bottom-most line of text. |
236 const int line_height = (kCharacterHeight + kLineSpacing) * kScale; | 241 const int line_height = (kCharacterHeight + kLineSpacing) * kScale; |
237 int top = frame->visible_rect().height() - line_height; | 242 int top = source->visible_rect().height() - line_height; |
238 if (top < 0 || !VLOG_IS_ON(1)) | 243 if (top < 0) |
239 return; | 244 return source; // No pixels would change: Return source frame. |
| 245 |
| 246 // Allocate a new frame, identical in configuration to |source| and copy over |
| 247 // all data and metadata. |
| 248 const scoped_refptr<VideoFrame> frame = VideoFrame::CreateFrame( |
| 249 source->format(), source->coded_size(), source->visible_rect(), |
| 250 source->natural_size(), source->timestamp()); |
| 251 if (!frame) |
| 252 return source; // Allocation failure: Return source frame. |
| 253 for (size_t plane = 0, num_planes = VideoFrame::NumPlanes(source->format()); |
| 254 plane < num_planes; ++plane) { |
| 255 memcpy(frame->data(plane), source->data(plane), |
| 256 source->stride(plane) * source->rows(plane)); |
| 257 } |
| 258 frame->metadata()->MergeMetadataFrom(source->metadata()); |
| 259 // Important: After all consumers are done with the frame, copy-back the |
| 260 // changed/new metadata to the source frame, as it contains feedback signals |
| 261 // that need to propagate back up the video stack. The destruction callback |
| 262 // for the |frame| holds a ref-counted reference to the source frame to ensure |
| 263 // the source frame has the right metadata before its destruction observers |
| 264 // are invoked. |
| 265 frame->AddDestructionObserver(base::Bind( |
| 266 [](const VideoFrameMetadata* sent_frame_metadata, |
| 267 const scoped_refptr<VideoFrame>& source_frame) { |
| 268 source_frame->metadata()->Clear(); |
| 269 source_frame->metadata()->MergeMetadataFrom(sent_frame_metadata); |
| 270 }, |
| 271 frame->metadata(), std::move(source))); |
240 | 272 |
241 // Line 3: Frame duration, resolution, and timestamp. | 273 // Line 3: Frame duration, resolution, and timestamp. |
242 int frame_duration_ms = 0; | 274 int frame_duration_ms = 0; |
243 int frame_duration_ms_frac = 0; | 275 int frame_duration_ms_frac = 0; |
244 base::TimeDelta frame_duration; | 276 base::TimeDelta frame_duration; |
245 if (frame->metadata()->GetTimeDelta(VideoFrameMetadata::FRAME_DURATION, | 277 if (frame->metadata()->GetTimeDelta(VideoFrameMetadata::FRAME_DURATION, |
246 &frame_duration)) { | 278 &frame_duration)) { |
247 const int decimilliseconds = base::saturated_cast<int>( | 279 const int decimilliseconds = base::saturated_cast<int>( |
248 frame_duration.InMicroseconds() / 100.0 + 0.5); | 280 frame_duration.InMicroseconds() / 100.0 + 0.5); |
249 frame_duration_ms = decimilliseconds / 10; | 281 frame_duration_ms = decimilliseconds / 10; |
250 frame_duration_ms_frac = decimilliseconds % 10; | 282 frame_duration_ms_frac = decimilliseconds % 10; |
251 } | 283 } |
252 base::TimeDelta rem = frame->timestamp(); | 284 base::TimeDelta rem = frame->timestamp(); |
253 const int minutes = rem.InMinutes(); | 285 const int minutes = rem.InMinutes(); |
254 rem -= base::TimeDelta::FromMinutes(minutes); | 286 rem -= base::TimeDelta::FromMinutes(minutes); |
255 const int seconds = static_cast<int>(rem.InSeconds()); | 287 const int seconds = static_cast<int>(rem.InSeconds()); |
256 rem -= base::TimeDelta::FromSeconds(seconds); | 288 rem -= base::TimeDelta::FromSeconds(seconds); |
257 const int hundredth_seconds = static_cast<int>(rem.InMilliseconds() / 10); | 289 const int hundredth_seconds = static_cast<int>(rem.InMilliseconds() / 10); |
258 RenderLineOfText(base::StringPrintf("%d.%01d %dx%d %d:%02d.%02d", | 290 RenderLineOfText( |
259 frame_duration_ms, | 291 base::StringPrintf("%d.%01d %dx%d %d:%02d.%02d", frame_duration_ms, |
260 frame_duration_ms_frac, | 292 frame_duration_ms_frac, frame->visible_rect().width(), |
261 frame->visible_rect().width(), | 293 frame->visible_rect().height(), minutes, seconds, |
262 frame->visible_rect().height(), | 294 hundredth_seconds), |
263 minutes, | 295 top, frame.get()); |
264 seconds, | |
265 hundredth_seconds), | |
266 top, | |
267 frame); | |
268 | 296 |
269 // Move up one line's worth of pixels. | 297 // Move up one line's worth of pixels. |
270 top -= line_height; | 298 top -= line_height; |
271 if (top < 0 || !VLOG_IS_ON(2)) | 299 if (top < 0 || !VLOG_IS_ON(2)) |
272 return; | 300 return frame; |
273 | 301 |
274 // Line 2: Capture duration, target playout delay, low-latency mode, and | 302 // Line 2: Capture duration, target playout delay, low-latency mode, and |
275 // target bitrate. | 303 // target bitrate. |
276 int capture_duration_ms = 0; | 304 int capture_duration_ms = 0; |
277 base::TimeTicks capture_begin_time, capture_end_time; | 305 base::TimeTicks capture_begin_time, capture_end_time; |
278 if (frame->metadata()->GetTimeTicks(VideoFrameMetadata::CAPTURE_BEGIN_TIME, | 306 if (frame->metadata()->GetTimeTicks(VideoFrameMetadata::CAPTURE_BEGIN_TIME, |
279 &capture_begin_time) && | 307 &capture_begin_time) && |
280 frame->metadata()->GetTimeTicks(VideoFrameMetadata::CAPTURE_END_TIME, | 308 frame->metadata()->GetTimeTicks(VideoFrameMetadata::CAPTURE_END_TIME, |
281 &capture_end_time)) { | 309 &capture_end_time)) { |
282 capture_duration_ms = base::saturated_cast<int>( | 310 capture_duration_ms = base::saturated_cast<int>( |
283 (capture_end_time - capture_begin_time).InMillisecondsF() + 0.5); | 311 (capture_end_time - capture_begin_time).InMillisecondsF() + 0.5); |
284 } | 312 } |
285 const int target_playout_delay_ms = | 313 const int target_playout_delay_ms = |
286 static_cast<int>(target_playout_delay.InMillisecondsF() + 0.5); | 314 static_cast<int>(target_playout_delay.InMillisecondsF() + 0.5); |
287 const int target_kbits = target_bitrate / 1000; | 315 const int target_kbits = target_bitrate / 1000; |
288 RenderLineOfText(base::StringPrintf("%d %4.1d%c %4.1d", | 316 RenderLineOfText( |
289 capture_duration_ms, | 317 base::StringPrintf("%d %4.1d%c %4.1d", capture_duration_ms, |
290 target_playout_delay_ms, | 318 target_playout_delay_ms, |
291 in_low_latency_mode ? '!' : '.', | 319 in_low_latency_mode ? '!' : '.', target_kbits), |
292 target_kbits), | 320 top, frame.get()); |
293 top, | |
294 frame); | |
295 | 321 |
296 // Move up one line's worth of pixels. | 322 // Move up one line's worth of pixels. |
297 top -= line_height; | 323 top -= line_height; |
298 if (top < 0 || !VLOG_IS_ON(3)) | 324 if (top < 0 || !VLOG_IS_ON(3)) |
299 return; | 325 return frame; |
300 | 326 |
301 // Line 1: Recent utilization metrics. | 327 // Line 1: Recent utilization metrics. |
302 const int encoder_pct = | 328 const int encoder_pct = |
303 base::saturated_cast<int>(encoder_utilization * 100.0 + 0.5); | 329 base::saturated_cast<int>(encoder_utilization * 100.0 + 0.5); |
304 const int lossy_pct = | 330 const int lossy_pct = |
305 base::saturated_cast<int>(lossy_utilization * 100.0 + 0.5); | 331 base::saturated_cast<int>(lossy_utilization * 100.0 + 0.5); |
306 RenderLineOfText(base::StringPrintf("%d %3.1d%% %3.1d%%", frames_ago, | 332 RenderLineOfText(base::StringPrintf("%d %3.1d%% %3.1d%%", frames_ago, |
307 encoder_pct, lossy_pct), | 333 encoder_pct, lossy_pct), |
308 top, frame); | 334 top, frame.get()); |
| 335 |
| 336 return frame; |
309 } | 337 } |
310 | 338 |
311 } // namespace cast | 339 } // namespace cast |
312 } // namespace media | 340 } // namespace media |
OLD | NEW |