/*
 *  import_mplayer.c
 *
 *  Copyright (C) Thomas Oestreich - June 2001
 *
 *  This file is part of transcode, a video stream  processing tool
 *
 *  transcode is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  transcode is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#define MOD_NAME    "import_mplayer.so"
#define MOD_VERSION "v0.1.2 (2007-11-01)"
#define MOD_CODEC   "(video) rendered by mplayer | (audio) rendered by mplayer"

#include "transcode.h"

static int verbose_flag = TC_QUIET;
static int capability_flag = TC_CAP_YUV|TC_CAP_RGB|TC_CAP_VID|TC_CAP_PCM;

#define MOD_PRE mplayer
#include "import_def.h"

#include <sys/types.h>



#define VIDEOPIPE_TEMPLATE "/tmp/mplayer2transcode-video.XXXXXX"
#define AUDIOPIPE_TEMPLATE "/tmp/mplayer2transcode-audio.XXXXXX"
static char videopipe[40];
static char audiopipe[40];
static FILE *videopipefd = NULL;
static FILE *audiopipefd = NULL;

/* ------------------------------------------------------------
 * private helper macros/functions.
 * ------------------------------------------------------------*/

#define RETURN_IF_BAD_SRET(SRET, FIFO) do { \
    if ((SRET) < 0) { \
        unlink((FIFO)); \
        return TC_IMPORT_ERROR; \
    } \
} while (0)

#define RETURN_IF_OPEN_FAILED(FP, MSG) do { \
    if ((FP) == NULL) { \
        tc_log_perror(MOD_NAME, (MSG)); \
        unlink(videopipe); \
        return TC_IMPORT_ERROR; \
    } \
} while (0)


static int tc_mplayer_open_video(vob_t *vob, transfer_t *param)
{
    char import_cmd_buf[TC_BUF_MAX];
    long sret = 0;

    tc_snprintf(videopipe, sizeof(videopipe), VIDEOPIPE_TEMPLATE);
    if (!mktemp(videopipe)) {
        tc_log_perror(MOD_NAME, "mktemp videopipe failed");
        return(TC_IMPORT_ERROR);
    }
    if (mkfifo(videopipe, 00660) == -1) {
        tc_log_perror(MOD_NAME, "mkfifo video failed");
        return(TC_IMPORT_ERROR);
    }

    sret = tc_snprintf(import_cmd_buf, TC_BUF_MAX,
                       "mplayer -slave -benchmark -noframedrop -nosound"
                       " -vo yuv4mpeg:file=%s %s \"%s\" -osdlevel 0"
                       " > /dev/null 2>&1",
                       videopipe,
                       ((vob->im_v_string) ? vob->im_v_string : ""),
                       vob->video_in_file);
    RETURN_IF_BAD_SRET(sret, videopipe);

    if (verbose_flag)
        tc_log_info(MOD_NAME, "%s", import_cmd_buf);

    videopipefd = popen(import_cmd_buf, "w");
    RETURN_IF_OPEN_FAILED(videopipefd, "popen videopipe failed");

    if (vob->im_v_codec == CODEC_YUV) {
        sret = tc_snprintf(import_cmd_buf, TC_BUF_MAX,
                           "tcextract -i %s -x yuv420p -t yuv4mpeg", videopipe);
        RETURN_IF_BAD_SRET(sret, videopipe);
    } else {
        sret = tc_snprintf(import_cmd_buf, TC_BUF_MAX,
                           "tcextract -i %s -x yuv420p -t yuv4mpeg |"
                           " tcdecode -x yuv420p -g %dx%d",
                           videopipe, vob->im_v_width, vob->im_v_height);
        RETURN_IF_BAD_SRET(sret, videopipe);
    }

    // print out
    if (verbose_flag)
        tc_log_info(MOD_NAME, "%s", import_cmd_buf);

    param->fd = popen(import_cmd_buf, "r");
    RETURN_IF_OPEN_FAILED(videopipefd, "popen YUV stream");

    return TC_IMPORT_OK;
}

