A way to extract the playback time from mpg123

In the last period I was working on a project concerning UPnP. In particular I was developing a “smart speaker”, a wireless speaker which plays mp3 files by streaming them from a NAS (UPnP media server). The code is written in Java and leans to mpg123, a real time CLI MPEG audio player/decoder. The hardware platform is composed by a Raspberry Pi (model B), on which I installed Raspbian, and a pair of speakers connected to the Raspberry Pi through the 3.5 mm jack.

In order to provide a synchronization between my UPnP media renderer and the cursor of the playback time displayed on a Web interface, I needed to store continuously the playback time on a text file. After several trials, I solved this issue by applying a simple patch to the source code of mpg123.

The full procedure to modify and rebuild a Debian package is well explained here.  In any case I report here below all the steps I followed on Raspbian (2014-09-09-wheezy-raspbian.img).

First of all you have to install these (required) packages: build-essential, fakeroot, devscripts, equivs and lintian:

sudo apt-get install build-essential fakeroot devscripts equivs lintian

Open (with your preferred editor) the file /etc/apt/sources.list and below each line that starts with deb, copy the same line but replacing deb with deb-src and save your changes. Once this is done, update the repositories with:

sudo apt-get update

Create a working directory and enter it:

mkdir -p ~/src/debian/; cd ~/src/debian

Install mpg123 (since there’s the keyword sudo at the beginning of the command, then you must insert your user’s password):

sudo apt-get install mpg123

Get the sources of mpg123:

apt-get source mpg123

Enter the mpg123 directory:

cd mpg123-1.14.4

Get the build dependencies:

sudo mk-build-deps -i -r mpg123

Install quilt (this package is needed in order to apply a patch to mpg123):

sudo apt-get install quilt

Ensure that all the previous patches have been applied:

quilt push -a

Create a new patch:

quilt new 0001_pbtime_add

Edit the file common.c, located into the subdirectory src, by adding the highlighted lines (see the code fragment listed below), then save and exit (if your editor is nano, type “Ctrl+O” and “Enter” to save changes and then “Ctrl+X” to exit):

quilt edit src/common.c
void print_stat(mpg123_handle *fr, long offset, long buffsize)
{
        FILE *fpout;
        double tim1,tim2;
        off_t rno, no;
        double basevol, realvol;
        char *icy;
#ifndef WIN32
#ifndef GENERIC
/* Only generate new stat line when stderr is ready... don't overfill... */
        {
                struct timeval t;
                fd_set serr;
                int n,errfd = fileno(stderr);

                t.tv_sec=t.tv_usec=0;

                FD_ZERO(&serr);
                FD_SET(errfd,&serr);
                n = select(errfd+1,NULL,&serr,NULL,&t);
                if(n <= 0) return;
        }
#endif
#endif
        if(    MPG123_OK == mpg123_position(fr, offset, buffsize, &no, &rno, &tim1, &tim2)
            && MPG123_OK == mpg123_getvolume(fr, &basevol, &realvol, NULL) )
        {
                if ((fpout = fopen ("time.txt", "w")) == NULL) {
                    printf("\nError: cannot open output file!\n");
                    exit(EXIT_FAILURE);
                }

                fprintf(fpout, "%02lu:%02u", (unsigned long) tim1/60, (unsigned int)tim1%60);
                fclose(fpout);

                fprintf(stderr, "\rFrame# %5"OFF_P" [%5"OFF_P"], Time: %02lu:%02u.%02u [%02u:%02u.%02u], RVA:%6s, Vol: %3u(%3u)",
                        (off_p)no, (off_p)rno,
                        (unsigned long) tim1/60, (unsigned int)tim1%60, (unsigned int)(tim1*100)%100,
                        (unsigned int)tim2/60, (unsigned int)tim2%60, (unsigned int)(tim2*100)%100,
                        rva_name[param.rva], roundui(basevol*100), roundui(realvol*100) );
                if(param.usebuffer) fprintf(stderr,", [%8ld] ",(long)buffsize);
        }
        /* Check for changed tags here too? */
        if( mpg123_meta_check(fr) & MPG123_NEW_ICY && MPG123_OK == mpg123_icy(fr, &icy) )
        fprintf(stderr, "\nICY-META: %s\n", icy);
}

Update the changelog and create a .diff file:

dch -n

This command will open a text file. In the line with the empty *, add a comment (i.e. Added a patch to extract the playback time and put it in a file called time.txt). Save and exit and then clean the building directory:

fakeroot debian/rules clean

Now you have to rebuild the mpg123 package with the changes you’ve just applied:

fakeroot debian/rules binary

Finally, if the building procedure has success, you can reinstall the package:

