Skip to content

A python library for handling grain-based media

License

Apache-2.0, Unknown licenses found

Licenses found

Apache-2.0
LICENSE.md
Unknown
COPYING
Notifications You must be signed in to change notification settings

bbc/rd-apmm-python-lib-mediagrains

Repository files navigation

rd-apmm-python-lib-mediagrains

A python library for handling grain-based media in a python-native style. Please read the pydoc documentation for more details.

Provides constructor functions for various types of grains and classes that nicely wrap those grains, as well as a full serialisation and deserialisation library for GSF format.

Installing with Python and make

To run the installer run

make install

to create a redistributable source tarball call

make source

Running tests

To run the tests for Python 2 and 3 (with tox)

make test

Prerequisites

The nmoscommon library used to provide the Timestamp class is needed during testing. This library is available here:

< git+https://github.com/jamesba/nmos-common/@python3 >

to install this package that one must be installed, to run the tests it must be available in a repository that pip will look at during package instalation (if you don't have such a repo you can set one up quickly using devpi).

Examples

As an example of using this in your own code the following is a simple program to load the contents of a GSF file and print the timestamp of each grain.

>>> from mediagrains.gsf import load
>>> f = open('examples/video.gsf', "rb")
>>> (head, segments) = load(f)
>>> print('\n'.join(str(grain.origin_timestamp) for grain in segments[1]))
1420102800:0
1420102800:20000000
1420102800:40000000
1420102800:60000000
1420102800:80000000
1420102800:100000000
1420102800:120000000
1420102800:140000000
1420102800:160000000
1420102800:180000000

Alternatively to create a new video grain in 10-bit planar YUV 4:2:0 and fill it with colour-bars:

>>> from mediagrains import VideoGrain
>>> from uuid import uuid1
>>> from mediagrains.cogenum import CogFrameFormat, CogFrameLayout
>>> src_id = uuid1()
>>> flow_id = uuid1()
>>> grain = VideoGrain(src_id, flow_id, cog_frame_format=CogFrameFormat.S16_422_10BIT, width=1920, height=1080)
>>> colours = [
...      (0x3FF, 0x000, 0x3FF),
...      (0x3FF, 0x3FF, 0x000),
...      (0x3FF, 0x000, 0x000),
...      (0x3FF, 0x3FF, 0x3FF),
...      (0x3FF, 0x200, 0x3FF),
...      (0x3FF, 0x3FF, 0x200) ]
>>> x_offset = [0, 0, 0]
>>> for colour in colours:
...     i = 0
...     for c in grain.components:
...             for x in range(0,c.width//len(colours)):
...                     for y in range(0,c.height):
...                             grain.data[c.offset + y*c.stride + (x_offset[i] + x)*2 + 0] = colour[i] & 0xFF
...                             grain.data[c.offset + y*c.stride + (x_offset[i] + x)*2 + 1] = colour[i] >> 8
...             x_offset[i] += c.width//len(colours)
...             i += 1

The object grain can then be freely used for whatever video processing is desired, or it can be serialised into a GSF file as follows:

>>> from mediagrains.gsf import dump
>>> f = open('dummyfile.gsf', 'wb')
>>> dump(f, [grain])
>>> f.close()

The encoding module also supports a "progressive" mode where an encoder object is created and a dump started, then grains can be added and will be written to the output file as they are added.

>>> from uuid import uuid1
>>> from mediagrains import Grain
>>> from mediagrains.gsf import GSFEncoder
>>> src_id = uuid1()
>>> flow_id = uuid1()
>>> f = open('dummyfile.gsf', 'wb')
>>> enc = GSFEncoder(f)
>>> seg = enc.add_segment()  # This must be done before the call to start_dump
>>> enc.start_dump()  # This writes the file header and starts the export
>>> seg.add_grain(Grain(src_id, flow_id))  # Adds a grain and writes it to the file
>>> seg.add_grain(Grain(src_id, flow_id))  # Adds a grain and writes it to the file
>>> seg.add_grain(Grain(src_id, flow_id))  # Adds a grain and writes it to the file
>>> enc.end_dump()  # This ends the export and finishes off the file
>>> f.close()

If the underlying file is seekable then the end_dump call will upade all segment metadata to list the correct grain count, otherwise the counts will be left at -1.