Skip to content
xfangfang edited this page Sep 4, 2021 · 13 revisions

With just a few lines of code, you can publish your device as DLNA renderer.

Tutorials

First, You need install macast via pip. see: https://github.com/xfangfang/Macast/wiki/Installation#pip

Then refer to the following example or MPVRenderer (located at macast_renderer/mpv.py) create your CustomRenderer class which inherits the Renderer class in macast/renderer.py

You need to implement part of the following callbacks to get data from DLNA clients.(At least set_media_url needs to be implemented to obtain the media url from DLNA clients.)

    def set_media_stop(self):
        pass

    def set_media_pause(self):
        pass

    def set_media_resume(self):
        pass

    def set_media_volume(self, data):
        """ data : int, range from 0 to 100
        """
        pass

    def set_media_mute(self, data):
        """ data : bool
        """
        pass

    def set_media_url(self, data):
        """ data : string
        """
        pass

    def set_media_title(self, data):
        """ data : string
        """
        pass

    def set_media_position(self, data):
        """ data : string position, 00:00:00
        """
        pass

When your CustomRenderer status is updated, you need to call some of the following functions to notify the DLNA clients of the new changes.

    # data : string position, 00:00:00
    self.set_state_position(data)

    # data : string position, 00:00:00
    self.set_state_duration(data):

    # data : string in [PLAYING, PAUSED_PLAYBACK, STOPPED, NO_MEDIA_PRESENT, TRANSITIONING]
    self.set_state_transport(data):

    self.set_state_transport_error():

    # data : bool
    self.set_state_mute(data):

    # data : int, range from 0 to 100
    self.set_state_volume(data)

Example

This example runs on raspberry pi,it receives MP3 audio from DLNA client, and sends FM broadcast through PiFmRds

Warning and Disclaimer
PiFmRds is an experimental program, designed only for experimentation. It is in no way intended to become a personal media center or a tool to operate a radio station, or even broadcast sound to one's own stereo system.

In most countries, transmitting radio waves without a state-issued licence specific to the transmission modalities (frequency, power, bandwidth, etc.) is illegal.

Therefore, always connect a shielded transmission line from the RaspberryPi directly to a radio receiver, so as not to emit radio waves. Never use an antenna.

Even if you are a licensed amateur radio operator, using PiFmRds to transmit radio waves on ham frequencies without any filtering between the RaspberryPi and an antenna is most probably illegal because the square-wave carrier is very rich in harmonics, so the bandwidth requirements are likely not met.

I could not be held liable for any misuse of your own Raspberry Pi. Any experiment is made under your own responsibility.

# Copyright (c) 2021 by xfangfang. All Rights Reserved.
#
# Using pi_fm_rds as DLNA media renderer
# https://github.com/ChristopheJacquet/PiFmRds
#

import os
import time
import threading
import subprocess
from macast import cli, gui
from macast.renderer import Renderer


class PIFMRenderer(Renderer):
    def __init__(self):
        super(PIFMRenderer, self).__init__()
        self.start_position = 0
        self.position_thread = None
        self.position_thread_running = False
        self.sox = None
        self.fm = subprocess.Popen(['sudo', 'pi_fm_rds', '-freq', '108', '-audio', '-'],
                                   stdin=subprocess.PIPE,
                                   bufsize=1024)

    def position_tick(self):
        while self.position_thread_running:
            time.sleep(1)
            self.start_position += 1
            sec = self.start_position
            position = '%d:%02d:%02d' % (sec // 3600, (sec % 3600) // 60, sec % 60)
            self.set_state_position(position)

    def set_media_stop(self):
        if self.sox is not None:
            self.sox.terminate()
        os.waitpid(-1, 1)
        self.set_state_transport('STOPPED')

    def set_media_url(self, data):
        self.position_thread_running = False
        if self.position_thread is not None:
            self.position_thread.join()

        self.start_position = 0
        self.position_thread_running = True
        self.position_thread = threading.Thread(target=self.position_tick, args=())
        self.position_thread.start()
        # Because the playback progress cannot be easily obtained from **sox**,
        # a thread is started here to increase the playback position once per second
        # to simulate that the audio is playing.

        self.sox = subprocess.Popen(['sox', '-t', 'mp3', data, '-t', 'wav', '-'],
                                    stdout=self.fm.stdin)
        self.set_state_transport("PLAYING")

    def stop(self):
        super(PIFMRenderer, self).stop()
        os._exit()


if __name__ == '__main__':
    cli(PIFMRenderer())
    # or using gui
    # gui(PIFMRenderer())
Clone this wiki locally