Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate MOT and send to DAB provider (SMC) #155

Open
hairmare opened this issue Jan 16, 2022 · 1 comment
Open

Generate MOT and send to DAB provider (SMC) #155

hairmare opened this issue Jan 16, 2022 · 1 comment
Labels
enhancement New feature or request output-dab

Comments

@hairmare
Copy link
Member

hairmare commented Jan 16, 2022

This story is about implementing our internal wiki examples irl. The MOT we generate can be used for other vectors (ie. radioplayer) as well. The heavy lifting for this feature is probably something to add in nowplaypadgen.

Things EBU sez:

It is strongly recommended that images intended for SlideShow applications accompanying audio services are authored
at a resolution 320 × 240 pixels in landscape format, to prevent rescaling distortion in receivers. Deviation from these
dimensions may create significantly sub-optimal display.

For the simple profile:

Receivers shall be able to display an image at a resolution of 320 × 240 pixels at a colour/grey scale depth of 8 bits per
pixel (¼-VGA). If a receiver cannot display an image natively at this resolution, it is permitted to rescale it provided the
aspect ratio is maintained and the image is fully visible.
If a slide is broadcast which is smaller than 320 × 240 pixels, then it shall be displayed in the centre of the screen
surrounded by a black background if needed.
Content providers need to be aware that if they broadcast an image larger than 320 × 240 pixels, it may be cropped by
the receiver or not displayed at all. Receivers are only permitted to crop at the right hand side and at the bottom of the
image.

All receivers shall be able to decode images up to a file size (JPEG or PNG) of 50 kbytes (51 200 bytes). The Holding
Buffer shall be large enough for one image.

and for the enhanced profile:

Receivers are strongly recommended to implement a display equal to or larger than 320 × 240 pixels, at a colour depth
of at least 15 bits per pixel. Receivers shall not implement SlideShow on displays smaller than 160 × 120 pixels.
The SlideShow application display may be rotated to best fit the physical display aspect ratio (portrait or landscape),
assuming that the majority of content will be formatted to fit a landscape display. However the orientation of the
SlideShow application display shall be consistent across all services, and individual images received by the application
shall not be rotated on a case-by-case basis.
The original aspect ratio of the image shall always be preserved.
Images may be scaled at factors of 150 % or greater in order to maximize the available physical display space.
It is mandatory to implement a scale factor of 50 %, and this is the only downscaling factor permitted.
The use of anti-aliasing and similar techniques is strongly recommended to optimize the quality of the scaled images.

All receivers shall be able to decode images up to an MOT object size (Body + Header) of 450 kbytes (460 800 bytes).
The Holding Buffer shall be at least 450 kbytes (460 800 bytes) and be able to store between 1 and 64 images. When
multiple images are stored, each image may be a different size and/or colour depth.

@hairmare hairmare added output-dab enhancement New feature or request labels Jan 21, 2022
@hairmare
Copy link
Member Author

hairmare commented Dec 30, 2022

here is some code that generates MOT images as svg and png:

from textwrap import dedent
from base64 import b64encode
from io import StringIO
from argparse import Namespace, BooleanOptionalAction
import logging
import subprocess
from pathlib import PosixPath

import cairosvg
import svg
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
from font_roboto import RobotoLight
import font_fjallaone as FjallaOne
from svglib.fonts import FontMap
from wand.image import Image

Options = Namespace


out = "out.png"

light = "#00e1d4"
dark = "#0d4b56"
white = "#ffffff"

width = 310
height = 240


def data_from_file(path: str) -> str:
    return b64encode(open(path, mode="rb").read()).decode("ascii")


