Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(175)

Unified Diff: tools/telemetry/telemetry/core/bitmapiter.py

Issue 141053006: [telemetry] Process streams of bitmaps via BitmapIter Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/telemetry/telemetry/core/bitmapiter.cc ('k') | tools/telemetry/telemetry/core/platform/__init__.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/telemetry/telemetry/core/bitmapiter.py
diff --git a/tools/telemetry/telemetry/core/bitmapiter.py b/tools/telemetry/telemetry/core/bitmapiter.py
new file mode 100644
index 0000000000000000000000000000000000000000..22444b7d7ff424dbb2e6c72e8c9fb0b6a0a028ef
--- /dev/null
+++ b/tools/telemetry/telemetry/core/bitmapiter.py
@@ -0,0 +1,175 @@
+# Copyright 2014 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.
+
+import array
+import os
+import struct
+import subprocess
+
+from telemetry.core import bitmap
+from telemetry.core import util
+
+
+def _GetDimensions(video):
+ proc = subprocess.Popen(['avconv', '-i', video], stderr=subprocess.PIPE)
+ dimensions = None
+ output = ''
+ for line in proc.stderr.readlines():
+ output += line
+ if 'Video:' in line:
+ dimensions = line.split(',')[2]
+ dimensions = map(int, dimensions.split()[0].split('x'))
+ break
+ proc.wait()
+ assert dimensions, ('Failed to determine video dimensions. output=%s' %
+ output)
+ return dimensions
+
+
+def _GetFrameTimestampMs(stderr):
+ """Returns the frame timestamp in integer milliseconds from the avconv log.
+
+ The expected line format is:
+ ' dts=1.715 pts=1.715\n'
+
+ We have to be careful to only read a single timestamp per call to avoid
+ deadlock because avconv interleaves its writes to stdout and stderr.
+ """
+ while True:
+ line = ''
+ next_char = ''
+ while next_char != '\n':
+ next_char = stderr.read(1)
+ line += next_char
+ if 'pts=' in line:
+ return int(1000 * float(line.split('=')[-1]))
+
+
+class _Commands(object):
+ """Command types for the external bitmapiter tool."""
+ NEXT = 1
+ PIXELS = 2
+ BOUNDING_BOX = 3
+ HISTOGRAM = 4
+ CROP = 5
+ MAGIC = 0x2751912F
+
+
+class BitmapIter(object):
+ """Extracts Bitmap from video files and supports fast per-pixel operations.
+
+ It communicates with the external bitmapiter tool to process a stream of
+ uncompressed pixels and perform expensive operations: GetBoundingBox and
+ ColorHistogram.
+ """
+
+ def __init__(self, mp4_file):
+ bitmapiter_binary = util.FindSupportBinary('bitmapiter')
+ assert bitmapiter_binary, 'You must build bitmapiter first!'
+
+ self._bpp = 3
+ self._width, self._height = _GetDimensions(mp4_file)
+
+ # Current bitmap, if available.
+ self._bitmap = None
+ # Last timestamp.
+ self._timestamp = None
+
+ # Use rawvideo so that we don't need any external library to parse pixels.
+ command = ['avconv', '-i', mp4_file, '-vcodec', 'rawvideo',
+ '-pix_fmt', 'rgb24', '-dump', '-loglevel', 'debug',
+ '-f', 'rawvideo', '-']
+
+ avconv = subprocess.Popen(command, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ self._dumpfile = avconv.stderr
+ # Command pipe to communicate to bitmapiter.
+ pipe = os.pipe()
+ bmpit = subprocess.Popen([bitmapiter_binary, str(pipe[0])],
+ stdin=avconv.stdout,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ self._cmdpipe = pipe[1]
+ self._outpipe = bmpit.stdout.fileno()
+ self._errfile = bmpit.stderr
+ self._RunCommand(_Commands.MAGIC, self._bpp, self._width, self._height)
+
+ def _RunCommand(self, *command):
+ # Command is a packed bunch of ints.
+ packed_command = struct.pack('i' * len(command), *command)
+ os.write(self._cmdpipe, packed_command)
+ # Response is length + bytes.
+ length_response = os.read(self._outpipe, struct.calcsize('i'))
+ if not length_response:
+ msg = self._errfile.read()
+ if msg == b'STOP\n':
+ raise StopIteration
+ raise Exception(msg)
+
+ length = struct.unpack('i', length_response)[0]
+ if length:
+ return os.read(self._outpipe, length)
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ self._RunCommand(_Commands.NEXT)
+ self._timestamp = _GetFrameTimestampMs(self._dumpfile)
+ return self
+
+ @property
+ def bpp(self):
+ return self._bpp
+
+ @property
+ def width(self):
+ return self._width
+
+ @property
+ def height(self):
+ return self._height
+
+ @property
+ def timestamp(self):
+ return self._timestamp
+
+ @property
+ def bitmap(self):
+ if not self._bitmap:
+ pixels = self._RunCommand(_Commands.PIXELS)
+ self._bitmap = bitmap.Bitmap(self._bpp, self._width, self._height, pixels)
+ return self._bitmap
+
+ def GetBoundingBox(self, color, tolerance=0):
+ """Finds the minimum box surrounding all occurences of |color|.
+ Returns: (top, left, width, height), match_count
+ Ignores the alpha channel."""
+ response = self._RunCommand(_Commands.BOUNDING_BOX, color, tolerance)
+ top, left, width, height, match_count = struct.unpack('iiiii', response)
+ return (top, left, width, height), match_count
+
+ def Crop(self, left, top, width, height):
+ """Crops the current bitmap down to the specified box."""
+ if (left < 0 or top < 0 or
+ (left + width) > self.width or
+ (top + height) > self.height):
+ raise ValueError('Invalid dimensions')
+ self._RunCommand(_Commands.CROP, left, top, width, height)
+ return self
+
+ def ColorHistogram(self, ignore_color=-1, tolerance=0):
+ """Computes a histogram of the pixel colors in this Bitmap.
+ Args:
+ ignore_color: An RgbaColor to exclude from the bucket counts.
+ tolerance: A tolerance for the ignore_color.
+
+ Returns:
+ A list of 3x256 integers formatted as
+ [r0, r1, ..., g0, g1, ..., b0, b1, ...].
+ """
+ response = self._RunCommand(_Commands.HISTOGRAM, ignore_color, tolerance)
+ out = array.array('i')
+ out.fromstring(response)
+ return out
« no previous file with comments | « tools/telemetry/telemetry/core/bitmapiter.cc ('k') | tools/telemetry/telemetry/core/platform/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698