/* 
 * xtoffmpeg.c
 *
 * Copyright (C) 2003-06 Karl H. Beckers, Frankfurt
 * EMail: khb@jarre-de-the.net
 *
 * This program 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 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#ifdef HAVE_CONFIG_H
# include <config.h>
#endif                          // HAVE_CONFIG_H

#define DEBUGFILE "xtoffmpeg.c"

#ifdef USE_FFMPEG

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/time.h>           // for timeval struct and related
                                // functions
#include <math.h>

#include <X11/Intrinsic.h>

// xvidcap specific
#include "app_data.h"
#include "job.h"
#include "colors.h"
#include "frame.h"
#include "codecs.h"
#include "xvidcap-intl.h"

// ffmpeg stuff
#include <ffmpeg/avcodec.h>
#include <ffmpeg/avformat.h>
#include "swscale.h"
#include "rgb2rgb.h"
#include "framehook.h"
#include "dsputil.h"
#include "opt.h"
#include "fifo.h"

#define PIX_FMT_ARGB32 PIX_FMT_RGBA32   // this is just my personal
// convenience


/* 
 * globals
 *
 * first libavcodec related stuff
 */
extern xvCodec tCodecs[NUMCODECS];
extern xvFFormat tFFormats[NUMCODECS];
extern xvAuCodec tAuCodecs[NUMAUCODECS];

static AVCodec *codec;          // params for the codecs video 
static AVFrame *p_inpic;        // a AVFrame as wrapper around the
                                        // original image data 
static AVFrame *p_outpic;       // and one for the image converted to
                                        // yuv420p 
static uint8_t *outpic_buf;     // data buffer for output frame
static uint8_t *outbuf;         // output buffer for encoded frame 
static int outbuf_size;         // the size of the outbuf may be
                                        // other than image_size
static AVFormatContext *output_file;    // output file via avformat 
static AVOutputFormat *file_oformat;    // ... plus related data 
static AVStream *out_st = NULL; // ... 
static struct SwsContext *img_resample_ctx; // for image resampling

static int image_size, input_pixfmt;    // size of yuv image, pix_fmt of
                                        // original image 
static int out_size;            // size of the encoded frame to write to
                                        // file 
static double audio_pts, video_pts;

static ColorInfo c_info;
static uint8_t *scratchbuf8bit;

#ifdef DEBUG
void dump8bit(XImage * image, u_int32_t * ct);
void dump32bit(XImage * input);
void x2ffmpeg_dump_ximage_info(XImage * img, FILE * fp);
#endif                          // DEBUG


#ifdef HAVE_FFMPEG_AUDIO

#define MAX_AUDIO_PACKET_SIZE (128 * 1024)

#include <pthread.h>
#include <signal.h>


typedef struct AVOutputStream {
    int file_index;             /* file index */
    int index;                  /* stream index in the output file */
    int source_index;           /* AVInputStream index */
    AVStream *st;               /* stream in the output file */
    int encoding_needed;        /* true if encoding needed for this stream 
                                 */
    int frame_number;
    /* input pts and corresponding output pts for A/V sync */
    // double sync_ipts; /* dts from the AVPacket of the demuxer in second 
    // 
    // 
    // units */
    struct AVInputStream *sync_ist; /* input stream to sync against */
    int64_t sync_opts;          /* output frame counter, could be changed
                                 * to some true timestamp */
    /* video only */
    int video_resample;
    AVFrame pict_tmp;           /* temporary image for resampling */
    struct SwsContext *img_resample_ctx;    /* for image resampling */
    int resample_height;

    int video_crop;
    int topBand;                /* cropping area sizes */
    int leftBand;

    int video_pad;
    int padtop;                 /* padding area sizes */
    int padbottom;
    int padleft;
    int padright;

    /* audio only */
    int audio_resample;
    ReSampleContext *resample;  /* for audio resampling */
    AVFifoBuffer fifo;          /* for compression: one audio fifo per
                                 * codec */
    FILE *logfile;
} AVOutputStream;



typedef struct AVInputStream {
    int file_index;
    int index;
    AVStream *st;
    int discard;                /* true if stream data should be discarded 
                                 */
    int decoding_needed;        /* true if the packets must be decoded in
                                 * 'raw_fifo' */
    int64_t sample_index;       /* current sample */

    int64_t start;              /* time when read started */
    unsigned long frame;        /* current frame */
    int64_t next_pts;           /* synthetic pts for cases where pkt.pts
                                 * is not defined */
    int64_t pts;                /* current pts */
    int is_start;               /* is 1 at the start and after a
                                 * discontinuity */
} AVInputStream;



// external variables for thread synchronization
extern pthread_mutex_t recording_mutex;
extern pthread_cond_t recording_condition_unpaused;

// FIXME: check if this all needs to be global
static AVCodec *au_codec;
static AVCodecContext *au_c;
static AVFormatContext *ic;
static AVInputFormat *grab_iformat;
static AVOutputStream *au_out_st;
static AVInputStream *au_in_st;

static uint8_t *audio_buf = NULL;
static uint8_t *audio_out = NULL;

static pthread_attr_t tattr;
static pthread_mutex_t mp = PTHREAD_MUTEX_INITIALIZER;
static pthread_t tid = 0;
static int tret = 0;


// functions ...