sudo dpkg -i ../mpg123_1.14.4-1.1_armhf.deb

Ok! That’s all! Now, when you’ll launch mpg123 with the -v option, you’ll find in your current directory a file called time.txt containing the current value of the playback time in the format mm:ss.

__________________

Instead, if you want to rebuild the package on Slackware, you can just use a SlackBuild script, since the mpg123 package is provided by the distribution.

The procedure is simple. First of all you have to download mpg123-1.15.4.tar.xz (the sources), mpg123.SlackBuild and slack-desc files from a Slackware mirror (I suggest you to choose an ftp mirror):

# 32 bit version
wget -r -nd ftp://ftp.swin.edu.au/slackware/slackware-14.1/source/ap/mpg123/
# 64 bit version
wget -r -nd ftp://ftp.swin.edu.au/slackware/slackware64-14.1/source/ap/mpg123/

The -r option turns on the recursive retrieving, while the -nd option tells wget to not create a hierarchy of directories when retrieving recursively. Unpack the sources and enter the right directory:

tar -xf mpg123-1.15.4.tar.xz && cd mpg123-1.15.4

Now, as previously, you have to modify the file common.c (you can do it with your preferred editor). In this case, the changes to apply are shown here below:

void print_stat(mpg123_handle *fr, long offset, long buffsize)
{
    FILE *fpout;
    double tim[2];
    off_t rno, no;
    double basevol, realvol;
    char *icy;
#ifndef WIN32
#ifndef GENERIC
/* Only generate new stat line when stderr is ready... don't overfill... */
    {
        struct timeval t;
        fd_set serr;
        int n,errfd = fileno(stderr);

        t.tv_sec=t.tv_usec=0;

        FD_ZERO(&serr);
        FD_SET(errfd,&serr);
        n = select(errfd+1,NULL,&serr,NULL,&t);
        if(n <= 0) return;
    }
#endif
#endif
    if(    MPG123_OK == mpg123_position(fr, offset, buffsize, &no, &rno, tim, tim+1)
        && MPG123_OK == mpg123_getvolume(fr, &basevol, &realvol, NULL) )
    {
        int ti;
        /* Deal with overly long times. */
        unsigned long times[2][3];
        char timesep[2];
        char sign[2] = {' ', ' '};

        if ((fpout = fopen ("time.txt", "w")) == NULL) {
            printf("\nError: cannot open output file!\n");
            exit(EXIT_FAILURE);
        }

        for(ti=0; ti<2; ++ti)
        {
            if(tim[ti] < 0.){ sign[ti] = '-'; tim[ti] = -tim[ti]; }
            settle_time(tim[ti], times[ti], &timesep[ti]);
        }

        fprintf(fpout, "%02lu:%02lu", times[0][0], times[0][1]);
        fclose(fpout);

        fprintf(stderr, "\rFrame# %5"OFF_P" [%5"OFF_P"], Time:%c%02lu:%02lu%c%02lu%c[%02lu:%02lu%c%02lu], RVA:%6s, Vol: %3u(%3u)",
                (off_p)no, (off_p)rno,
                sign[0],
                times[0][0], times[0][1], timesep[0], times[0][2],
                sign[1],
                times[1][0], times[1][1], timesep[1], times[1][2],
                rva_name[param.rva], roundui(basevol*100), roundui(realvol*100) );
        if(param.usebuffer) fprintf(stderr,", [%8ld] ",(long)buffsize);
    }
    /* Check for changed tags here too? */
    if( mpg123_meta_check(fr) & MPG123_NEW_ICY && MPG123_OK == mpg123_icy(fr, &icy) )
    fprintf(stderr, "\nICY-META: %s\n", icy);
}

Now go out from the mpg123 folder and rebuild the archive:

cd ..
tar cfJ mpg123-1.15.4.tar.xz mpg123-1.15.4

Become root and give execution permissions to the mpg123 SlackBuild script:

su
chmod +x mpg123.SlackBuild

Create the package:

./mpg123.SlackBuild

At the end you’ll find the mpg123 package in your /tmp directory. Now you only have to reinstall your package:

# 32 bit version
removepkg mpg123-1.15.4-i486-1.txz
installpkg /tmp/mpg123-1.15.4-i486-1.txz
# 64 bit version
removepkg mpg123-1.15.4-x86_64-1.txz
installpkg /tmp/mpg123-1.15.4-x86_64-1.txz

And this is all for now! ;-)

Did you like this post? Share it! Email this to someone
email
Share on Google+
Google+
Tweet about this on Twitter
Twitter
Share on Facebook
Facebook
Share on LinkedIn
Linkedin

Leave a Reply