MP3 Transcoder

This module transcodes the users source music into mp3 data that can be streamed via musicdb.lib.stream.icecast.IcecastInterface. Therefore it uses the musicdb.lib.stream.gstreamer.GStreamerInterface class. A GStreamer Pipeline will be created to transcode m4a, mp3 and flac files into a specific mp3 encoding. The output of the GStreamer Pipeline gets written into a UNIX Pipe. The data can then be accessed via GetChunk().

GStreamer Pipeline for Transcoding

As shown in the graph below, GStreamer is used for the transcoding. The files get read by the filesrc element and then be decoded. The raw audio data gets then be encoded using the lamemp3enc element. These mp3 encoded data will then be provided by writing into a UNIX Pipe for further processing.

digraph hierarchy { size="5,8" filesrc [shape=box, label="filesrc"] decodebin [shape=box, label="decodebin"] audioconvert [shape=box, label="audioconvert"] lamemp3enc [shape=box, label="lamemp3enc"] fdsink [shape=box, label="fdsink"] filesrc -> decodebin decodebin -> audioconvert audioconvert -> lamemp3enc lamemp3enc -> fdsink }

The encoding is a MPEG v1 Layer III encoding with 320kb/s and Joint Stereo.

The following example shows the bash representation of the pipeline:

gst-launch-1.0 filesrc location=in.m4a ! decodebin ! audioconvert ! lamemp3enc target=1 bitrate=320 cbr=true ! filesink location=out.mp3

file out.mp3
#> out.mp3: MPEG ADTS, layer III, v1, 320 kbps, 44.1 kHz, JntStereo

ffpribe out.mp3
#> Duration: 00:03:43.16, start: 0.000000, bitrate: 320 kb/s
#> Stream #0:0: Audio: mp3, 44100 Hz, stereo, s16p, 320 kb/s

UNIX Pipe

A UNIX Pipe is connected to the fdsink GStreamer Element. fdsink writes into the pipe. Then MP3Transcoder.GetChunk() reads some chunks from the mp3 data encoded by the lamemp3enc Element.

The pipe is accessed non-blocking.

Transcoding

Transcoding will be done in a separate thread. To safely work with the MP3Transcoder class, you should use the provided context management:

with MP3Transcoder("/tmp/test.flac") as transcoder:
    # …

MP3Transcoder Class

class musicdb.lib.stream.mp3transcoder.MP3Transcoder(path)[source]
Parameters

path (str/Path) – The absolute path of the audio file that shall be transcoded

Example

with MP3Transcoder("/tmp/test.flac") as transcoder:
    while True:
        chunk = transcoder.GetChunk(4096)
        if len(chunk) == 0:
            break
Cancel()[source]

This method cancels a currently running transcoding process. If there is no transcoding going on, nothing happens.

The GStreamer object gets set into CANCEL state. The connection to GStreamer stays established!

GetChunk(size)[source]

This method reads a chunk of data that gets provided by the GStreamer fdsink element from the GStreamer Pipeline. This element writes into a UNIX Pipe. It tries to read size bytes of data.

When reading from the UNIX Pipe fails with an BlockingIOError, than the state of the current transcoding gets checked. If it is RUNNING, than the method tries to read from the pipe again after 100ms. Otherwise it is assumed that the process of transcoding is complete.

When 0 bytes were read and the state of the transcoding process is not RUNNING, than it is also assumed that the process is competed.

In all cases, all collected bytes were returned by this method. It may only be less that size. When there were less than size bytes returned, or even 0, than the process of transcoding can be considered complete.

The following diagram shows how this method gets the data from the GStreamer Pipeline via UNIX Pipes:

digraph hierarchy { size="5,8" start [label="Start"]; read [shape=box, label="Read data from pipe"] isrunningstate [shape=diamond, label="Is GStreamer Pipeline\nprocess still running?"] isempty [shape=diamond, label="No further bytes\nto expect from pipe?"] calcrest [shape=box, label="Calculate remaining bytes"] appendbytes [shape=box, label="Collect already read bytes"] bytesremaining [shape=diamond, label="Remaining\nbytes?"] sleep [shape=box, label="Sleep for 0.1s"] end [label="Return chunk"]; start -> read read -> isrunningstate [style="dashed", label="Blocking IO Exception"] isrunningstate -> sleep [label="yes"] isrunningstate -> end [label="no"] sleep -> read read -> isempty isempty -> end [label="yes"] isempty -> calcrest [label="no"] calcrest -> appendbytes appendbytes -> bytesremaining bytesremaining -> sleep [label="yes"] bytesremaining -> end [label="no"] }

Parameters

size (int) – Number of bytes to read

Returns

A chunk of data as type bytes

Example

print("\033[1;36mTranscoding %s"%(path))
sinkpath = path + ".mp3"
sinkfile = open(sinkpath, "wb")

retval   = transcoder.Transcode(path)
if retval == False:
    print("\033[1;31mStarting Transcoder failed")
    return 1

while True:
    chunk = transcoder.GetChunk(4096)
    if len(chunk) == 0:
        break

    sinkfile.write(chunk)
sinkfile.close()
Transcode()[source]

This method starts the transcoding process of a file as thread. After setting the path as source for the audio data, a thread gets started that runs the GStreamer Pipeline. Before leaving, the method ensures that the GStreamer Pipeline is running. If an error occurs False gets returned, otherwise True.

When calling this method, a previous started transcoding process gets canceled.

Returns

If an error occurs False gets returned, otherwise True.

Example

transcoder = MP3Transcoder("/tmp/test.flac")

retval   = transcoder.Transcode()
if retval == False:
    print("Starting Transcoder failed!")
onDecoderPadAdded(dbin, pad)[source]

Args:

Returns:

Example