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