def main(options: Options):
    format = options.format
    outfile = f"{options.name}.{format}"
    png_renderer = options.png_renderer

    style = None
    if format != "png":
        fjallaone_data = data_from_file(FjallaOne.font)
        roboto_data = data_from_file(RobotoLight)

        style = (
            svg.Style(
                type="text/css",
                text=dedent(
                    f"""
                    @font-face {{
                        font-family: "Fjalla-One";
                        src: url(data:font/ttf;charset=utf-8;base64,{fjallaone_data});
                    }}
                    @font-face {{
                        font-family: "Roboto-Light";
                        src: url(data:font/ttf;charset=utf-8;base64,{roboto_data});
                    }}
                    """
                ),
            ),
        )

    img_data = data_from_file("./monsters.jpg")

    # https://github.com/orsinium-labs/svg.py
    canvas = svg.SVG(
        width=width,
        height=height,
        viewBox=svg.ViewBoxSpec(0, 0, width, height),
        elements=[
            # add our fonts
            style,
            # background for top "title" part of image
            svg.Rect(width="100%", height="25%", fill=light),
            # background for remainder of image
            svg.Rect(x="0", y="25%", width="100%", height="77%", fill=dark),
            # station ident at top of image
            svg.Text(
                x="5",
                y=height * 0.25 - 5,
                text="RaBe",
                font_family="Fjalla-One",
                font_weight="bold",
                font_size=height * 0.25 - 10,
                fill=dark,
                text_rendering="optimizeLegibility",
            ),
            # "title" for things like show name
            svg.Text(
                x="5",
                y=height * 0.4 + 5,
                text="Klangbecken",
                font_family="Fjalla-One",
                font_weight="bold",
                font_size=height * 0.2 - 10,
                fill=light,
            ),
            # content part
            svg.Text(
                x="7",
                y=height * 0.5 + 10,
                text="Artist - Title",
                font_family="Roboto-Light",
                # font_style="Light",
                font_size=height * 0.125 - 10,
                fill=white,
            ),
            # smol text at bottom of image
            svg.Text(
                x="5",
                y=height - 5,
                text="Radio Bern RaBe | rabe.ch",
                font_family="Roboto-Light",
                font_size=height * 0.125 - 10,
                fill=light,
            ),
            svg.Image(
                x="70%",
                y="50%",
                width=width * 0.25,
                height=width * 0.25,
                preserveAspectRatio=True,
                #href=PosixPath("/home/hairmare/Documents/git.repos/rabe/nowplaying/monsters.jpg"),
                href=f"data:image/jpeg;base64,{img_data}",
            )
        ],
    )

    if format == "png":
        if png_renderer == "svglib":
            font_map = FontMap()
            font_map.register_font(
                "Fjalla One", font_path=FjallaOne.font, rlgFontName="Fjalla-One"
            )
            font_map.register_font(
                "Roboto Light", RobotoLight, style="light", rlgFontName="Roboto-Light"
            )

            f = StringIO(str(canvas))
            # https://clay-atlas.com/us/blog/2021/03/08/python-en-svglib-convert-svg-png/
            drawing = svg2rlg(f, font_map=font_map)
            configPIL = {}
            renderPM.drawToFile(drawing, outfile, fmt="PNG", configPIL=configPIL)
        elif png_renderer == "cairosvg":
            cairosvg.svg2png(
                bytestring=str(canvas).encode(),
                write_to=outfile,
                output_width=width,
                output_height=height,
            )
        elif png_renderer == "wand":
            with Image(
                blob=str(canvas).encode(), format="svg", width=width, height=height
            ) as img:
                with img.convert("png") as output_img:
                    output_img.save(filename=outfile)
        elif png_renderer == "inkscape":
            # svg string -> write png file
            inkscape = "/usr/bin/inkscape"
            subprocess.run(
                [
                    inkscape,
                    "--export-type=png",
                    f"--export-filename={outfile}",
                    f"--export-width={width}",
                    f"--export-height={height}",
                    "--export-png-use-dithering=false",
                    "--export-dpi=72",
                    "--pipe",
                ],
                input=str(canvas).encode(),
            )

    elif format == "svg":
        outfp = open(outfile, "w")
        outfp.write(str(canvas))


if __name__ == "__main__":
    from configargparse import ArgParser

    args = ArgParser()
    args.add_argument(
        "--format",
        default="png",
        required=False,
        choices=["png", "svg"],
        help="Format to output",
    )
    args.add_argument(
        "--name",
        default="out",
        required=False,
        help="basename of file to output, the default of 'out' creates a file called out.<format>",
    )
    args.add_argument(
        "--png-renderer",
        default="svglib",
        choices=["svglib", "cairosvg", "wand", "inkscape"],
    )
    args.add_argument("--debug", action=BooleanOptionalAction)

    options: Options = args.parse_args()

    if options.debug:
        logging.basicConfig(level=logging.DEBUG)

    main(options)

It looks like this as SVG (for some reason all my browsers are rendering the font wrong, see png for how it should look):

out

And sized for DAB it looks like this as PNG (rendered with svglib/reportlab, others options didn't improve the result):

out

The final PNG is 28K in size which in our limits. Most of the size is from embedding album art from a JPEG.

@hairmare hairmare changed the title Generate MOT and send to DAB Companion Generate MOT and send to DAB provider (SMC) Nov 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request output-dab
Projects
Status: Backlog
Development

No branches or pull requests

1 participant