static int tc_mplayer_open_audio(vob_t *vob, transfer_t *param)
{
    char import_cmd_buf[TC_BUF_MAX];
    long sret = 0;

    tc_snprintf(audiopipe, sizeof(audiopipe), AUDIOPIPE_TEMPLATE);
    if (!mktemp(audiopipe)) {
        tc_log_perror(MOD_NAME, "mktemp audiopipe failed");
        return(TC_IMPORT_ERROR);
    }
    if (mkfifo(audiopipe, 00660) == -1) {
        tc_log_perror(MOD_NAME, "mkfifo audio failed");
        unlink(audiopipe);
        return(TC_IMPORT_ERROR);
    }

    sret = tc_snprintf(import_cmd_buf, TC_BUF_MAX,
                       "mplayer -slave -hardframedrop -vo null -ao pcm:nowaveheader"
                       ":file=\"%s\" %s \"%s\" > /dev/null 2>&1",
                       audiopipe, (vob->im_a_string ? vob->im_a_string : ""),
                       vob->audio_in_file);
    RETURN_IF_BAD_SRET(sret, audiopipe);

    if (verbose_flag)
        tc_log_info(MOD_NAME, "%s", import_cmd_buf);

    audiopipefd = popen(import_cmd_buf, "w");
    RETURN_IF_OPEN_FAILED(audiopipefd, "popen audiopipe failed");

    /* 
     * XXX
     * ok, this is really an ugly *temporary* hack that make things work.
     * I'm not proud nor satisfied of this, but there isn't much that
     * better this moment.
     */
    sret = tc_snprintf(import_cmd_buf, TC_BUF_MAX,
                       "tcextract -i %s -x pcm -t raw", audiopipe);
    RETURN_IF_BAD_SRET(sret, audiopipe);

    // print out
    if (verbose_flag)
        tc_log_info(MOD_NAME, "%s", import_cmd_buf);

    param->fd = popen(import_cmd_buf, "r");
    RETURN_IF_OPEN_FAILED(audiopipefd, "popen PCM stream");

    return TC_IMPORT_OK;
}

/*
 * OK, there is a nasty deadlocks when dealing with (audio) pipe
 * hidden and buried in this module internals.
 * short history:
 * - mplayer keeps writing data on the FIFO but
 * - transcode stops reading from FIFO, so
 * - FIFO buffer eventually become full and
 * - mplayer blocks, so cannot terminate, but
 * - transcode waits for mplayer termination:
 * - DEADLOCK!
 *
 * possible workaround:
 * static void tc_mplayer_send_quit(FILE *fd)
 * {
 *     fprintf(fd, "quit\n");
 *     fflush(fd);
 * }
 *
 * and invoke it in close* functions
 */

static int tc_mplayer_close_video(transfer_t *param)
{
    if (param->fd != NULL) {
        pclose(param->fd);
    }
    if (videopipefd != NULL) {
        pclose(videopipefd);
        videopipefd = NULL;
    }
    unlink(videopipe);
    return TC_IMPORT_OK; 
}

static int tc_mplayer_close_audio(transfer_t *param)
{
    if (param->fd != NULL)
        pclose(param->fd);
    if (audiopipefd != NULL) {
        pclose(audiopipefd);
        audiopipefd = NULL;
    }
    unlink(audiopipe);
    return TC_IMPORT_OK;
}

/* ------------------------------------------------------------
 * main external API.
 * ------------------------------------------------------------*/

MOD_open
{
    /* check for mplayer */
    if (tc_test_program("mplayer") != 0) {
        return TC_IMPORT_ERROR;
    }
    if (param->flag == TC_VIDEO) {
        return tc_mplayer_open_video(vob, param);
    }
    if (param->flag == TC_AUDIO) {
        return tc_mplayer_open_audio(vob, param);
    }
    return TC_IMPORT_ERROR;
}


MOD_decode
{
    return TC_IMPORT_OK;
}

MOD_close
{
    if (param->flag == TC_VIDEO) {
        return tc_mplayer_close_video(param);
    }
    if (param->flag == TC_AUDIO) {
        return tc_mplayer_close_audio(param);
    }
    return TC_IMPORT_ERROR;
}

/*************************************************************************/

/*
 * Local variables:
 *   c-file-style: "stroustrup"
 *   c-file-offsets: ((case-label . *) (statement-case-intro . *))
 *   indent-tabs-mode: nil
 * End:
 *
 * vim: expandtab shiftwidth=4:
 */