static void add_audio_stream(Job * job)
{
#define DEBUGFUNCTION "add_audio_stream()"
    Boolean grab_audio = TRUE;
    AVFormatParameters params, *ap = &params;   // audio stream params
    int err, ret;

#ifdef DEBUG
    printf("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG

    if (!strcmp(job->snd_device, "-")) {
        job->snd_device = "pipe:";
        grab_audio = FALSE;
    } else {
        grab_audio = TRUE;
    }

    // prepare input stream
    memset(ap, 0, sizeof(*ap));
    ap->device = job->snd_device;

    // this is required to guarantee import of the audio input format
    // context
    // for some strange reason this only works here and not further down
    // with av_register_all
    // audio_init();

    if (grab_audio) {
        ap->sample_rate = job->snd_rate;
        ap->channels = job->snd_channels;

        grab_iformat = av_find_input_format("audio_device");

#ifdef DEBUG
        printf("%s %s: grab iformat %p\n", DEBUGFILE, DEBUGFUNCTION,
               grab_iformat);
        if (grab_iformat)
            printf("%s %s: grab iformat name %s\n", DEBUGFILE,
                   DEBUGFUNCTION, grab_iformat->name);
#endif                          // DEBUG

        if (av_open_input_file(&ic, "", grab_iformat, 0, ap) < 0) {
            fprintf(stderr,
                    _("%s %s:Could not find audio grab device %s\n"),
                    DEBUGFILE, DEBUGFUNCTION, job->snd_device);
            exit(1);
        }
    } else {
        err = av_open_input_file(&ic, ap->device, NULL, 0, ap);
        if (err < 0) {
            fprintf(stderr, _("%s %s: error opening input file %s: %i\n"),
                    DEBUGFILE, DEBUGFUNCTION, job->snd_device, err);
            exit(1);
        }
    }
    au_in_st = av_mallocz(sizeof(AVInputStream));
    if (!au_in_st) {
        fprintf(stderr,
                _("%s %s: Could not alloc input stream ... aborting\n"),
                DEBUGFILE, DEBUGFUNCTION);
        exit(1);
    }
    au_in_st->st = ic->streams[0];

    // If not enough info to get the stream parameters, we decode
    // the first frames to get it. (used in mpeg case for example) 
    ret = av_find_stream_info(ic);
    if (ret < 0) {
        fprintf(stderr, _("%s %s: %s: could not find codec parameters\n"),
                DEBUGFILE, DEBUGFUNCTION, job->snd_device);
        exit(1);
    }
    // init pts stuff
    au_in_st->next_pts = 0;
    au_in_st->is_start = 1;

#ifdef DEBUG
    dump_format(ic, 0, job->snd_device, 0);
#endif                          // DEBUG

    // OUTPUT
    // setup output codec
    au_c = avcodec_alloc_context();

    // put sample parameters 
    au_c->codec_id = tAuCodecs[job->au_targetCodec].ffmpeg_id;
    au_c->codec_type = CODEC_TYPE_AUDIO;
    au_c->bit_rate = job->snd_smplsize;
    au_c->sample_rate = job->snd_rate;
    au_c->channels = job->snd_channels;
    // au_c->debug = 0x00000FFF;

    // prepare output stream 
    au_out_st = av_mallocz(sizeof(AVOutputStream));
    if (!au_out_st) {
        fprintf(stderr,
                _("%s %s: Could not alloc stream ... aborting\n"),
                DEBUGFILE, DEBUGFUNCTION);
        exit(1);
    }
    au_out_st->st = av_new_stream(output_file, 1);
    if (!au_out_st->st) {
        fprintf(stderr, _("%s %s: Could not alloc stream\n"),
                DEBUGFILE, DEBUGFUNCTION);
        exit(1);
    }
    au_out_st->st->codec = au_c;

    if (av_fifo_init(&au_out_st->fifo, 2 * MAX_AUDIO_PACKET_SIZE)) {
        fprintf(stderr,
                _("%s %s: Can't initialize fifo for audio recording\n"),
                DEBUGFILE, DEBUGFUNCTION);
        exit(1);
    }
    // This bit is important for inputs other than self-sampled.
    // The sample rates and no of channels a user asks for
    // are the ones he/she wants in the encoded mpeg. For self-
    // sampled audio, these are also the values used for sampling.
    // Hence there is no resampling necessary (case 1).
    // When dubbing from a pipe or a different
    // file, we might have different sample rates or no of
    // channels
    // in the input file.....
    if (au_c->channels == au_in_st->st->codec->channels &&
        au_c->sample_rate == au_in_st->st->codec->sample_rate) {
        au_out_st->audio_resample = 0;
    } else {
        if (au_c->channels != au_in_st->st->codec->channels &&
            au_in_st->st->codec->codec_id == CODEC_ID_AC3) {
            // Special case for 5:1 AC3 input 
            // and mono or stereo output 
            // Request specific number of channels 
            au_in_st->st->codec->channels = au_c->channels;
            if (au_c->sample_rate == au_in_st->st->codec->sample_rate)
                au_out_st->audio_resample = 0;
            else {
                au_out_st->audio_resample = 1;
                au_out_st->resample =
                    audio_resample_init(au_c->channels,
                                        au_in_st->st->codec->
                                        channels,
                                        au_c->sample_rate,
                                        au_in_st->st->codec->sample_rate);
                if (!au_out_st->resample) {
                    printf(_("%s %s: Can't resample. Aborting.\n"),
                           DEBUGFILE, DEBUGFUNCTION);
                    exit(1);
                }
            }
            // Request specific number of channels 
            au_in_st->st->codec->channels = au_c->channels;
        } else {
            au_out_st->audio_resample = 1;
            au_out_st->resample =
                audio_resample_init(au_c->channels,
                                    au_in_st->st->codec->channels,
                                    au_c->sample_rate,
                                    au_in_st->st->codec->sample_rate);
            if (!au_out_st->resample) {
                printf(_("%s %s: Can't resample. Aborting.\n"), DEBUGFILE,
                       DEBUGFUNCTION);
                exit(1);
            }
        }
    }
    au_in_st->decoding_needed = 1;
    au_out_st->encoding_needed = 1;

    // open encoder
    au_codec = avcodec_find_encoder(au_out_st->st->codec->codec_id);
    if (avcodec_open(au_out_st->st->codec, au_codec) < 0) {
        fprintf(stderr,
                _("%s %s: Error while opening codec for output stream\n"),
                DEBUGFILE, DEBUGFUNCTION);
        exit(1);
    }
    // open decoder
    au_codec = avcodec_find_decoder(ic->streams[0]->codec->codec_id);
    if (!au_codec) {
        fprintf(stderr,
                _("%s %s: Unsupported codec (id=%d) for input stream\n"),
                DEBUGFILE, DEBUGFUNCTION, ic->streams[0]->codec->codec_id);
        exit(1);
    }
    if (avcodec_open(ic->streams[0]->codec, au_codec) < 0) {
        fprintf(stderr,
                _("%s %s: Error while opening codec for input stream\n"),
                DEBUGFILE, DEBUGFUNCTION);
        exit(1);
    }
#ifdef DEBUG
    printf("%s %s: Leaving with %i streams in oc\n", DEBUGFILE,
           DEBUGFUNCTION, output_file->nb_streams);
#endif                          // DEBUG
#undef DEBUGFUNCTION
}


static void
do_audio_out(AVFormatContext * s, AVOutputStream * ost,
             AVInputStream * ist, unsigned char *buf, int size)
{
#define DEBUGFUNCTION "do_audio_out()"
    uint8_t *buftmp;
    const int audio_out_size = 4 * MAX_AUDIO_PACKET_SIZE;
    int size_out, frame_bytes;
    AVCodecContext *enc;

    // SC: dynamic allocation of buffers
    if (!audio_buf)
        audio_buf = av_malloc(2 * MAX_AUDIO_PACKET_SIZE);
    if (!audio_out)
        audio_out = av_malloc(audio_out_size);
    if (!audio_out || !audio_buf)
        return;                 // Should signal an error !

    enc = ost->st->codec;

    // resampling is only used for pipe input here
    if (ost->audio_resample) {
        buftmp = audio_buf;
        size_out =
            audio_resample(ost->resample, (short *) buftmp, (short *) buf,
                           size / (ist->st->codec->channels * 2));
        size_out = size_out * enc->channels * 2;
    } else {
        buftmp = buf;
        size_out = size;
    }

    // now encode as many frames as possible 
    if (enc->frame_size > 1) {
        // output resampled raw samples 
        av_fifo_write(&ost->fifo, buftmp, size_out);
        frame_bytes = enc->frame_size * 2 * enc->channels;

        while (av_fifo_read(&ost->fifo, audio_buf, frame_bytes) == 0) {
            AVPacket pkt;

            // initialize audio output packet
            av_init_packet(&pkt);

            pkt.size =
                avcodec_encode_audio(enc, audio_out, audio_out_size,
                                     (short *) audio_buf);

            if (enc->coded_frame) {
                pkt.pts =
                    av_rescale_q(enc->coded_frame->pts, enc->time_base,
                                 ost->st->time_base);
            }
            pkt.flags |= PKT_FLAG_KEY;
            pkt.stream_index = ost->st->index;

            pkt.data = audio_out;

            if (pthread_mutex_trylock(&mp) == 0) {
                // write the compressed frame in the media file 
                if (av_interleaved_write_frame(s, &pkt) != 0) {
                    fprintf(stderr,
                            _("%s %s: Error while writing audio frame\n"),
                            DEBUGFILE, DEBUGFUNCTION);
                    exit(1);
                }

                if (pthread_mutex_unlock(&mp) > 0) {
                    fprintf(stderr,
                            _
                            ("%s %s: Couldn't unlock mutex lock for writing audio frame\n"),
                            DEBUGFILE, DEBUGFUNCTION);
                }
            }
        }
    }
#undef DEBUGFUNCTION
}


void cleanup_thread_when_stopped()
{
#define DEBUGFUNCTION "cleanup_thread_when_stopped()"
    int retval = 0;

#ifdef DEBUG
    printf("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG

    if (audio_out) {
        av_free(audio_out);
        audio_out = NULL;
    }
    if (audio_buf) {
        av_free(audio_buf);
        audio_buf = NULL;
    }
    grab_iformat = NULL;
    if (au_in_st) {
        av_free(au_in_st);
        au_in_st = NULL;
    }

    av_close_input_file(ic);

#ifdef DEBUG
    printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG

    pthread_exit(&retval);

#undef DEBUGFUNCTION
}


void capture_audio_thread(Job * job)
{
#define DEBUGFUNCTION "capture_audio_thread()"
    unsigned long start, stop, start_s, stop_s;
    struct timeval thr_curr_time;
    long sleep;
    int retval = -1, len, data_size;
    uint8_t *ptr, *data_buf;
    short samples[AVCODEC_MAX_AUDIO_FRAME_SIZE / 2];
    AVPacket pkt;

    signal(SIGUSR1, cleanup_thread_when_stopped);

    while (TRUE) {
        // get start time
        gettimeofday(&thr_curr_time, NULL);
        start_s = thr_curr_time.tv_sec;
        start = thr_curr_time.tv_usec;

        if ((job->state & VC_PAUSE) && !(job->state & VC_STEP)) {
            pthread_mutex_lock(&recording_mutex);
            pthread_cond_wait(&recording_condition_unpaused,
                              &recording_mutex);
            pthread_mutex_unlock(&recording_mutex);
        } else if (job->state == VC_REC) {

            audio_pts = (double)    /* au_out_st->st->pts.val * * *
                                     * av_q2d(au_out_st->st->time_base); */
                au_out_st->st->pts.val *
                au_out_st->st->time_base.num /
                au_out_st->st->time_base.den;
            video_pts =
                (double) out_st->pts.val * out_st->time_base.num /
                out_st->time_base.den;

            // sometimes we need to pause writing audio packets for a/v
            // sync (when audio_pts >= video_pts)
            // now, if we're reading from a file/pipe, we stop sampling or 
            // 
            // 
            // 
            // else the audio track in the
            // video would become choppy (packets missing where they were
            // read but not written)
            // for real-time sampling we can't do that because otherwise
            // the input packets queue up
            // and will eventually be sampled (only later) and lead to
            // out-of-sync audio (video faster)
            if (audio_pts < video_pts) {
                // read a packet from it and output it in the fifo 
                if (av_read_packet(ic, &pkt) < 0) {
                    fprintf(stderr,
                            _("%s %s: error reading audio packet\n"),
                            DEBUGFILE, DEBUGFUNCTION);
                }
                len = pkt.size;
                ptr = pkt.data;
                while (len > 0) {
                    // decode the packet if needed 
                    data_buf = NULL;    /* fail safe */
                    data_size = 0;

                    if (au_in_st->decoding_needed) {
                        /* 
                         * XXX: could avoid copy if PCM 16 bits with same
                         * endianness as CPU 
                         */
                        retval =
                            avcodec_decode_audio(au_in_st->st->codec,
                                                 samples, &data_size, ptr,
                                                 len);
                        if (retval < 0) {
                            fprintf(stderr,
                                    _
                                    ("%s %s: couldn't decode captured audio packet\n"),
                                    DEBUGFILE, DEBUGFUNCTION);
                            break;
                        }
                        // Some bug in mpeg audio decoder gives 
                        // data_size < 0, it seems they are overflows 
                        if (data_size <= 0) {
                            // no audio frame 
#ifdef DEBUG
                            fprintf(stderr, _("%s %s: no audio frame\n"),
                                    DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG
                            ptr += retval;
                            len -= retval;
                            continue;
                        }
                        data_buf = (uint8_t *) samples;
                        au_in_st->next_pts +=
                            ((int64_t) AV_TIME_BASE / 2 * data_size) /
                            (au_in_st->st->codec->sample_rate *
                             au_in_st->st->codec->channels);
                    } else {
                        // FIXME: dunno about the following
                        au_in_st->next_pts +=
                            ((int64_t) AV_TIME_BASE / 2 * data_size) /
                            (au_in_st->st->codec->sample_rate *
                             au_in_st->st->codec->channels);
                    }
                    ptr += retval;
                    len -= retval;

                    do_audio_out(output_file, au_out_st, au_in_st,
                                 data_buf, data_size);
                }
                // discard packet
                av_free_packet(&pkt);
            }                   // end outside if pts ...
            else {
                if (strcmp(job->snd_device, "pipe:") < 0)
                    if (av_read_packet(ic, &pkt) < 0) {
                        fprintf(stderr, _("error reading audio packet\n"));
                    }
#ifdef DEBUG
                printf(_("%s %s: dropping audio frame %f %f\n"),
                       DEBUGFILE, DEBUGFUNCTION, audio_pts, video_pts);
#endif                          // DEBUG
            }
        }                       // end if VC_REC
        // get end time
        gettimeofday(&thr_curr_time, NULL);
        stop_s = thr_curr_time.tv_sec;
        stop = thr_curr_time.tv_usec;
        stop += ((stop_s - start_s) * 1000000);
        sleep = (1000000 / job->snd_rate) - (stop - start);
        // FIXME: perhaps this whole stuff is irrelevant. Haven't really
        // seen a situation
        // where the encoding was faster than the time needed for a 
        // decent frame-rate
        // need to look into more details about audio/video sync in 
        // libavcodec/-format
        // the strange thing, however is: If I leave away the getting of
        // start/end time and
        // the usleep stuff, normal audio capture works, piped audio
        // doesn't *BLINK*
        if (sleep < 0)
            sleep = 0;

        usleep(sleep);
    }                           // end while(TRUE) loop
    retval = 1;
    pthread_exit(&retval);
#undef DEBUGFUNCTION
}

#endif                          // HAVE_FFMPEG_AUDIO


static void
do_video_out(AVFormatContext * s, AVStream * ost,
             unsigned char *buf, int size)
{
#define DEBUGFUNCTION "do_video_out()"
    AVCodecContext *enc;
    AVPacket pkt;

#ifdef DEBUG
    printf
        ("%s %s: Entering with format context %p output stream %p buffer %p size %i\n",
         DEBUGFILE, DEBUGFUNCTION, s, ost, buf, size);
#endif                          // DEBUG

    enc = ost->codec;

    // initialize video output packet
    av_init_packet(&pkt);

    if (enc->coded_frame) {
        pkt.pts =
            av_rescale_q(enc->coded_frame->pts, enc->time_base,
                         ost->time_base);
        if (enc->coded_frame->key_frame)
            pkt.flags |= PKT_FLAG_KEY;
    }

    pkt.stream_index = ost->index;
    pkt.data = buf;
    pkt.size = size;

    if (av_interleaved_write_frame(s, &pkt) != 0) {
        fprintf(stderr, _("%s %s: Error while writing video frame\n"),
                DEBUGFILE, DEBUGFUNCTION);
        exit(1);
    }
#ifdef DEBUG
    printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG

#undef DEBUGFUNCTION
}



// 
// convert bgra32 to rgba32
// needed on Solaris/SPARC because the ffmpeg version used doesn't know
// PIX_FMT_ABGR32
// 
void myABGR32toARGB32(XImage * image)
{
#define DEBUGFUNCTION "myABGR32toARGB32()"

    char *pdata, *counter;

#ifdef DEBUG
    printf("%s %s: Entering with image %p\n",
           DEBUGFILE, DEBUGFUNCTION, image);
#endif                          // DEBUG

    pdata = image->data;

    for (counter = pdata;
         counter < (pdata + (image->width * image->height * 4));
         counter += 4) {
        char swap;              // , swap1, swap2, swap3, swap4;

        if (image->byte_order) {    // MSBFirst has order argb -> abgr 
            // = rgba32
            swap = *(counter + 1);
            *(counter + 1) = *(counter + 3);
            *(counter + 3) = swap;
        } else {                // LSBFirst has order bgra -> rgba
            swap = *counter;
            *counter = *(counter + 2);
            *(counter + 2) = swap;
        }
    }

#ifdef DEBUG
    printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG

#undef DEBUGFUNCTION
}


// 
// convert pal8 to rgba32
// libswscale does not support pal8 input
// 
void myPAL8toRGB24(XImage * image, AVFrame * p_inpic, Job * job)
{
#define DEBUGFUNCTION "myABGR32toRGB24()"

    u_int32_t *color_table = job->color_table;
    int y = 0, x = 0;
    uint8_t *out_cursor = NULL;
    uint8_t *out = (uint8_t *) p_inpic->data[0];

    /* 
     * 8bit pseudo-color images may have lines padded by excess bytes
     * these need to be removed before conversion FIXME: other formats 
     * might also have this problem 
     */
    for (y = 0; y < image->height; y++) {
        out_cursor = (uint8_t *) image->data + (y * image->bytes_per_line);
        for (x = 0; x < image->width; x++) {
            *out++ = ((color_table[*out_cursor] & 0x00FF0000) >> 16);
            *out++ = ((color_table[*out_cursor] & 0x0000FF00) >> 8);
            *out++ = (color_table[*out_cursor] & 0x000000FF);
            out_cursor++;
        }
    }

#undef DEBUGFUNCTION
}


/* 
 * prepare the color table for pseudo color input to libavcodec's imgconvert
 * I'm downsampling the 16 bit color entries to 8 bit as it expects
 * 32 bit (=4 * 8)
 * from looking at imgconvert_template.c I'd say libavcodec expects argb
 * logically, not byte-wise
 */
u_int32_t *FFMPEGcolorTable(XColor * colors, int ncolors)
{
#define DEBUGFUNCTION "FFMPEGcolorTable()"

    u_int32_t *color_table, *pixel;
    int i;                      // , n;

#ifdef DEBUG
    printf("%s %s: Entering with colors %p and %i colors\n",
           DEBUGFILE, DEBUGFUNCTION, colors, ncolors);
#endif                          // DEBUG

    color_table = malloc(256 * 4);
    if (!color_table)
        return (NULL);

    pixel = color_table;
    for (i = 0; i < ncolors; i++) {
        u_int32_t color_table_entry, swap;

        color_table_entry = 0;
        // alpha alway zero

        swap = colors[i].red;
        swap &= 0x0000FF00;     // color is 16 bits, delete ls 8 bits
        swap <<= 8;             // then shift ms 8 bits into position
        color_table_entry = (color_table_entry | swap);

        swap = colors[i].green;
        swap &= 0x0000FF00;     // color is 16 bits, ms 8 bits already in
        // position, delete ls 8 bits
        color_table_entry = (color_table_entry | swap);

        swap = colors[i].blue;
        swap >>= 8;
        color_table_entry = (color_table_entry | swap);

        *pixel = color_table_entry;
        pixel++;
    }
#ifdef DEBUG
    printf("%s %s: color_table pointer: %p\n",
           DEBUGFILE, DEBUGFUNCTION, color_table);
    printf("%s %s: color_table third entry: 0x%.8X\n",
           DEBUGFILE, DEBUGFUNCTION, *(color_table + 2));
    // first and second black & white
#endif                          // DEBUG

#ifdef DEBUG
    printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG

    return (color_table);

#undef DEBUGFUNCTION
}



void prepareOutputFile(char *jFileName, AVFormatContext * oc, int number)
{
#define DEBUGFUNCTION "prepareOutputFile()"

#ifdef DEBUG
    printf
        ("%s %s: Entering with filename %s format context %p and number %i\n",
         DEBUGFILE, DEBUGFUNCTION, jFileName, oc, number);
#endif                          // DEBUG

    // prepare output file for format context 

    if ((strcasecmp(jFileName, "-") == 0)
        || (strcasecmp(jFileName, "pipe:") == 0)) {
        jFileName = "pipe:";
        snprintf(oc->filename, sizeof(oc->filename), "pipe:");
        // register_protocol (&pipe_protocol);
    } else {
        char first;
        char tmp_buf[PATH_MAX + 1];

        first = jFileName[0];
        sprintf(tmp_buf, jFileName, number);

        // if the filename's first char is a / we have an absolute path
        // and we want one for the file URL. If we don't have one, we
        // construct one 
        if (first != '/') {
            sprintf(oc->filename, "file://%s/%s", getenv("PWD"), tmp_buf);
        } else {
            sprintf(oc->filename, "file://%s", tmp_buf);
        }
        // register_protocol (&file_protocol);
    }

#ifdef DEBUG
    printf("%s %s: Leaving with filename %s\n", DEBUGFILE, DEBUGFUNCTION,
           oc->filename);
#endif                          // DEBUG

#undef DEBUGFUNCTION
}



/* 
 * add a video output stream 
 */
AVStream *add_video_stream(AVFormatContext * oc, XImage * image,
                           int input_pixfmt, int codec_id, Job * job)
{
#define DEBUGFUNCTION "add_video_stream()"
    AVStream *st;
    int pix_fmt_mask = 0, i = 0;
    int fps = job->fps / 100, quality = job->quality;

#ifdef DEBUG
    printf("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG

    st = av_new_stream(oc, 0);
    if (!st) {
        fprintf(stderr,
                _("%s %s: Could not alloc output stream\n"), DEBUGFILE,
                DEBUGFUNCTION);
        exit(1);
    }

    st->codec->codec_id = codec_id;
    st->codec->codec_type = CODEC_TYPE_VIDEO;

    // find the video encoder 
    codec = avcodec_find_encoder(st->codec->codec_id);
    if (!codec) {
        fprintf(stderr, _("%s %s: video codec not found\n"),
                DEBUGFILE, DEBUGFUNCTION);
        exit(1);
    }
    // put sample parameters 
    st->codec->bit_rate = 400000;
    // resolution must be a multiple of two ... this is taken care of
    // elsewhere but needs to be ensured for rescaled dimensions, too
    if (job->rescale != 100) {
        int n;
        double r;
        r = sqrt(100 / job->rescale);

        n = image->width / r;
        if (n % 2 > 0)
            n--;
        st->codec->width = n;

        n = image->height / r;
        if (n % 2 > 0)
            n--;
        st->codec->height = n;
    } else {
        st->codec->width = image->width;
        st->codec->height = image->height;
    }
    // time base: this is the fundamental unit of time (in seconds) in
    // terms of which frame timestamps are represented. for fixed-fps
    // content, timebase should be 1/framerate and timestamp increments
    // should be identically 1. 
    st->codec->time_base.den = fps;
    st->codec->time_base.num = 1;
    // emit one intra frame every fifty frames at most
    st->codec->gop_size = 50;

    // find suitable pix_fmt for codec
    if (&(codec->pix_fmts) != NULL) {
        for (i = 0; codec->pix_fmts[i] != -1; i++) {
            pix_fmt_mask |= (1 << codec->pix_fmts[i]);
        }
        st->codec->pix_fmt =
            avcodec_find_best_pix_fmt(pix_fmt_mask, input_pixfmt,
                                      (input_pixfmt ==
                                       PIX_FMT_ARGB32 ? 1 : 0), NULL);
        if (st->codec->pix_fmt < 0)
            st->codec->pix_fmt = PIX_FMT_YUV420P;
    } else
        st->codec->pix_fmt = PIX_FMT_YUV420P;

#ifdef DEBUG
    printf
        ("%s %s: pix_fmt_mask %i, has alpha %i, input_pixfmt %i, output pixfmt %i\n",
         DEBUGFILE, DEBUGFUNCTION, pix_fmt_mask,
         (input_pixfmt == PIX_FMT_ARGB32 ? 1 : 0), input_pixfmt,
         st->codec->pix_fmt);
#endif                          // DEBUG

    // flags
    st->codec->flags |= (CODEC_FLAG_TRELLIS_QUANT | CODEC_FLAG2_FAST);
    st->codec->flags &= ~CODEC_FLAG_OBMC;
    // some formats want stream headers to be seperate
    if (oc->oformat->flags & AVFMT_GLOBALHEADER)
        st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;

    // bit rate calculation
    st->codec->bit_rate =
        (st->codec->width * st->codec->height *
         (((((st->codec->height + st->codec->width) / 100) - 5) >> 1) +
          10) * quality) / 100;
    if (st->codec->bit_rate < 300000)
        st->codec->bit_rate = 300000;

#ifdef DEBUG
    printf("%s %s: bitrate = %i\n", DEBUGFILE, DEBUGFUNCTION,
           st->codec->bit_rate);
#endif

    st->codec->mb_decision = 2;
    /* st->codec->me_method = ME_ZERO; st->codec->qmin = 1;
     * st->codec->qmax = 1; */
    // st->codec->debug = 0x00000FFF;
    /* out_st->time_base.num = out_st->codec->time_base.num;
     * out_st->time_base.den = out_st->codec->time_base.den;
     * out_st->pts.val = (double)out_st->pts.val * out_st->time_base.num / 
     * * * out_st->time_base.den; */

#ifdef DEBUG
    printf("%s %s: Leaving with %i streams in oc\n", DEBUGFILE,
           DEBUGFUNCTION, oc->nb_streams);
#endif                          // DEBUG

    return st;

#undef DEBUGFUNCTION
}





int guess_input_pix_fmt(XImage * image, ColorInfo * c_info)
{
#define DEBUGFUNCTION "guess_input_pix_fmt()"
    int input_pixfmt;

    switch (image->bits_per_pixel) {
    case 8:
#ifdef DEBUG
        printf("%s %s: 8 bit pallete\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG
        input_pixfmt = PIX_FMT_PAL8;
        break;
    case 16:
        if (image->red_mask == 0xF800 && image->green_mask == 0x07E0
            && image->blue_mask == 0x1F) {
#ifdef DEBUG
            printf("%s %s: 16 bit RGB565\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG
            input_pixfmt = PIX_FMT_BGR565;
        } else if (image->red_mask == 0x7C00
                   && image->green_mask == 0x03E0
                   && image->blue_mask == 0x1F) {
#ifdef DEBUG
            printf("%s %s: 16 bit RGB555\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG
            input_pixfmt = PIX_FMT_BGR555;

        } else {
            fprintf(stderr,
                    _
                    ("%s %s: rgb ordering at image depth %i not supported ... aborting\n"),
                    DEBUGFILE, DEBUGFUNCTION, image->bits_per_pixel);
            fprintf(stderr,
                    "%s %s: color masks: r 0x%.6lX g 0x%.6lX b 0x%.6lX\n",
                    DEBUGFILE, DEBUGFUNCTION, image->red_mask,
                    image->green_mask, image->blue_mask);
            exit(1);
        }
        break;
    case 24:
        if (image->red_mask == 0xFF0000 && image->green_mask == 0xFF00
            && image->blue_mask == 0xFF) {
            input_pixfmt = PIX_FMT_BGR24;
        } else if (image->red_mask == 0xFF
                   && image->green_mask == 0xFF00
                   && image->blue_mask == 0xFF0000) {
            input_pixfmt = PIX_FMT_RGB24;
        } else {
            fprintf(stderr,
                    _
                    ("%s %s: rgb ordering at image depth %i not supported ... aborting\n"),
                    DEBUGFILE, DEBUGFUNCTION, image->bits_per_pixel);
            fprintf(stderr,
                    "%s %s: color masks: r 0x%.6lX g 0x%.6lX b 0x%.6lX\n",
                    DEBUGFILE, DEBUGFUNCTION, image->red_mask,
                    image->green_mask, image->blue_mask);
            exit(1);
        }
        break;
    case 32:
        if (c_info->alpha_mask == 0xFF000000
            && image->green_mask == 0xFF00) {
            // byte order is relevant here, not endianness
            // endianness is handled by avcodec, but atm no such thing
            // as having ABGR, instead of ARGB in a word. Since we
            // need this for Solaris/SPARC, but need to do the
            // conversion
            // for every frame we do it outside of this loop, cf.
            // below
            // this matches both ARGB32 and ABGR32
            input_pixfmt = PIX_FMT_ARGB32;
        } else {
            fprintf(stderr,
                    _
                    ("%s %s: image depth %i not supported ... aborting\n"),
                    DEBUGFILE, DEBUGFUNCTION, image->bits_per_pixel);
            exit(1);
        }
        break;
    default:
        fprintf(stderr,
                _("%s %s: image depth %i not supported ... aborting\n"),
                DEBUGFILE, DEBUGFUNCTION, image->bits_per_pixel);
        exit(1);
    }

    return input_pixfmt;
#undef DEBUGFUNCTION
}





/* 
 * write ximage as mpeg file to 'fp'
 */
void XImageToFFMPEG(FILE * fp, XImage * image, Job * job)
{
#define DEBUGFUNCTION "XImageToFFMPEG()"

    int ret;

#ifdef DEBUG
    printf("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG

    // encoder needs to be prepared only once .. 
    if (job->state & VC_START) {    // it's the first call

#ifdef DEBUG
        printf("%s %s: doing x2ffmpeg init for targetCodec %i\n",
               DEBUGFILE, DEBUGFUNCTION, job->targetCodec);
#endif                          // DEBUG

        // determine input picture format 
        // 
        // FIXME: test for exotic formats
        // endianness is treated by avcodec

        // color info only needs to be retrieved once for true color X ... 
        // 
        // 
        // 
        // dunno about pseudo color 
        xvc_get_color_info(image, &c_info);

#ifdef DEBUG
        {
            FILE *errout;

            printf("%s %s: got color info\n", DEBUGFILE, DEBUGFUNCTION);
            errout = fdopen(2, "w");
            // x2ffmpeg_dump_ximage_info(image, errout);
            printf("%s %s: alpha_mask: 0x%.8X\n",
                   DEBUGFILE, DEBUGFUNCTION, c_info.alpha_mask);
            printf("%s %s: alpha_shift: %li\n",
                   DEBUGFILE, DEBUGFUNCTION, c_info.alpha_shift);
            printf("%s %s: red_shift: %li\n",
                   DEBUGFILE, DEBUGFUNCTION, c_info.red_shift);
            printf("%s %s: green_shift: %li\n",
                   DEBUGFILE, DEBUGFUNCTION, c_info.green_shift);
            printf("%s %s: blue_shift: %li\n",
                   DEBUGFILE, DEBUGFUNCTION, c_info.blue_shift);
        }
#endif                          // DEBUG

#ifdef DEBUG
        printf("%s %s: image->byte_order: %i, msb=%i, lsb=%i\n",
               DEBUGFILE, DEBUGFUNCTION, image->byte_order, MSBFirst,
               LSBFirst);
#endif                          // DEBUG

        // determin input picture format
        input_pixfmt = guess_input_pix_fmt(image, &c_info);

        // register all libav* related stuff
        av_register_all();

        /* // next call the right init() function for the file format
         * (e.g. // avienc_init()) if ( tFFormats[job->target].init )
         * (*tFFormats[job->target].init) (); */
        // guess AVOutputFormat
        if (job->target >= CAP_MF)
            file_oformat =
                guess_format(tFFormats[job->target].ffmpeg_name, NULL,
                             NULL);
        else {
            char tmp_fn[30];
            snprintf(tmp_fn, 29, "test-%%d.%s",
                     xvc_next_element(tFFormats[job->target].extensions));
            file_oformat = guess_format(NULL, tmp_fn, NULL);
        }
        if (!file_oformat) {
            fprintf(stderr,
                    _
                    ("%s %s: Couldn't determin output format ... aborting\n"),
                    DEBUGFILE, DEBUGFUNCTION);
            exit(1);
        }
#ifdef DEBUG
        printf
            ("%s %s: found AVOutputFormat %s it expects a number in the filename (0=no/1=yes) %i\n",
             DEBUGFILE, DEBUGFUNCTION, file_oformat->name,
             (file_oformat->flags & AVFMT_NEEDNUMBER));
        printf
            ("%s %s: found based on: target %i - targetCodec %i - ffmpeg codec id %i\n",
             DEBUGFILE, DEBUGFUNCTION, job->target, job->targetCodec,
             tCodecs[job->targetCodec].ffmpeg_id);
#endif                          // DEBUG

        // prepare AVFormatContext
        output_file = av_alloc_format_context();
        if (!output_file) {
            fprintf(stderr,
                    _
                    ("%s %s: Error allocating memory for format context ... aborting\n"),
                    DEBUGFILE, DEBUGFUNCTION);
            exit(1);
        }
        output_file->oformat = file_oformat;
        if (output_file->oformat->priv_data_size > 0) {
            output_file->priv_data =
                av_mallocz(output_file->oformat->priv_data_size);
            // FIXME: do I need to free this?
            if (!output_file->priv_data) {
                fprintf(stderr,
                        _
                        ("%s %s: Error allocating private data for format context ... aborting\n"),
                        DEBUGFILE, DEBUGFUNCTION);
                exit(1);
            }
        }
        // output_file->packet_size= mux_packet_size;
        // output_file->mux_rate= mux_rate;
        output_file->preload = (int) (0.5 * AV_TIME_BASE);
        output_file->max_delay = (int) (0.7 * AV_TIME_BASE);
        // output_file->loop_output = loop_output;


        // add the video stream and initialize the codecs 
        // 
        // prepare stream 
        out_st =
            add_video_stream(output_file, image,
                             (input_pixfmt ==
                              PIX_FMT_PAL8 ? PIX_FMT_RGB24 : input_pixfmt),
                             tCodecs[job->targetCodec].ffmpeg_id, job);

        // FIXME: set params
        // memset (p_fParams, 0, sizeof(*p_fParams));
        // p_fParams->image_format = image_format;
        // p_fParams->time_base.den = out_st->codec->time_base.den;
        // p_fParams->time_base.num = out_st->codec->time_base.num;
        // p_fParams->width = out_st->codec->width;
        // p_fParams->height = out_st->codec->height; 
        // if (av_set_parameters (output_file, p_fParams) < 0) {
        if (av_set_parameters(output_file, NULL) < 0) {
            fprintf(stderr,
                    _("%s %s: Invalid encoding parameters ... aborting\n"),
                    DEBUGFILE, DEBUGFUNCTION);
            exit(1);
        }
        // open the codec 
        // c = out_st->codec;

        if (avcodec_open(out_st->codec, codec) < 0) {
            fprintf(stderr, _("%s %s: could not open video codec\n"),
                    DEBUGFILE, DEBUGFUNCTION);
            exit(1);
        }
#ifdef HAVE_FFMPEG_AUDIO
        if ((job->flags & FLG_REC_SOUND) && (job->au_targetCodec > 0)) {

            add_audio_stream(job);

#ifdef DEBUG
            dump_format(output_file, 0, output_file->filename, 1);
#endif                          // DEBUG

            // initialize a mutex lock to its default value 
            ret = pthread_mutex_init(&mp, NULL);

            // create and start capture thread 
            // initialized with default attributes 
            tret = pthread_attr_init(&tattr);

            // create the thread 
            tret =
                pthread_create(&tid, &tattr, (void *) capture_audio_thread,
                               job);
        }
#endif                          // HAVE_FFMPEG_AUDIO

        /* 
         * prepare pictures 
         */
        // input picture
        p_inpic = avcodec_alloc_frame();

        if (input_pixfmt == PIX_FMT_PAL8) {
            scratchbuf8bit =
                malloc(avpicture_get_size
                       (PIX_FMT_RGB24, image->width, image->height));
            if (!scratchbuf8bit) {
                fprintf(stderr,
                        "%s %s: Could not allocate buffer for 8bit palette conversion\n",
                        DEBUGFILE, DEBUGFUNCTION);
                exit(1);
            }

            avpicture_fill((AVPicture *) p_inpic, scratchbuf8bit,
                           input_pixfmt, image->width, image->height);
            p_inpic->data[0] = scratchbuf8bit;
            p_inpic->linesize[0] = image->width * 3;
        } else {
            avpicture_fill((AVPicture *) p_inpic, (uint8_t *) image->data,
                           input_pixfmt, image->width, image->height);
        }

        // output picture
        p_outpic = avcodec_alloc_frame();

        image_size =
            avpicture_get_size(out_st->codec->pix_fmt,
                               out_st->codec->width,
                               out_st->codec->height);
        outpic_buf = av_malloc(image_size);
        if (!outpic_buf) {
            fprintf(stderr,
                    _
                    ("%s %s: Could not allocate buffer for output frame! ... aborting\n"),
                    DEBUGFILE, DEBUGFUNCTION);
            exit(1);
        }
        avpicture_fill((AVPicture *) p_outpic, outpic_buf,
                       out_st->codec->pix_fmt, out_st->codec->width,
                       out_st->codec->height);

        /* 
         * prepare output buffer for encoded frames 
         */
        if ((image_size + 200) < FF_MIN_BUFFER_SIZE)
            outbuf_size = FF_MIN_BUFFER_SIZE;
        else
            outbuf_size = image_size + 200;
        outbuf = malloc(outbuf_size);
        if (!outbuf) {
            fprintf(stderr,
                    _
                    ("%s %s: Could not allocate buffer for encoded frame (outbuf)! ... aborting\n"),
                    DEBUGFILE, DEBUGFUNCTION);
            exit(1);
        }
        // img resampling
        if (!img_resample_ctx) {
            img_resample_ctx = sws_getContext(image->width,
                                              image->height,
                                              (input_pixfmt ==
                                               PIX_FMT_PAL8 ? PIX_FMT_RGB24
                                               : input_pixfmt),
                                              out_st->codec->width,
                                              out_st->codec->height,
                                              out_st->codec->pix_fmt, 1,
                                              NULL, NULL, NULL);
            // sws_rgb2rgb_init(SWS_CPU_CAPS_MMX*0);
        }
        // file preparation needs to be done once for multi-frame capture
        // and multiple times for single-frame capture
        if (job->target >= CAP_MF) {
            // prepare output filenames and register protocols
            // after this output_file->filename should have the right
            // filename
            prepareOutputFile(job->file, output_file, job->movie_no);

            // open the file 
            if (url_fopen
                (&output_file->pb, output_file->filename,
                 URL_WRONLY) < 0) {
                fprintf(stderr,
                        _("%s %s: Could not open '%s' ... aborting\n"),
                        DEBUGFILE, DEBUGFUNCTION, output_file->filename);
                exit(1);
            }

            if (av_write_header(output_file) < 0) {
                dump_format(output_file, 0, output_file->filename, 1);
                fprintf(stderr,
                        _
                        ("%s %s: Could not write header for output file (incorrect codec paramters ?) ... aborting\n"),
                        DEBUGFILE, DEBUGFUNCTION);
                exit(1);
            }


        }
#ifdef DEBUG
        printf("%s %s: leaving xffmpeg init\n", DEBUGFILE, DEBUGFUNCTION);

        printf("%s %s: codec %p\n", DEBUGFILE, DEBUGFUNCTION, codec);
        printf("%s %s: p_inpic %p\n", DEBUGFILE, DEBUGFUNCTION, p_inpic);
        printf("%s %s: p_outpic %p\n", DEBUGFILE, DEBUGFUNCTION, p_outpic);
        printf("%s %s: outpic_buf %p\n", DEBUGFILE, DEBUGFUNCTION,
               outpic_buf);
        printf("%s %s: outbuf %p\n", DEBUGFILE, DEBUGFUNCTION, outbuf);

        printf("%s %s: output_file %p\n", DEBUGFILE, DEBUGFUNCTION,
               output_file);
        printf("%s %s: file_oformat %p\n", DEBUGFILE, DEBUGFUNCTION,
               file_oformat);
        printf("%s %s: out_st %p\n", DEBUGFILE, DEBUGFUNCTION, out_st);

        printf("%s %s: image size %i - input pixfmt %i - out_size %i\n",
               DEBUGFILE, DEBUGFUNCTION, image_size, input_pixfmt,
               out_size);
        printf("%s %s: audio_pts %.f - video_pts %.f\n", DEBUGFILE,
               DEBUGFUNCTION, audio_pts, video_pts);

        printf("%s %s: c_info %p - scratchbuf8bit %p\n", DEBUGFILE,
               DEBUGFUNCTION, &c_info, scratchbuf8bit);
#endif                          // DEBUG
    }


    if (job->target < CAP_MF) {
        // prepare output filenames and register protocols
        // after this output_file->filename should have the right filename
        prepareOutputFile(job->file, output_file, job->pic_no);

        // open the file 
        if (url_fopen(&output_file->pb, output_file->filename, URL_WRONLY)
            < 0) {
            fprintf(stderr, _("%s %s: Could not open '%s' ... aborting\n"),
                    DEBUGFILE, DEBUGFUNCTION, output_file->filename);
            exit(1);
        }

        if (av_write_header(output_file) < 0) {
            dump_format(output_file, 0, output_file->filename, 1);
            fprintf(stderr,
                    _
                    ("%s %s: Could not write header for output file (incorrect codec paramters ?) ... aborting\n"),
                    DEBUGFILE, DEBUGFUNCTION);
            exit(1);
        }
    }


    /* 
     * convert input pic to pixel format the encoder expects 
     */
#ifdef DEBUG
    if (input_pixfmt == PIX_FMT_ARGB32)
        dump32bit(image);
    if (input_pixfmt == PIX_FMT_PAL8)
        dump8bit(image, (u_int32_t *) job->color_table);
#endif                          // DEBUG

    // FIXME: test if this is still necessary
    if (input_pixfmt == PIX_FMT_ARGB32 && c_info.alpha_mask == 0xFF000000
        && image->red_mask == 0xFF && image->green_mask == 0xFF00
        && image->blue_mask == 0xFF0000) {
        myABGR32toARGB32(image);
    } else if (input_pixfmt == PIX_FMT_PAL8) {
        myPAL8toRGB24(image, p_inpic, job);
    }
    // img resampling and conversion
    if (sws_scale(img_resample_ctx, p_inpic->data, p_inpic->linesize,
                  0, image->height, p_outpic->data,
                  p_outpic->linesize) < 0) {
        fprintf(stderr,
                _
                ("%s %s: error converting or resampling frame: context %p, iwidth %i, iheight %i, owidth %i, oheight %i, inpfmt %i opfmt %i\n"),
                DEBUGFILE, DEBUGFUNCTION, img_resample_ctx, image->width,
                image->height, out_st->codec->width, out_st->codec->height,
                input_pixfmt, out_st->codec->pix_fmt);
        exit(1);
    }

    /* 
     * encode the image 
     */
#ifdef DEBUG
    printf
        ("%s %s: calling encode_video with codec %p, outbuf %p, outbuf size %i, output frame %p\n",
         DEBUGFILE, DEBUGFUNCTION, out_st->codec, outbuf, outbuf_size,
         p_outpic);
#endif                          // DEBUG

    out_size =
        avcodec_encode_video(out_st->codec, outbuf, outbuf_size, p_outpic);
    if (out_size < 0) {
        fprintf(stderr,
                _
                ("%s %s: error encoding frame: c %p, outbuf %p, size %i, frame %p\n"),
                DEBUGFILE, DEBUGFUNCTION, out_st->codec, outbuf,
                outbuf_size, p_outpic);
        exit(1);
    }
#ifdef HAVE_FFMPEG_AUDIO
    if (job->flags & FLG_REC_SOUND) {
        if (pthread_mutex_lock(&mp) > 0) {
            fprintf(stderr,
                    _
                    ("mutex lock for writing video frame failed ... aborting\n"));
            exit(1);
        }
    }
#endif                          // HAVE_FFMPEG_AUDIO

    /* 
     * write frame to file 
     */
    if (out_size > 0) {
        // if ( job->target >= CAP_MF ) 
        do_video_out(output_file, out_st, outbuf, out_size);
        // else 
        // fwrite(outbuf, image_size, 1, fp);
    }

    if (job->target < CAP_MF)
        url_fclose(&output_file->pb);

#ifdef HAVE_FFMPEG_AUDIO
    /* 
     * release the mutex 
     */
    if (job->flags & FLG_REC_SOUND) {
        // printf("releasing the mutex lock\n");
        if (pthread_mutex_unlock(&mp) > 0) {
            fprintf(stderr,
                    _
                    ("couldn't release the mutex for writing video frame ... aborting\n"));
        }
    }
#endif                          // HAVE_FFMPEG_AUDIO

#undef DEBUGFUNCTION
}

/* 
 * KHB: FFMPEG cleanup
 */
void FFMPEGClean(Job * job)
{
#define DEBUGFUNCTION "FFMPEGClean()"

#ifdef DEBUG
    printf("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG

#ifdef HAVE_FFMPEG_AUDIO
    // 
    if (job->flags & FLG_REC_SOUND && tid > 0) {
        {
            int tret;

            tret = pthread_kill(tid, SIGUSR1);
            pthread_join(tid, NULL);
            tid = 0;
        }
    }
#endif                          // HAVE_FFMPEG_AUDIO

    if (output_file) {
        /* 
         * write trailer 
         */
        av_write_trailer(output_file);
    }

    if (output_file) {
        int i;
        /* 
         * close file if multi-frame capture ... otherwise closed already
         */
        if (job->target >= CAP_MF)
            url_fclose(&output_file->pb);
        /* 
         * free streams
         */
        for (i = 0; i < output_file->nb_streams; i++) {
            if (output_file->streams[i]->codec) {
                avcodec_close(output_file->streams[i]->codec);
                av_free(output_file->streams[i]->codec);
                output_file->streams[i]->codec = NULL;
            }
            av_free(output_file->streams[i]);
            if (out_st)
                out_st = NULL;
#ifdef HAVE_FFMPEG_AUDIO
            if (au_out_st)
                au_out_st = NULL;
#endif                          // HAVE_FFMPEG_AUDIO
        }
        /* 
         * free format context 
         */
        av_free(output_file->priv_data);
        output_file->priv_data = NULL;
        av_free(output_file);
        output_file = NULL;
    }

    if (img_resample_ctx) {
        sws_freeContext(img_resample_ctx);
        // img_resample_close(img_resample_ctx);
        img_resample_ctx = NULL;
    }

    if (outpic_buf) {
        av_free(outpic_buf);
        outpic_buf = NULL;
    }
    av_free(outbuf);            /* avcodec seems to do that job */
    outbuf = NULL;
    /* if (frame) { free(frame); frame = NULL; } */
    av_free(p_inpic);
    p_inpic = NULL;
    av_free(outpic_buf);
    outpic_buf = NULL;
    av_free(p_outpic);
    p_outpic = NULL;

    if (input_pixfmt == PIX_FMT_PAL8 && scratchbuf8bit) {
        free(scratchbuf8bit);
        scratchbuf8bit = NULL;
    }

    codec = NULL;
#ifdef HAVE_FFMPEG_AUDIO
    au_codec = NULL;
#endif                          // HAVE_FFMPEG_AUDIO

    // av_free_static();
#ifdef DEBUG
    printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG

#undef DEBUGFUNCTION
}


#ifdef DEBUG
// 
// dump info about XImage - for debugging purposes
// 
void x2ffmpeg_dump_ximage_info(XImage * img, FILE * fp)
{
#define DEBUGFUNCTION "x2ffmpeg_dump_ximage_info()"

#ifdef DEBUG
    printf("%s %s: Entering with image %p and file pointer %p\n",
           DEBUGFILE, DEBUGFUNCTION, img, fp);
#endif                          // DEBUG

    fprintf(fp, " width %d\n", img->width);
    fprintf(fp, " height %d\n", img->height);
    fprintf(fp, " xoffset %d\n", img->xoffset);
    fprintf(fp, " format %d\n", img->format);
    fprintf(fp, " data addr 0x%X\n", (int) img->data);
    fprintf(fp, " first four bytes of data 0x%X 0x%X 0x%X 0x%X\n",
            (unsigned char) (img->data[0]), (unsigned char) (img->data[1]),
            (unsigned char) img->data[2], (unsigned char) img->data[3]);
    fprintf(fp, " byte_order %s\n",
            img->byte_order ? "MSBFirst" : "LSBFirst");
    fprintf(fp, " bitmap_unit %d\n", img->bitmap_unit);
    fprintf(fp, " bitmap_bit_order %s\n",
            img->bitmap_bit_order ? "MSBFirst" : "LSBFirst");
    fprintf(fp, " bitmap_pad %d\n", img->bitmap_pad);
    fprintf(fp, " depth %d\n", img->depth);
    fprintf(fp, " bytes_per_line %d\n", img->bytes_per_line);
    fprintf(fp, " bits_per_pixel %d\n", img->bits_per_pixel);
    fprintf(fp, " red_mask 0x%.8lX\n", img->red_mask);
    fprintf(fp, " green_mask 0x%.8lX\n", img->green_mask);
    fprintf(fp, " blue_mask 0x%.8lX\n", img->blue_mask);

#ifdef DEBUG
    printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG

#undef DEBUGFUNCTION
}


// 
// dump 32bit image to pnm
// this is just used for debugging purposes
// 
void dump32bit(XImage * input)
{
#define DEBUGFUNCTION "dump32bit()"

    int row, col;
    static char head[256];

    static FILE *fp2 = NULL;
    uint8_t *ptr2, *output;
    long size;

    register unsigned int
        rm = input->red_mask,
        gm = input->green_mask,
        bm = input->blue_mask,
        rs = c_info.red_shift,
        gs = c_info.green_shift,
        bs = c_info.blue_shift, *p32 = (unsigned int *) input->data;

#ifdef DEBUG
    printf("%s %s: Entering with image %p\n",
           DEBUGFILE, DEBUGFUNCTION, input);
#endif                          // DEBUG

    sprintf(head, "P6\n%d %d\n%d\n", input->width, input->height, 255);
    size = ((input->bytes_per_line * input->height) / 4) * 3;
    output = malloc(size);
    ptr2 = output;

    for (row = 0; row < input->height; row++) {
        for (col = 0; col < input->width; col++) {
            *output++ = ((*p32 & rm) >> rs);
            *output++ = ((*p32 & gm) >> gs);
            *output++ = ((*p32 & bm) >> bs);
            p32++;              // ignore alpha values
        }
        // 
        // eat paded bytes, for better speed we use shifting,
        // (bytes_per_line - bits_per_pixel / 8 * width ) / 4 
        // 
        p32 += (input->bytes_per_line - (input->bits_per_pixel >> 3)
                * input->width) >> 2;
    }

    fp2 = fopen("/tmp/pic.rgb.pnm", "w");
    fwrite(head, strlen(head), 1, fp2);
    // 
    // x2ffmpeg_dump_ximage_info (input, fp2); 
    // 
    fwrite(ptr2, size, 1, fp2);
    fclose(fp2);
    free(ptr2);

#ifdef DEBUG
    printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG

#undef DEBUGFUNCTION
}


// 
// dump 8 bit image to ppm
// this is just used for debugging purposes
// 
void dump8bit(XImage * image, u_int32_t * ct)
{
#define DEBUGFUNCTION "dump8bit()"

    static char head[256];
    static unsigned int image_size;
    register unsigned char *line_ptr, *col_ptr;
    unsigned char *pnm_image = NULL;
    int row, col;

    static FILE *fp2 = NULL;

#ifdef DEBUG
    printf("%s %s: Entering with image %p\n",
           DEBUGFILE, DEBUGFUNCTION, image);
#endif                          // DEBUG

    sprintf(head, "P6\n%d %d\n%d\n", image->width, image->height, 255);
    image_size = image->width * 3 * image->height;  // RGB
    pnm_image = (unsigned char *) malloc(image_size);

    fp2 = fopen("/tmp/pic.rgb.pnm", "w");
    fwrite(head, strlen(head), 1, fp2);

    line_ptr = pnm_image;
    for (row = 0; row < image->height; row++) {
        col_ptr =
            (unsigned char *) image->data + (row * image->bytes_per_line);
        for (col = 0; col < image->width; col++) {
            *line_ptr++ = ((ct[*col_ptr] & 0x00FF0000) >> 16);
            *line_ptr++ = ((ct[*col_ptr] & 0x0000FF00) >> 8);
            *line_ptr++ = (ct[*col_ptr] & 0x000000FF);
            col_ptr++;
        }
    }
    fwrite(pnm_image, image_size, 1, fp2);

    // 
    // x2ffmpeg_dump_ximage_info (input, fp2); 
    // 
    fclose(fp2);
    free(pnm_image);

#ifdef DEBUG
    printf("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
#endif                          // DEBUG

#undef DEBUGFUNCTION
}

#endif                          // DEBUG



#endif                          // USE_FFMPEG


syntax highlighted by Code2HTML, v. 0.9.1