/////////////////////////////////////////////////////////////////////////////
// Name:        mediatrc_ffmpeg.cpp
// Purpose:     FFMPEG Media Transcoder
// Author:      Alex Thuering
// Created:     26.04.2008
// RCS-ID:      $Id: mediatrc_ffmpeg.cpp,v 1.38 2011/11/27 23:19:36 ntalex Exp $
// Copyright:   (c) Alex Thuering
// Licence:     GPL
/////////////////////////////////////////////////////////////////////////////

#include "mediatrc_ffmpeg.h"
#include <wx/wx.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <errno.h>
extern "C" {
#include <libavutil/opt.h>
#include <libavutil/avutil.h>
#include <libavutil/fifo.h>
#include <libavutil/dict.h>
#include <libavutil/pixdesc.h>
#include <libswscale/swscale.h>
#include <libavfilter/avcodec.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/avfiltergraph.h>
#include <libavfilter/vsink_buffer.h>
}

#define AUDIO_BUF_SIZE 50000
#define VIDEO_BUF_SIZE 1835008
#define QSCALE_NONE -99999
#define MAX_AUDIO_PACKET_SIZE (128 * 1024)
#define MAKE_SFMT_PAIR(a,b) ((a)+SAMPLE_FMT_NB*(b))

#ifndef FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
#endif

#ifndef INT64_MAX
#define INT64_MAX INT64_C(9223372036854775807)
#endif

#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0)
#define AVIO_FLAG_WRITE AVIO_WRONLY
#endif

//////////////////////////// AVAudioConvert /////////////////////////////////
// audioconvert.h
extern "C" {
struct AVAudioConvert;
AVAudioConvert *av_audio_convert_alloc(enum AVSampleFormat out_fmt, int out_channels, enum AVSampleFormat in_fmt,
		int in_channels, const float *matrix, int flags);
void av_audio_convert_free(AVAudioConvert *ctx);
int av_audio_convert(AVAudioConvert *ctx, void * const out[6], const int out_stride[6], const void * const in[6],
		const int in_stride[6], int len);
}

//////////////////////////// OutputStream /////////////////////////////////
struct InputStream;

typedef struct OutputStream {
	int file_index; /* file index */
	int index; /* stream index in the output file */
	int source_index; /* InputStream 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 InputStream *sync_ist; /* input stream to sync against */
	int64_t sync_opts; /* output frame counter, could be changed to some true timestamp *///FIXME look at frame_number
	AVBitStreamFilterContext *bitstream_filters;
	AVCodec *enc;
	
	/* video only */
	int video_resample;
	AVFrame resample_frame; /* temporary frame for image resampling */
	struct SwsContext *img_resample_ctx; /* for image resampling */
	int resample_height;
	int resample_width;
	int resample_pix_fmt;
    AVRational frame_rate;
	
	float frame_aspect_ratio;
	
	/* forced key frames */
	int64_t *forced_kf_pts;
	int forced_kf_count;
	int forced_kf_index;

	/* audio only */
	int audio_resample;
	ReSampleContext *resample; /* for audio resampling */
	AVSampleFormat resample_sample_fmt;
	int resample_channels;
	int resample_sample_rate;
	int reformat_pair;
	AVAudioConvert *reformat_ctx;
	AVFifoBuffer* fifo; /* for compression: one audio fifo per codec */
	FILE *logfile;
	
    AVFilterContext *output_video_filter;
    AVFilterContext *input_video_filter;
    AVFilterBufferRef *picref;
    char *avfilter;
    AVFilterGraph *graph;
    
    int sws_flags;
    AVDictionary *opts;
    int is_past_recording_time;
} OutputStream;

typedef struct InputStream {
	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' */
	AVCodec *dec;
	
	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 */
	double ts_scale;
	int is_start; /* is 1 at the start and after a discontinuity */
	int showed_multi_packet_warning;
	AVDictionary *opts;
} InputStream;

typedef struct InputFile {
	AVFormatContext *ctx;
	int eof_reached; /* true if eof reached */
	int ist_index; /* index of first stream in ist_table */
	int buffer_size; /* current total buffer size */
	int64_t ts_offset;
	int nb_streams;
} InputFile;

typedef struct OutputFile {
	AVFormatContext *ctx;
	AVDictionary *opts;
	int ost_index;       /* index of the first stream in output_streams */
	int64_t recording_time; /* desired length of the resulting file in microseconds */
} OutputFile;

static int configure_video_filters(InputStream *ist, OutputStream *ost)
{
    AVFilterContext *last_filter, *filter;
    /** filter graph containing all filters including input & output */
    AVCodecContext *codec = ost->st->codec;
    AVCodecContext *icodec = ist->st->codec;
    enum PixelFormat pix_fmts[] = { codec->pix_fmt, PIX_FMT_NONE };
    AVRational sample_aspect_ratio;
    char args[255];
    int ret;

    ost->graph = avfilter_graph_alloc();

    if (ist->st->sample_aspect_ratio.num){
        sample_aspect_ratio = ist->st->sample_aspect_ratio;
    }else
        sample_aspect_ratio = ist->st->codec->sample_aspect_ratio;

    snprintf(args, 255, "%d:%d:%d:%d:%d:%d:%d", ist->st->codec->width,
             ist->st->codec->height, ist->st->codec->pix_fmt, 1, AV_TIME_BASE,
             sample_aspect_ratio.num, sample_aspect_ratio.den);

    ret = avfilter_graph_create_filter(&ost->input_video_filter, avfilter_get_by_name("buffer"),
                                       "src", args, NULL, ost->graph);
    if (ret < 0)
        return ret;
    ret = avfilter_graph_create_filter(&ost->output_video_filter, avfilter_get_by_name("buffersink"),
                                       "out", NULL, pix_fmts, ost->graph);
    if (ret < 0)
        return ret;
    last_filter = ost->input_video_filter;

    if (codec->width  != icodec->width || codec->height != icodec->height) {
        snprintf(args, 255, "%d:%d:flags=0x%X",
                 codec->width,
                 codec->height,
                 ost->sws_flags);
        if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"),
                                                NULL, args, NULL, ost->graph)) < 0)
            return ret;
        if ((ret = avfilter_link(last_filter, 0, filter, 0)) < 0)
            return ret;
        last_filter = filter;
    }

    snprintf(args, sizeof(args), "flags=0x%X", ost->sws_flags);
    ost->graph->scale_sws_opts = av_strdup(args);

    if (ost->avfilter) {
        AVFilterInOut *outputs = avfilter_inout_alloc();
        AVFilterInOut *inputs  = avfilter_inout_alloc();

        outputs->name    = av_strdup("in");
        outputs->filter_ctx = last_filter;
        outputs->pad_idx = 0;
        outputs->next    = NULL;

        inputs->name    = av_strdup("out");
        inputs->filter_ctx = ost->output_video_filter;
        inputs->pad_idx = 0;
        inputs->next    = NULL;

        if ((ret = avfilter_graph_parse(ost->graph, ost->avfilter, &inputs, &outputs, NULL)) < 0)
            return ret;
        av_freep(&ost->avfilter);
    } else {
        if ((ret = avfilter_link(last_filter, 0, ost->output_video_filter, 0)) < 0)
            return ret;
    }

    if ((ret = avfilter_graph_config(ost->graph, NULL)) < 0)
        return ret;

    codec->width  = ost->output_video_filter->inputs[0]->w;
    codec->height = ost->output_video_filter->inputs[0]->h;
    codec->sample_aspect_ratio = ost->st->sample_aspect_ratio =
        ost->frame_aspect_ratio ? // overriden by the -aspect cli option
        av_d2q(ost->frame_aspect_ratio*codec->height/codec->width, 255) :
        ost->output_video_filter->inputs[0]->sample_aspect_ratio;

    return 0;
}

size_t ff_strlcpy(char *dst, const char *src, size_t size) {
	size_t len = 0;
	while (++len < size && *src)
		*dst++ = *src++;
	if (len <= size)
		*dst = 0;
	return len + strlen(src) - 1;
}

int ff_strstart(const char *str, const char *pfx, const char **ptr) {
	while (*pfx && *pfx == *str) {
		pfx++;
		str++;
	}
	if (!*pfx && ptr)
		*ptr = str;
	return !*pfx;
}

double psnr(double d) {
	return -10.0*log(d)/log(10.0);
}

void av_log_wxLog_callback(void* ptr, int level, const char* fmt, va_list vl) {
	if (level>av_log_get_level())
		return;
	static wxString buf;
	char s[1024];
	vsnprintf(s, 1024, fmt, vl);
	buf += wxString(s, wxConvLocal);
	if (buf.GetChar(buf.Length()-1) == wxT('\n')) {
		wxLogMessage(buf.SubString(0, buf.Length()-2));
		buf = wxT("");
	}
}

wxFfmpegMediaTranscoder::wxFfmpegMediaTranscoder(int threadCount) {
	ts_scale = NULL;
	nb_ts_scale = 0;
	stream_maps = NULL;
	nb_stream_maps = 0;
	meta_data_maps = NULL;
	nb_meta_data_maps = 0;
	metadata_global_autocopy = 0;
	metadata_streams_autocopy = 0;
	metadata_chapters_autocopy = 0;
	chapter_maps = NULL;
	nb_chapter_maps = 0;
	chapters_input_file = INT_MAX;
	streamid_map = NULL;
	nb_streamid_map = 0;
	frame_width = 0;
	frame_height = 0;
	frame_aspect_ratio = 0;
	frame_pix_fmt = PIX_FMT_NONE;
	frame_bits_per_raw_sample = 0;
	audio_sample_fmt = SAMPLE_FMT_NONE;
	max_frames[0] = INT_MAX;
	max_frames[1] = INT_MAX;
	max_frames[2] = INT_MAX;
	max_frames[3] = INT_MAX;
	frame_rate = (AVRational) {0,0};
	video_qscale = 0;
	intra_matrix = NULL;
	inter_matrix = NULL;
	video_rc_override_string = NULL;
	video_codec_tag = 0;
	video_language = NULL; 
	same_quant = 0;
	do_interlace = 0;
	do_deinterlace = 0;
	top_field_first = -1;
	me_threshold = 0;
	intra_dc_precision = 8;
	loop_input = 0;
	loop_output = AVFMT_NOOUTPUTLOOP;
	qp_hist = 0;
	
	intra_only = 0;
	audio_sample_rate = 44100;
	audio_qscale = QSCALE_NONE;
	audio_channels = 1;
	audio_codec_tag = 0;
	audio_language = NULL;
	
	subtitle_language = NULL;
	subtitle_codec_tag = 0;
	
	mux_preload= 0.5;
	mux_max_delay= 0.7;
	
	recording_time = INT64_MAX;
	start_time = 0;
	input_ts_offset = 0;
	metadata = NULL;
	do_psnr = 0;
	do_pass = 0;
	pass_logfilename_prefix = "ffmpeg2pass";
	video_sync_method = -1;
	audio_sync_method = 0;
	audio_drift_threshold = 0.1;
	copy_ts = 0;
	copy_tb = 0;
	opt_shortest = 0;
	vstats_filename = NULL;
	vstats_file = NULL;
	opt_programid = 0;
	copy_initial_nonkeyframes = 0;
	
	rate_emu = 0;
	
	exit_on_error = 0;
	verbose = 1;
	thread_count = threadCount;
	q_pressed = 0;
	video_size = 0;
	audio_size = 0;
	extra_size = 0;
	nb_frames_dup = 0;
	nb_frames_drop = 0;
	input_sync = 0;
	limit_filesize = 0;
	force_fps = 0;
	
	pgmyuv_compatibility_hack = 0;
	dts_delta_threshold = 10;

	audio_buf = NULL;
	audio_out = NULL;
	allocated_audio_out_size = allocated_audio_buf_size = 0;
	samples = NULL;
	bit_buffer_size = 1024 * 256;
	bit_buffer = NULL;
	
	video_bitstream_filters = NULL;
	audio_bitstream_filters = NULL;
	subtitle_bitstream_filters = NULL;
	
	input_streams = NULL;
	nb_input_streams = 0;
	input_files = NULL;
	nb_input_files = 0;
	
	output_streams = NULL;
	nb_output_streams = 0;
	output_files = NULL;
	nb_output_files = 0;
	
	format_opts = NULL;
	codec_opts = NULL;
	
	av_log_set_callback(&av_log_wxLog_callback);
}

wxFfmpegMediaTranscoder::~wxFfmpegMediaTranscoder() {
	End();
	av_log_set_callback(&av_log_default_callback);
}

void wxFfmpegMediaTranscoder::Init() {
	avfilter_register_all();
}

// similar to ff_dynarray_add() and av_fast_realloc()
void* grow_array(void *array, int elem_size, int *size, int new_size) {
    if (new_size >= INT_MAX / elem_size) {
        wxLogError(wxT("Array too big."));
        return NULL;
    }
    if (*size < new_size) {
        uint8_t* tmp = (uint8_t*) av_realloc(array, new_size*elem_size);
        if (!tmp) {
        	wxLogError(wxT("Could not alloc buffer."));
            return NULL;
        }
        memset(tmp + *size*elem_size, 0, (new_size-*size) * elem_size);
        *size = new_size;
        return tmp;
    }
    return array;
}

/**
 * Add all the streams from the given input file to the global
 * list of input streams.
 */
bool wxFfmpegMediaTranscoder::AddInputStreams(AVFormatContext *ic, bool disableVideo, bool disableAudio, bool disableSubtitle) {
    int i, rfps, rfps_base;

    /* update the current parameters so that they match the one of the input stream */
	for (i=0; i<(int)ic->nb_streams; i++) {
		AVStream *st = ic->streams[i];
		AVCodecContext *dec = st->codec;
		//AVDictionaryEntry *e = NULL;
		InputStream *ist;
		char *scale = NULL;
		
		dec->thread_count = thread_count;

		input_streams = (InputStream*) grow_array(input_streams, sizeof(*input_streams), &nb_input_streams, nb_input_streams + 1);
		ist = &input_streams[nb_input_streams - 1];
		ist->st = st;
		ist->file_index = nb_input_files;
		ist->discard = 1;
		
//		while (e = av_dict_get(ts_scale, "", e, AV_DICT_IGNORE_SUFFIX)) {
//			char *p = strchr(e->key, ':');
//			
//			if ((ret = check_stream_specifier(ic, st, p ? p + 1 : "")) > 0)
//				scale = e->value;
//			else if (ret < 0)
//				exit_program(1);
//		}
		if (scale)
			ist->ts_scale = strtod(scale, NULL);
		
		switch (dec->codec_type) {
		case AVMEDIA_TYPE_AUDIO:
			ist->dec = avcodec_find_decoder(dec->codec_id);
			if (disableAudio)
				st->discard = AVDISCARD_ALL;
			break;
		case AVMEDIA_TYPE_VIDEO:
			ist->dec = avcodec_find_decoder(dec->codec_id);
			if(st->sample_aspect_ratio.num)
				frame_aspect_ratio=av_q2d(st->sample_aspect_ratio);
			else
				frame_aspect_ratio=av_q2d(dec->sample_aspect_ratio);
			frame_aspect_ratio *= (float) dec->width / dec->height;
			rfps = st->r_frame_rate.num;
			rfps_base = st->r_frame_rate.den;
			if (dec->lowres) {
				dec->flags |= CODEC_FLAG_EMU_EDGE;
			}
			if (me_threshold)
				dec->debug |= FF_DEBUG_MV;

			if (dec->time_base.den != rfps*dec->ticks_per_frame || dec->time_base.num != rfps_base) {

				if (verbose >= 0)
					wxLogMessage(wxT("\nSeems stream %d codec frame rate differs from container frame rate: %2.2f (%d/%d) -> %2.2f (%d/%d)"),
					i, (float)dec->time_base.den / dec->time_base.num, dec->time_base.den, dec->time_base.num,
					(float)rfps / rfps_base, rfps, rfps_base);
			}

			if (disableVideo)
				st->discard = AVDISCARD_ALL;
			break;
		case AVMEDIA_TYPE_DATA:
			break;
		case AVMEDIA_TYPE_SUBTITLE:
			ist->dec = avcodec_find_decoder(dec->codec_id);
			if (disableSubtitle)
				st->discard = AVDISCARD_ALL;
			break;
		case AVMEDIA_TYPE_ATTACHMENT:
		case AVMEDIA_TYPE_UNKNOWN:
			break;
		default:
			wxLogError(_("Unknown codec type"));
			return false;
		}
	}
    return true;
}


bool wxFfmpegMediaTranscoder::AddInputFile(const wxString& fileName, bool disableVideo, bool disableAudio,
		bool disableSubtitle) {
	AVFormatContext *ic;
	int err, ret;
	int64_t timestamp;
	
	/* get default parameters from command line */
	ic = avformat_alloc_context();
	if (!ic) {
		PrintError(fileName, AVERROR(ENOMEM));
		return false;
	}
	ic->flags |= AVFMT_FLAG_NONBLOCK;
	
	// open the input file with generic libav function
	err = avformat_open_input(&ic, (const char*) fileName.mb_str(), NULL, &format_opts);
	if (err < 0) {
		PrintError(fileName, err);
		return false;
	}
	
	if (opt_programid) {
		unsigned int i, j;
		int found=0;
		for(i=0; i<ic->nb_streams; i++){
			ic->streams[i]->discard= AVDISCARD_ALL;
		}
		for(i=0; i<ic->nb_programs; i++){
			AVProgram *p= ic->programs[i];
			if(p->id != opt_programid){
				p->discard = AVDISCARD_ALL;
			}else{
				found=1;
				for(j=0; j<p->nb_stream_indexes; j++){
					ic->streams[p->stream_index[j]]->discard= AVDISCARD_DEFAULT;
				}
			}
		}
		if(!found){
			wxLogError(wxT("Specified program id not found"));
			return false;
		}
		opt_programid = 0;
	}

	ic->loop_input = loop_input;

	/* 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 && verbose >= 0) {
		wxLogError(wxT("%s: could not find codec parameters"), fileName.c_str());
		return false;
	}

	timestamp = start_time;
	/* add the stream start time */
	if (ic->start_time != (int64_t) AV_NOPTS_VALUE)
		timestamp += ic->start_time;

	/* if seeking requested, we execute it */
	if (start_time != 0) {
		ret = av_seek_frame(ic, -1, timestamp, AVSEEK_FLAG_BACKWARD);
		if (ret < 0) {
			wxLogError(wxT("%s: could not seek to position %0.3f"),
			fileName.c_str(), (double)timestamp / AV_TIME_BASE);
		}
		/* reset seek info */
		start_time = 0;
	}

	if (!AddInputStreams(ic, disableVideo, disableAudio, disableSubtitle))
		return false;

	/* dump the file content */
	if (verbose >= 0)
		av_dump_format(ic, nb_input_files, (const char*)fileName.mb_str(), 0);

	input_files = (InputFile*) grow_array(input_files, sizeof(*input_files), &nb_input_files, nb_input_files + 1);
	input_files[nb_input_files - 1].ctx        = ic;
	input_files[nb_input_files - 1].ist_index  = nb_input_streams - ic->nb_streams;
	input_files[nb_input_files - 1].nb_streams = ic->nb_streams;
	input_files[nb_input_files - 1].ts_offset = input_ts_offset - (copy_ts ? 0 : timestamp);
	
	top_field_first = -1;
    frame_rate    = (AVRational){0, 0};
    frame_pix_fmt = PIX_FMT_NONE;
    frame_height = 0;
    frame_width  = 0;
    audio_sample_rate = 0;
    audio_channels    = 0;
    audio_sample_fmt  = AV_SAMPLE_FMT_NONE;
    av_dict_free(&ts_scale);
	return true;
}

bool wxFfmpegMediaTranscoder::SetOutputFile(const wxString& fileName,
		VideoFormat videoFormat, AudioFormat audioFormat, SubtitleFormat subtitleFormat,
		int videoBitrate, bool vbr, int audioBitrate) {
	wxArrayInt audioFormats;
	if (audioFormat != afNONE)
		audioFormats.Add(audioFormat);
	wxArrayInt subtitleFormats;
	subtitleFormats.Add(subtitleFormat);
	return SetOutputFile(fileName, videoFormat, audioFormats, subtitleFormats,
			videoBitrate);
}

bool wxFfmpegMediaTranscoder::SetOutputFile(const wxString& fileName,
		VideoFormat videoFormat, wxArrayInt audioFormats, wxArrayInt subtitleFormats,
		int videoBitrate, bool vbr, int audioBitrate) {
	AVOutputFormat *outputFormat = NULL;
	if (audioFormats.GetCount() == 0)
		outputFormat = av_guess_format("mpeg2video", NULL, NULL);
	else if (videoFormat == vfNONE)
		outputFormat = av_guess_format("mp2", NULL, NULL);
	else
		outputFormat = av_guess_format("dvd", NULL, NULL);
	if (!outputFormat) {
		wxLogError(wxT("Error setting output format to DVD."));
		return false;
	}

	AVFormatContext *oc = avformat_alloc_context();

	oc->oformat = outputFormat;
	ff_strlcpy(oc->filename, (const char*) fileName.mb_str(),
			sizeof(oc->filename));

	// disable if no corresponding type found and at least one input file
	if (nb_input_files == 0) {
		wxLogError(wxT("Please add input files"));
		return false;
	}
	// add video, audio and subtitle streams
	if (videoFormat != vfNONE && !AddVideoStream(oc, videoFormat, videoBitrate, vbr))
		return false;
	for (unsigned int afIdx = 0; afIdx < audioFormats.GetCount(); afIdx++)
		if (audioFormats[afIdx] != afNONE
				&& !AddAudioStream(oc, (AudioFormat) audioFormats[afIdx], audioBitrate))
			return false;
	for (unsigned int sfIdx = 0; sfIdx < subtitleFormats.GetCount(); sfIdx++)
		if (subtitleFormats[sfIdx] != sfNONE
				&& !AddSubtitleStream(oc, (SubtitleFormat) subtitleFormats[sfIdx]))
			return false;
	
	av_dict_copy(&oc->metadata, metadata, 0);
	av_dict_free(&metadata);
	
	output_files = (OutputFile*) grow_array(output_files, sizeof(*output_files), &nb_output_files, nb_output_files + 1);
	output_files[nb_output_files - 1].ctx = oc;
	output_files[nb_output_files - 1].ost_index = nb_output_streams - oc->nb_streams;
	output_files[nb_output_files - 1].recording_time = recording_time;
	av_dict_copy(&output_files[nb_output_files - 1].opts, format_opts, 0);

	/* check filename in case of an image number is expected */
	if (oc->oformat->flags & AVFMT_NEEDNUMBER) {
		if (!av_filename_number_test(oc->filename)) {
			PrintError(fileName, AVERROR(EINVAL));
			return false;
		}
	}

	/* test if it already exists to avoid loosing precious files */
	if (avio_check((const char*) fileName.mb_str(), 0) == 0) {
		wxLogError(wxT("File '%s' already exists"), fileName.c_str());
		return false;
	}

	/* open the file */
	int err = avio_open(&oc->pb, (const char*) fileName.mb_str(), AVIO_FLAG_WRITE);
	if (err < 0) {
		PrintError(fileName, err);
		return false;
	}
	
	oc->preload= (int)(mux_preload*AV_TIME_BASE);
	oc->max_delay= (int)(mux_max_delay*AV_TIME_BASE);
	oc->flags |= AVFMT_FLAG_NONBLOCK;

	/* copy chapters */
	if (chapters_input_file >= nb_input_files) {
		if (chapters_input_file == INT_MAX) {
			/* copy chapters from the first input file that has them*/
			chapters_input_file = -1;
			for (int i = 0; i < nb_input_files; i++)
				if (input_files[i].ctx->nb_chapters) {
					chapters_input_file = i;
					break;
				}
		} else {
			av_log(NULL, AV_LOG_ERROR, "Invalid input file index %d in chapter mapping.\n", chapters_input_file);
			return false;
		}
	}
	if (chapters_input_file >= 0)
		CopyChapters(chapters_input_file, nb_output_files - 1);
	
	/* copy metadata */
	for (int i = 0; i < nb_meta_data_maps; i++) {
		AVFormatContext *files[2];
		AVDictionary    **meta[2];
		int j;

#define METADATA_CHECK_INDEX(index, nb_elems, desc)\
		if ((index) < 0 || (index) >= (nb_elems)) {\
			av_log(NULL, AV_LOG_ERROR, "Invalid %s index %d while processing metadata maps\n",\
					 (desc), (index));\
			return false;\
		}

		int in_file_index = meta_data_maps[i][1].file;
		if (in_file_index < 0)
			continue;
		METADATA_CHECK_INDEX(in_file_index, nb_input_files, "input file")

		files[0] = oc;
		files[1] = input_files[in_file_index].ctx;

		for (j = 0; j < 2; j++) {
			MetadataMap *map = &meta_data_maps[i][j];

			switch (map->type) {
			case 'g':
				meta[j] = &files[j]->metadata;
				break;
			case 's':
				METADATA_CHECK_INDEX(map->index, files[j]->nb_streams, "stream")
				meta[j] = &files[j]->streams[map->index]->metadata;
				break;
			case 'c':
				METADATA_CHECK_INDEX(map->index, files[j]->nb_chapters, "chapter")
				meta[j] = &files[j]->chapters[map->index]->metadata;
				break;
			case 'p':
				METADATA_CHECK_INDEX(map->index, files[j]->nb_programs, "program")
				meta[j] = &files[j]->programs[map->index]->metadata;
				break;
			}
		}

		av_dict_copy(meta[0], *meta[1], AV_DICT_DONT_OVERWRITE);
	}

	/* copy global metadata by default */
	if (metadata_global_autocopy && nb_input_files)
		av_dict_copy(&oc->metadata, input_files[0].ctx->metadata,
					 AV_DICT_DONT_OVERWRITE);
	if (metadata_streams_autocopy)
		for (int i = output_files[nb_output_files - 1].ost_index; i < nb_output_streams; i++) {
			InputStream *ist = &input_streams[output_streams[i].source_index];
			av_dict_copy(&output_streams[i].st->metadata, ist->st->metadata, AV_DICT_DONT_OVERWRITE);
		}
	
	frame_rate    = (AVRational){0, 0};
	frame_width   = 0;
	frame_height  = 0;
	audio_sample_rate = 0;
	audio_channels    = 0;
	audio_sample_fmt  = AV_SAMPLE_FMT_NONE;
	chapters_input_file = INT_MAX;
	recording_time = INT64_MAX;
	
	av_freep(&meta_data_maps);
	nb_meta_data_maps = 0;
	metadata_global_autocopy   = 1;
	metadata_streams_autocopy  = 1;
	metadata_chapters_autocopy = 1;
	av_freep(&stream_maps);
	nb_stream_maps = 0;
	
	return true;
}

void wxFfmpegMediaTranscoder::PrintError(const wxString& msg, int err) {
	char errbuf[128];
	const char *errbuf_ptr = errbuf;
	
	if (av_strerror(err, errbuf, sizeof(errbuf)) < 0)
		errbuf_ptr = strerror(AVUNERROR(err));
	wxString errorStr(errbuf_ptr, wxConvUTF8);
	wxLogError(msg + wxT(": ") + errorStr);
}

void choose_pixel_fmt(AVStream *st, AVCodec *codec) {
	if(codec && codec->pix_fmts){
		const enum PixelFormat *p= codec->pix_fmts;
		for(; *p!=-1; p++){
			if(*p == st->codec->pix_fmt)
				break;
		}
		if (*p == -1) {
			if(st->codec->pix_fmt != PIX_FMT_NONE)
			av_log(NULL, AV_LOG_WARNING,
				   "Incompatible pixel format '%s' for codec '%s', auto-selecting format '%s'\n",
				   av_pix_fmt_descriptors[st->codec->pix_fmt].name,
				   codec->name,
				   av_pix_fmt_descriptors[codec->pix_fmts[0]].name);
			st->codec->pix_fmt = codec->pix_fmts[0];
		}
	}
}

/**
 * Update the requested input sample format based on the output sample format.
 * This is currently only used to request float output from decoders which
 * support multiple sample formats, one of which is AV_SAMPLE_FMT_FLT.
 * Ideally this will be removed in the future when decoders do not do format
 * conversion and only output in their native format.
 */
static void update_sample_fmt(AVCodecContext *dec, AVCodec *dec_codec, AVCodecContext *enc) {
    /* if sample formats match or a decoder sample format has already been
       requested, just return */
    if (enc->sample_fmt == dec->sample_fmt ||
        dec->request_sample_fmt > AV_SAMPLE_FMT_NONE)
        return;

    /* if decoder supports more than one output format */
    if (dec_codec && dec_codec->sample_fmts &&
        dec_codec->sample_fmts[0] != AV_SAMPLE_FMT_NONE &&
        dec_codec->sample_fmts[1] != AV_SAMPLE_FMT_NONE) {
        const enum AVSampleFormat *p;
        int min_dec = -1, min_inc = -1;

        /* find a matching sample format in the encoder */
        for (p = dec_codec->sample_fmts; *p != AV_SAMPLE_FMT_NONE; p++) {
            if (*p == enc->sample_fmt) {
                dec->request_sample_fmt = *p;
                return;
            } else if (*p > enc->sample_fmt) {
                min_inc = FFMIN(min_inc, *p - enc->sample_fmt);
            } else
                min_dec = FFMIN(min_dec, enc->sample_fmt - *p);
        }

        /* if none match, provide the one that matches quality closest */
        dec->request_sample_fmt = (AVSampleFormat) (min_inc > 0 ? enc->sample_fmt + min_inc : enc->sample_fmt - min_dec);
    }
}

void choose_sample_fmt(AVStream *st, AVCodec *codec) {
	if (codec && codec->sample_fmts){
		const enum SampleFormat *p= codec->sample_fmts;
		for (; *p!=-1; p++){
			if(*p == st->codec->sample_fmt)
				break;
		}
		if (*p == -1) {
			if ((codec->capabilities & CODEC_CAP_LOSSLESS)
					&& av_get_sample_fmt_name(st->codec->sample_fmt) > av_get_sample_fmt_name(codec->sample_fmts[0]))
				av_log(NULL, AV_LOG_ERROR, "Convertion will not be lossless'\n");
			if (av_get_sample_fmt_name(st->codec->sample_fmt))
			av_log(NULL, AV_LOG_WARNING,
				   "Incompatible sample format '%s' for codec '%s', auto-selecting format '%s'\n",
				   av_get_sample_fmt_name(st->codec->sample_fmt),
				   codec->name,
				   av_get_sample_fmt_name(codec->sample_fmts[0]));
			st->codec->sample_fmt = codec->sample_fmts[0];
		}
	}
}

void choose_sample_rate(AVStream *st, AVCodec *codec) {
	if (codec && codec->supported_samplerates){
		const int *p= codec->supported_samplerates;
		int best = 0;
		int best_dist=INT_MAX;
		for (; *p; p++){
			int dist= abs(st->codec->sample_rate - *p);
			if(dist < best_dist){
				best_dist= dist;
				best= *p;
			}
		}
		if (best_dist){
			av_log(st->codec, AV_LOG_WARNING, "Requested sampling rate unsupported using closest supported (%d)\n", best);
		}
		st->codec->sample_rate= best;
	}
}

AVCodec* choose_codec(AVFormatContext *s, AVStream *st, AVMediaType type, int format) {
	switch (type) {
	case AVMEDIA_TYPE_VIDEO: {
		VideoFormat videoFormat = (VideoFormat) format;
		if (videoFormat == vfCOPY) {
			st->stream_copy = 1;
			return NULL;
		}
		st->codec->codec_id = CODEC_ID_MPEG2VIDEO;
		break;
	}
	case AVMEDIA_TYPE_AUDIO: {
		AudioFormat audioFormat = (AudioFormat) format;
		if (audioFormat == afCOPY) {
			st->stream_copy = 1;
			return NULL;
		}
		st->codec->codec_id = audioFormat == afMP2 ? CODEC_ID_MP2 : CODEC_ID_AC3;
		break;
	}
	case AVMEDIA_TYPE_SUBTITLE: {
		SubtitleFormat subtitleFormat = (SubtitleFormat) format;
		if (subtitleFormat == sfCOPY) {
			st->stream_copy = 1;
			return NULL;
		}
		st->codec->codec_id = CODEC_ID_DVD_SUBTITLE;
		break;
	}
	default:
		return NULL;
	}
	
	return avcodec_find_encoder(st->codec->codec_id);
}

OutputStream* wxFfmpegMediaTranscoder::NewOutputStream(AVFormatContext *oc, AVMediaType type, int format) {
	OutputStream* ost;
	AVStream *st = av_new_stream(oc, oc->nb_streams < nb_streamid_map ? streamid_map[oc->nb_streams] : 0);
    int idx = oc->nb_streams - 1;
	
	if (!st) {
		av_log(NULL, AV_LOG_ERROR, "Could not alloc stream.\n");
		return NULL;
	}
	
	output_streams = (OutputStream*) grow_array(
			output_streams, sizeof(*output_streams), &nb_output_streams, nb_output_streams + 1);
	ost = &output_streams[nb_output_streams - 1];
	ost->file_index = nb_output_files;
	ost->index = idx;
	ost->st = st;
	st->codec->codec_type = type;
	ost->enc = choose_codec(oc, st, type, format);
//	if (ost->enc)
//		ost->opts = filter_codec_opts(codec_opts, codec->id, 1);
	
	avcodec_get_context_defaults3(st->codec, ost->enc);
	st->codec->codec_type = type; // XXX hack, avcodec_get_context_defaults2() sets type to unknown for stream copy
	
	ost->sws_flags = SWS_BICUBIC;
	return ost;
}

bool wxFfmpegMediaTranscoder::AddVideoStream(AVFormatContext *oc, VideoFormat videoFormat,
		int bitrate, bool vbr) {
	AVStream *st;
	OutputStream *ost;
	AVCodecContext *video_enc;
	
	ost = NewOutputStream(oc, AVMEDIA_TYPE_VIDEO, (int) videoFormat);
	st = ost->st;
	if (videoFormat != vfCOPY) {
		wxSize frameSize = GetFrameSize(videoFormat);
		frame_width = frameSize.GetWidth();
		frame_height = frameSize.GetHeight();
		
		if (!isNTSC(videoFormat)) {
			frame_rate.num = 25;
			frame_rate.den = 1;
		} else if ((frame_rate.num != 24000 || frame_rate.num != 30000) && frame_rate.den != 1001) {
			double fr = frame_rate.num > 0 && frame_rate.den > 0 ? ((double) frame_rate.num)/frame_rate.den : 29.97;
			frame_rate.num = fr > 26 ? 30000 : 24000;
			frame_rate.den = 1001;
		}
		
		char* vfilters = NULL;
		if (m_videoFilters.length()) {
			vfilters = (char*) av_realloc(vfilters, m_videoFilters.length() + 1);
			snprintf(vfilters, m_videoFilters.length() + 1, "%s", (const char*) m_videoFilters.mb_str());
		}
        ost->avfilter = vfilters;
	}
	
	ost->bitstream_filters = video_bitstream_filters;
	video_bitstream_filters = NULL;

	st->codec->thread_count = thread_count;

	video_enc = st->codec;
	
	if (video_codec_tag)
		video_enc->codec_tag= video_codec_tag;
	
	if (oc->oformat->flags & AVFMT_GLOBALHEADER) {
		video_enc->flags |= CODEC_FLAG_GLOBAL_HEADER;
	}
	
	if (videoFormat == vfCOPY) {
		video_enc->sample_aspect_ratio = av_d2q(frame_aspect_ratio*frame_height/frame_width, 255); 
		st->sample_aspect_ratio = video_enc->sample_aspect_ratio;
	} else {
		const char *p;
		int i;
		
		av_set_int(video_enc, "gop", isNTSC(videoFormat) ? 15 : 12);
		av_set_int(video_enc, "b", bitrate * 1000);
		av_set_int(video_enc, "maxrate", vbr ? 9000000 : bitrate * 1000);
		av_set_int(video_enc, "minrate", vbr ? 0 : bitrate * 1000);
		av_set_int(video_enc, "bufsize", 1835008); //224*1024*8

		av_set_int(video_enc, "packetsize", 2048);
		av_set_int(video_enc, "muxrate", 10080000);
		
		if (frame_rate.num)
			ost->frame_rate = frame_rate;
		
		video_enc->width = frame_width;
		video_enc->height = frame_height;
		video_enc->sample_aspect_ratio = av_d2q(frame_aspect_ratio*frame_height/frame_width, 255);
		video_enc->pix_fmt = frame_pix_fmt;
		st->sample_aspect_ratio = video_enc->sample_aspect_ratio; 
		
		if (intra_only)
			video_enc->gop_size = 0;
		if (video_qscale || same_quant) {
			video_enc->flags |= CODEC_FLAG_QSCALE;
			st->quality = FF_QP2LAMBDA * video_qscale;
			video_enc->global_quality = (int) st->quality;
		}

		if (intra_matrix)
			video_enc->intra_matrix = intra_matrix;
		if (inter_matrix)
			video_enc->inter_matrix = inter_matrix;
		
		p = video_rc_override_string;
		for (i=0; p; i++) {
			int start, end, q;
			int e=sscanf(p, "%d,%d,%d", &start, &end, &q);
			if (e!=3) {
				wxLogError(wxT("error parsing rc_override"));
				return false;
			}
			video_enc->rc_override = (RcOverride*) av_realloc(
					video_enc->rc_override, sizeof(RcOverride)*(i+1));
			video_enc->rc_override[i].start_frame= start;
			video_enc->rc_override[i].end_frame = end;
			if (q>0) {
				video_enc->rc_override[i].qscale= q;
				video_enc->rc_override[i].quality_factor= 1.0;
			} else {
				video_enc->rc_override[i].qscale= 0;
				video_enc->rc_override[i].quality_factor= -q/100.0;
			}
			p= strchr(p, '/');
			if (p)
				p++;
		}
		video_enc->rc_override_count=i;
		if (!video_enc->rc_initial_buffer_occupancy)
			video_enc->rc_initial_buffer_occupancy = video_enc->rc_buffer_size*3/4;
		video_enc->me_threshold= me_threshold;
		video_enc->intra_dc_precision= intra_dc_precision - 8;

		if (do_psnr)
			video_enc->flags|= CODEC_FLAG_PSNR;
		
		if (do_interlace)
			video_enc->flags|= CODEC_FLAG_INTERLACED_DCT|CODEC_FLAG_INTERLACED_ME;

		/* two pass mode */
		if (do_pass) {
			if (do_pass == 1) {
				video_enc->flags |= CODEC_FLAG_PASS1;
			} else {
				video_enc->flags |= CODEC_FLAG_PASS2;
			}
		}
//		if (forced_key_frames)
//			parse_forced_key_frames(forced_key_frames, ost, video_enc);
	}
	if (video_language) {
		av_dict_set(&st->metadata, "language", video_language, 0);
		av_freep(&video_language);
	}
	
	/* reset some key parameters */
	frame_pix_fmt = PIX_FMT_NONE;
	return true;
}

bool wxFfmpegMediaTranscoder::AddAudioStream(AVFormatContext *oc, AudioFormat audioFormat, int bitrate) {
	AVStream *st;
	OutputStream *ost;
	AVCodecContext *audio_enc;
	
	ost = NewOutputStream(oc, AVMEDIA_TYPE_AUDIO, (int) audioFormat);
	st = ost->st;
	
	audio_sample_rate = 48000;
	
	ost->bitstream_filters = audio_bitstream_filters;
	audio_bitstream_filters= NULL;

	st->codec->thread_count = thread_count;

	audio_enc = st->codec;

	if (audio_codec_tag)
		audio_enc->codec_tag= audio_codec_tag;

	if (oc->oformat->flags & AVFMT_GLOBALHEADER) {
		audio_enc->flags |= CODEC_FLAG_GLOBAL_HEADER;
	}
	if (audioFormat != afCOPY) {
		av_set_int(audio_enc, "b:a", bitrate*1000);
		
		if (audio_qscale > QSCALE_NONE) {
			audio_enc->flags |= CODEC_FLAG_QSCALE;
			st->quality = FF_QP2LAMBDA * audio_qscale;
			audio_enc->global_quality = (int) st->quality;
		}
        if (audio_channels)
            audio_enc->channels = audio_channels;
		audio_enc->sample_fmt = audio_sample_fmt;
		if (audio_sample_fmt != AV_SAMPLE_FMT_NONE)
			audio_enc->sample_fmt = audio_sample_fmt;
		if (audio_sample_rate)
			audio_enc->sample_rate = audio_sample_rate;
	}
	if (audio_language) {
		av_dict_set(&st->metadata, "language", audio_language, 0);
		av_freep(&audio_language);
	}
	
	return true;
}

bool wxFfmpegMediaTranscoder::AddSubtitleStream(AVFormatContext *oc, SubtitleFormat subtitleFormat) {
	AVStream *st;
	OutputStream *ost;
	AVCodecContext *subtitle_enc;
	
	ost = NewOutputStream(oc, AVMEDIA_TYPE_SUBTITLE, (int) subtitleFormat);
	st = ost->st;
	subtitle_enc = st->codec;

	ost->bitstream_filters = subtitle_bitstream_filters;
	subtitle_bitstream_filters= NULL;

	if(subtitle_codec_tag)
		subtitle_enc->codec_tag= subtitle_codec_tag;
	
	if (subtitle_language) {
		av_dict_set(&st->metadata, "language", subtitle_language, 0);
		av_freep(&subtitle_language);
	}
	
	return true;
}

int read_file(const char *filename, char **bufptr, size_t *size)
{
	FILE *f = fopen(filename, "rb");

	if (!f) {
		wxLogError(wxT("Cannot read file '%s': %s"), filename, strerror(errno));
		return AVERROR(errno);
	}
	fseek(f, 0, SEEK_END);
	*size = ftell(f);
	fseek(f, 0, SEEK_SET);
	*bufptr = (char*) av_malloc(*size + 1);
	if (!*bufptr) {
		wxLogError(wxT("Could not allocate file buffer"));
		fclose(f);
		return AVERROR(ENOMEM);
	}
	*size = fread(*bufptr, 1, *size, f);
	(*bufptr)[*size++] = '\0';

	fclose(f);
	return 0;
}

/** Sets interlaced encoding flag */
void wxFfmpegMediaTranscoder::SetInterlaced(bool value) {
	do_interlace = value;
}

/** Sets field first flag (Auto, TFF, BFF) */
void wxFfmpegMediaTranscoder::SetFirstField(FirstField firstField) {
	top_field_first = firstField;
}

/** Sets start time */
void wxFfmpegMediaTranscoder::SetStartTime(double startTime) {
	start_time = (int64_t) (startTime * AV_TIME_BASE);
}

/** Sets recording time */
void wxFfmpegMediaTranscoder::SetRecordingTime(double recordingTime) {
	recording_time = recordingTime >= 0 ? (int64_t) (recordingTime * AV_TIME_BASE) : INT64_MAX;
}

int wxFfmpegMediaTranscoder::CopyChapters(int infile, int outfile) {
    AVFormatContext *is = input_files[infile].ctx;
    AVFormatContext *os = output_files[outfile].ctx;

    for (unsigned int i = 0; i < is->nb_chapters; i++) {
        AVChapter *in_ch = is->chapters[i], *out_ch;
        int64_t ts_off   = av_rescale_q(start_time - input_files[infile].ts_offset,
                                      AV_TIME_BASE_Q, in_ch->time_base);
        int64_t rt       = (recording_time == INT64_MAX) ? INT64_MAX :
                           av_rescale_q(recording_time, AV_TIME_BASE_Q, in_ch->time_base);


        if (in_ch->end < ts_off)
            continue;
        if (rt != INT64_MAX && in_ch->start > rt + ts_off)
            break;

        out_ch = (AVChapter*) av_mallocz(sizeof(AVChapter));
        if (!out_ch)
            return AVERROR(ENOMEM);

        out_ch->id        = in_ch->id;
        out_ch->time_base = in_ch->time_base;
        out_ch->start     = FFMAX(0,  in_ch->start - ts_off);
        out_ch->end       = FFMIN(rt, in_ch->end   - ts_off);

        if (metadata_chapters_autocopy)
        	av_dict_copy(&out_ch->metadata, in_ch->metadata, 0);

        os->nb_chapters++;
        os->chapters = (AVChapter**) av_realloc(os->chapters, sizeof(AVChapter)*os->nb_chapters);
        if (!os->chapters)
            return AVERROR(ENOMEM);
        os->chapters[os->nb_chapters - 1] = out_ch;
    }
    return 0;
}

/** Starts transcoding. */
bool wxFfmpegMediaTranscoder::Run(bool& canceled) {
	int ret = 0, i, j;
	AVFormatContext *is, *os;
	AVCodecContext *codec, *icodec;
	OutputStream *ost;
	InputStream *ist = NULL;
	wxString error;
	uint8_t *no_packet;
	int no_packet_count=0;
	int nb_frame_threshold[AVMEDIA_TYPE_NB]={0};
	int nb_streams[AVMEDIA_TYPE_NB]={0};
	
	if (canceled)
		return false;
	
	if (!(no_packet = (uint8_t*) av_mallocz(nb_input_files))) {
		wxLogError(wxT("Cannot allocate no_packet"));
		return false;
	}

	if (rate_emu)
		for (i = 0; i < nb_input_streams; i++)
			input_streams[i].start = av_gettime();
	
	/* output stream init */
	for (i=0; i<nb_output_files; i++) {
		os = output_files[i].ctx;
		if (!os->nb_streams) {
			av_dump_format(os, i, os->filename, 1);
			wxLogError(wxT("Output file #%d does not contain any stream"), i);
			return false;
		}
	}
	
	/* Sanity check the mapping args -- do the input files & streams exist? */
	for (i=0; i<nb_stream_maps; i++) {
		int fi = stream_maps[i].file_index;
		int si = stream_maps[i].stream_index;

		if (fi < 0 || fi > (int) nb_input_files - 1 ||
			si < 0 || si > (int) input_files[fi].nb_streams - 1) {
			wxLogError(wxT("Could not find input stream #%d.%d"), fi, si);
			return false;
		}
		fi = stream_maps[i].sync_file_index;
		si = stream_maps[i].sync_stream_index;
		if (fi < 0 || fi> nb_input_files - 1 ||
				si < 0 || si > (int) input_files[fi].ctx->nb_streams - 1) {
			wxLogError(wxT("Could not find sync stream #%d.%d"), fi, si);
			return false;
		}
	}
	
	for(int k=0;k<nb_output_files;k++) {
		os = output_files[k].ctx;
		for (i=0; i < (int) os->nb_streams; i++) {
			nb_streams[os->streams[i]->codec->codec_type]++;
		}
	}
	for(int step=1<<30; step; step>>=1){
		int found_streams[AVMEDIA_TYPE_NB]={0};
		for(j=0; j<AVMEDIA_TYPE_NB; j++)
			nb_frame_threshold[j] += step;

		for(j=0; j<nb_input_streams; j++) {
			int skip=0;
			ist = &input_streams[j];
			if(opt_programid){
				AVFormatContext *f= input_files[ist->file_index].ctx;
				skip=1;
				for (unsigned int pi=0; pi<f->nb_programs; pi++) {
					AVProgram *p= f->programs[pi];
					if(p->id == opt_programid)
						for (unsigned int si=0; si<p->nb_stream_indexes; si++) {
							if(f->streams[ p->stream_index[si] ] == ist->st)
								skip=0;
						}
				}
			}
			if (ist->discard && ist->st->discard != AVDISCARD_ALL && !skip
				&& nb_frame_threshold[ist->st->codec->codec_type] <= ist->st->codec_info_nb_frames){
				found_streams[ist->st->codec->codec_type]++;
			}
		}
		for(j=0; j<AVMEDIA_TYPE_NB; j++)
			if(found_streams[j] < nb_streams[j])
				nb_frame_threshold[j] -= step;
	}
	int n = 0;
	for (i=0; i<nb_output_streams; i++) {
		ost = &output_streams[i];
		int found;
		if (nb_stream_maps > 0) {
			ost->source_index = input_files[stream_maps[n].file_index].ist_index +
				stream_maps[n].stream_index;

			/* Sanity check that the stream types match */
			if (input_streams[ost->source_index].st->codec->codec_type
					!= ost->st->codec->codec_type) {
				wxLogError(wxT("Codec type mismatch for mapping #%d.%d -> #%d.%d"),
					stream_maps[n].file_index, stream_maps[n].stream_index,
					ost->file_index, ost->index);
				return false;
			}

		} else {
			/* get corresponding input stream index : we select the first one with the right type */
			found = 0;
			for (j=0; j < nb_input_streams; j++) {
				int skip=0;
				ist = &input_streams[j];
				if(opt_programid){
					unsigned int pi,si;
					AVFormatContext *f = input_files[ist->file_index].ctx;
					skip=1;
					for(pi=0; pi<f->nb_programs; pi++){
						AVProgram *p= f->programs[pi];
						if(p->id == opt_programid)
							for(si=0; si<p->nb_stream_indexes; si++){
								if(f->streams[ p->stream_index[si] ] == ist->st)
									skip=0;
							}
					}
				}
				if (ist->discard && ist->st->discard != AVDISCARD_ALL && !skip &&
						ist->st->codec->codec_type == ost->st->codec->codec_type &&
						nb_frame_threshold[ist->st->codec->codec_type] <= ist->st->codec_info_nb_frames) {
					ost->source_index = j;
					found = 1;
					break;
				}
			}
			if (!found) {
				if(! opt_programid) {
					/* try again and reuse existing stream */
					for (j = 0; j < nb_input_streams; j++) {
						ist = &input_streams[j];
						if (   ist->st->codec->codec_type == ost->st->codec->codec_type
							&& ist->st->discard != AVDISCARD_ALL) {
							ost->source_index = j;
							found = 1;
						}
					}
				}
				if (!found) {
					int i= ost->file_index;
					av_dump_format(output_files[i].ctx, i, output_files[i].ctx->filename, 1);
					wxLogError(wxT("Could not find input stream matching output stream #%d.%d"),
						ost->file_index, ost->index);
					return false;
				}
			}
		}
		ist = &input_streams[ost->source_index];
		ist->discard = 0;
		ost->sync_ist = (nb_stream_maps > 0) ?
				&input_streams[input_files[stream_maps[n].sync_file_index].ist_index +
				stream_maps[n].sync_stream_index] : ist;
	}
	
	/* for each output stream, we compute the right encoding parameters */
	for (i=0; i<nb_output_streams; i++) {
		ost = &output_streams[i];
		os = output_files[ost->file_index].ctx;
		ist = &input_streams[ost->source_index];

		codec = ost->st->codec;
		icodec = ist->st->codec;
		
		ost->st->disposition = ist->st->disposition;
		codec->bits_per_raw_sample= icodec->bits_per_raw_sample;
		codec->chroma_sample_location = icodec->chroma_sample_location;

		if (ost->st->stream_copy) {
			uint64_t extra_size = (uint64_t)icodec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE;
			
			if (extra_size > INT_MAX) {
				wxLogError(wxT("Invalid extra_size"));
				ret = AVERROR(ENOMEM);
				goto fail;
			}
			
			/* if stream_copy is selected, no need to decode or encode */
			codec->codec_id = icodec->codec_id;
			codec->codec_type = icodec->codec_type;
			
			if (!codec->codec_tag) {
				if ( !os->oformat->codec_tag
						|| av_codec_get_id((const struct AVCodecTag **) os->oformat->codec_tag, icodec->codec_tag) == codec->codec_id
						|| av_codec_get_tag((const struct AVCodecTag **) os->oformat->codec_tag, icodec->codec_id) <= 0)
					codec->codec_tag = icodec->codec_tag;
			}

			codec->bit_rate = icodec->bit_rate;
			codec->rc_max_rate    = icodec->rc_max_rate;
			codec->rc_buffer_size = icodec->rc_buffer_size;
			codec->extradata= (uint8_t*) av_mallocz(extra_size);
			if (!codec->extradata) {
				wxLogError(wxT("Invalid extra_size"));
				ret = AVERROR(ENOMEM);
				goto fail;
			}
			memcpy(codec->extradata, icodec->extradata, icodec->extradata_size);
			codec->extradata_size= icodec->extradata_size;
			codec->time_base = ist->st->time_base;
			if(!copy_tb && av_q2d(icodec->time_base)*icodec->ticks_per_frame > av_q2d(ist->st->time_base) && av_q2d(ist->st->time_base) < 1.0/500){
				codec->time_base = icodec->time_base;
				codec->time_base.num *= icodec->ticks_per_frame;
				av_reduce(&codec->time_base.num, &codec->time_base.den, codec->time_base.num, codec->time_base.den, INT_MAX);
			} else
				codec->time_base = ist->st->time_base;
			switch (codec->codec_type) {
			case AVMEDIA_TYPE_AUDIO:
				codec->channel_layout = icodec->channel_layout;
				codec->sample_rate = icodec->sample_rate;
				codec->channels = icodec->channels;
				codec->frame_size = icodec->frame_size;
				codec->audio_service_type = icodec->audio_service_type;
				codec->block_align= icodec->block_align;
				if (codec->block_align == 1 && codec->codec_id == CODEC_ID_MP3)
					codec->block_align= 0;
				if (codec->codec_id == CODEC_ID_AC3)
					codec->block_align= 0;
				break;
			case AVMEDIA_TYPE_VIDEO:
				codec->pix_fmt = icodec->pix_fmt;
				codec->width = icodec->width;
				codec->height = icodec->height;
				codec->has_b_frames = icodec->has_b_frames;
                if (!codec->sample_aspect_ratio.num) {
                    codec->sample_aspect_ratio =
                    ost->st->sample_aspect_ratio =
                        ist->st->sample_aspect_ratio.num ? ist->st->sample_aspect_ratio :
                        ist->st->codec->sample_aspect_ratio.num ?
                        ist->st->codec->sample_aspect_ratio : (AVRational){0, 1};
                }
				break;
			case AVMEDIA_TYPE_SUBTITLE:
				codec->width = icodec->width;
				codec->height = icodec->height;
				break;
			case AVMEDIA_TYPE_DATA:
				break;
			default:
				wxLogError(_("Unknown codec type"));
				return false;
			}
		} else {
			if (!ost->enc)
				ost->enc = avcodec_find_encoder(ost->st->codec->codec_id);
			switch (codec->codec_type) {
			case AVMEDIA_TYPE_AUDIO:
				ost->fifo= av_fifo_alloc(1024);
				if(!ost->fifo) {
					ret = AVERROR(ENOMEM);
					goto fail;
				}
				ost->reformat_pair = MAKE_SFMT_PAIR(AV_SAMPLE_FMT_NONE,AV_SAMPLE_FMT_NONE);
				if (!codec->sample_rate) {
					codec->sample_rate = icodec->sample_rate;
				}
				choose_sample_rate(ost->st, ost->enc);
				codec->time_base = (AVRational){1, codec->sample_rate};
				if (codec->sample_fmt == AV_SAMPLE_FMT_NONE)
					codec->sample_fmt = icodec->sample_fmt;
				choose_sample_fmt(ost->st, ost->enc);
				if (!codec->channels)
					codec->channels = icodec->channels;
				codec->channel_layout = icodec->channel_layout;
				if (av_get_channel_layout_nb_channels(codec->channel_layout) != codec->channels)
					codec->channel_layout = 0;
				ost->audio_resample = codec->sample_rate != icodec->sample_rate || audio_sync_method > 1;
				icodec->request_channels = codec->channels;
				ist->decoding_needed = 1;
				ost->encoding_needed = 1;
                ost->resample_sample_fmt  = icodec->sample_fmt;
                ost->resample_sample_rate = icodec->sample_rate;
                ost->resample_channels    = icodec->channels;
				break;
			case AVMEDIA_TYPE_VIDEO:
				if (codec->pix_fmt == PIX_FMT_NONE)
					codec->pix_fmt = icodec->pix_fmt;
				choose_pixel_fmt(ost->st, ost->enc);
				if (ost->st->codec->pix_fmt == PIX_FMT_NONE) {
					wxLogError(wxT("Video pixel format is unknown, stream cannot be encoded"));
					return false;
				}
                if (!codec->width || !codec->height) {
                    codec->width  = icodec->width;
                    codec->height = icodec->height;
                }
				ost->video_resample = codec->width != icodec->width || codec->height != icodec->height ||
						codec->pix_fmt != icodec->pix_fmt; 
				if (ost->video_resample) {
					codec->bits_per_raw_sample= frame_bits_per_raw_sample;
				}
				ost->resample_height = icodec->height;
				ost->resample_width  = icodec->width;
				ost->resample_pix_fmt= icodec->pix_fmt;
				ost->encoding_needed = 1;
				ist->decoding_needed = 1;

                if (!ost->frame_rate.num)
                    ost->frame_rate = ist->st->r_frame_rate.num ? ist->st->r_frame_rate : (AVRational){25,1};
                if (ost->enc && ost->enc->supported_framerates && !force_fps) {
                    int idx = av_find_nearest_q_idx(ost->frame_rate, ost->enc->supported_framerates);
                    ost->frame_rate = ost->enc->supported_framerates[idx];
                }
                codec->time_base = (AVRational){ost->frame_rate.den, ost->frame_rate.num};
                if(   av_q2d(codec->time_base) < 0.001 && video_sync_method
                   && (video_sync_method==1 || (video_sync_method<0 && !(os->oformat->flags & AVFMT_VARIABLE_FPS)))){
                    av_log(os, AV_LOG_WARNING, "Frame rate very high for a muxer not effciciently supporting it.\n"
                                               "Please consider specifiying a lower framerate, a different muxer or -vsync 2\n");
                }
				
                if (configure_video_filters(ist, ost)) {
                    wxLogError(wxT("Error opening filters!"));
                    return false;
                }
				break;
			case AVMEDIA_TYPE_SUBTITLE:
				ost->encoding_needed = 1;
				ist->decoding_needed = 1;
				break;
			default:
				wxLogError(_("Unknown codec type"));
				return false;
			}
			/* two pass mode */
			if (ost->encoding_needed && codec->codec_id != CODEC_ID_H264 &&
					(codec->flags & (CODEC_FLAG_PASS1 | CODEC_FLAG_PASS2))) {
				char logfilename[1024];
				FILE *f;
				
				snprintf(logfilename, sizeof(logfilename), "%s-%d.log", pass_logfilename_prefix, i);
				if (codec->flags & CODEC_FLAG_PASS1) {
					f = fopen(logfilename, "wb");
					if (!f) {
						wxLogError(wxT("Cannot write log file '%s' for pass-1 encoding: %s"), logfilename, strerror(errno));
						return false;
					}
					ost->logfile = f;
				} else {
					char  *logbuffer;
					size_t logbuffer_size;
					if (read_file(logfilename, &logbuffer, &logbuffer_size) < 0) {
						wxLogError(wxT("Error reading log file '%s' for pass-2 encoding"), logfilename);
						return false;
					}
					codec->stats_in = logbuffer;
				}
			}
		}
		if (codec->codec_type == AVMEDIA_TYPE_VIDEO) {
			/* maximum video buffer size is 6-bytes per pixel, plus DPX header size */
			int size= codec->width * codec->height;
			bit_buffer_size= FFMAX(bit_buffer_size, 6*size + 1664);
		}
	}

	if (!bit_buffer)
		bit_buffer = (uint8_t*) av_malloc(bit_buffer_size);
	if (!bit_buffer) {
		wxLogError(wxT("Cannot allocate %d bytes output buffer"), bit_buffer_size);
		ret = AVERROR(ENOMEM);
		goto fail;
	}

	/* open each encoder */
	for (i=0; i<nb_output_streams; i++) {
		ost = &output_streams[i];
		if (ost->encoding_needed) {
			AVCodec *codec = ost->enc;
			AVCodecContext *dec = input_streams[ost->source_index].st->codec;
			if (!codec) {
				error = wxString::Format(wxT("Encoder (codec id %d) not found for output stream #%d.%d"),
						ost->st->codec->codec_id, ost->file_index, ost->index);
				ret = AVERROR(EINVAL);
				goto dump_format;
			}
            if (dec->subtitle_header) {
                ost->st->codec->subtitle_header = (uint8_t*) av_malloc(dec->subtitle_header_size);
                if (!ost->st->codec->subtitle_header) {
                    ret = AVERROR(ENOMEM);
                    goto dump_format;
                }
                memcpy(ost->st->codec->subtitle_header, dec->subtitle_header, dec->subtitle_header_size);
                ost->st->codec->subtitle_header_size = dec->subtitle_header_size;
            }
			if (avcodec_open(ost->st->codec, codec) < 0) {
				error = wxString::Format(wxT("Error while opening encoder for output stream #%d.%d - maybe incorrect parameters such as bit_rate, rate, width or height"),
						ost->file_index, ost->index);
				ret = AVERROR(EINVAL);
				goto dump_format;
			}
			//assert_codec_experimental(ost->st->codec, 1));
			extra_size += ost->st->codec->extradata_size;
		}
	}

	/* open each decoder */
	for (i = 0; i < nb_input_streams; i++) {
		ist = &input_streams[i];
		if (ist->decoding_needed) {
			AVCodec *codec = avcodec_find_decoder(ist->st->codec->codec_id);
			if (!codec) {
				wxLogError(wxT("Decoder (codec id %d) not found for input stream #%d.%d"),
						ist->st->codec->codec_id, ist->file_index, ist->st->index);
				return false;
			}
			
			/* update requested sample format for the decoder based on the corresponding encoder sample format */
			for (j = 0; j < nb_output_streams; j++) {
				ost = &output_streams[j];
				if (ost->source_index == i) {
					update_sample_fmt(ist->st->codec, codec, ost->st->codec);
					break;
				}
			}
			            
//			if (avcodec_open2(ist->st->codec, codec, &ist->opts) < 0) {
			if (avcodec_open(ist->st->codec, codec) < 0) {
				wxLogError(wxT("Error while opening decoder for input stream #%d.%d"),
						ist->file_index, ist->st->index);
				return false;
			}
			//assert_codec_experimental(ist->st->codec, 0));
			//assert_avoptions(ost->opts);
		}
	}

	/* init pts */
	for (i = 0; i < nb_input_streams; i++) {
		AVStream *st;
		ist = &input_streams[i];
		st= ist->st;
		ist->pts = st->avg_frame_rate.num ? - st->codec->has_b_frames*AV_TIME_BASE / av_q2d(st->avg_frame_rate) : 0;
		ist->next_pts = AV_NOPTS_VALUE;
		ist->is_start = 1;
	}
	
	/* open files and write file headers */
	for (i=0; i<nb_output_files; i++) {
		os = output_files[i].ctx;
		if (avformat_write_header(os, &output_files[i].opts) < 0) {
			wxLogError(wxT("Could not write header for output file #%d (incorrect codec parameters ?)"), i);
			ret = AVERROR(EINVAL);
			goto dump_format;
		}
//		assert_avoptions(output_files[i].opts);
//		if (strcmp(output_files[i].ctx->oformat->name, "rtp")) {
//			want_sdp = 0;
//		}
	}
	
dump_format:
	// dump the file output parameters - cannot be done before in case of stream copy
	for(i=0;i<nb_output_files;i++) {
		av_dump_format(output_files[i].ctx, i, output_files[i].ctx->filename, 1);
	}

	/* dump the stream mapping */
	if (verbose >= 0) {
		wxLogMessage(wxT("Stream mapping:"));
		for(i=0;i<nb_output_streams;i++) {
			ost = &output_streams[i];
			wxString msg = wxString::Format(wxT("  Stream #%d.%d -> #%d.%d"),
					input_streams[ost->source_index].file_index, input_streams[ost->source_index].st->index,
					ost->file_index, ost->index);
			//if (ost->sync_ist != &input_streams[ost->source_index])
			//	msg += wxString::Format(wxT(" [sync #%d.%d]"), ost->sync_ist->file_index, ost->sync_ist->st->index);
			if (ost->st->stream_copy)
				msg += wxT(" (copy)");
			wxLogMessage(msg);
		}
	}
	if (ret) {
		wxLogError(error);
		goto fail;
	}
	
//	if (want_sdp) {
//		print_sdp(output_files, nb_output_files);
//	}
	
	timer_start = av_gettime();
	
	while (true) { //for(; received_sigterm == 0;) {
		int file_index, ist_index;
		AVPacket pkt;
		int64_t ipts_min;
		double opts_min;

	redo:
		ipts_min= INT64_MAX;
		opts_min= 1e100;
		
		// check if user canceled the transcoding
		if (canceled)
			return false;

		/* select the stream that we must read now by looking at the
		 smallest output pts */
		file_index = -1;
		for (i=0; i<nb_output_streams; i++) {
			int64_t ipts;
			double opts;
			ost =  &output_streams[i];
			os = output_files[ost->file_index].ctx;
			ist = &input_streams[ost->source_index];
			if (ost->is_past_recording_time || no_packet[ist->file_index])
				continue;
			opts = ost->st->pts.val * av_q2d(ost->st->time_base);
			ipts = ist->pts;
			if (!input_files[ist->file_index].eof_reached) {
				if (ipts < ipts_min) {
					ipts_min = ipts;
					if (input_sync)
						file_index = ist->file_index;
				}
				if (opts < opts_min) {
					opts_min = opts;
					if (!input_sync)
						file_index = ist->file_index;
				}
			}
			if (ost->frame_number >= max_frames[ost->st->codec->codec_type]) {
				file_index= -1;
				break;
			}
		}
		/* if none, if is finished */
		if (file_index < 0) {
			if(no_packet_count){
				no_packet_count=0;
				memset(no_packet, 0, nb_input_files);
				wxMicroSleep(10000);
				continue;
			}
			break;
		}
		
		/* finish if limit size exhausted */
		if (limit_filesize != 0 && limit_filesize <= (uint64_t) avio_tell(output_files[0].ctx->pb))
			break;

		/* read a frame from it and output it in the fifo */
		is = input_files[file_index].ctx;
		ret= av_read_frame(is, &pkt);
		if(ret == AVERROR(EAGAIN)){
			no_packet[file_index]=1;
			no_packet_count++;
			continue;
		}
		if (ret < 0) {
			input_files[file_index].eof_reached = 1;
			if (opt_shortest)
				break;
			else
				continue;
		}
		
		no_packet_count=0;
		memset(no_packet, 0, nb_input_files);
		
//		if (do_pkt_dump) {
//			av_pkt_dump_log2(NULL, AV_LOG_DEBUG, &pkt, do_hex_dump, is->streams[pkt.stream_index]);
//		}
		
		/* the following test is needed in case new streams appear
		   dynamically in stream : we ignore them */
		if (pkt.stream_index >= (int) input_files[file_index].nb_streams)
			goto discard_packet;
		ist_index = input_files[file_index].ist_index + pkt.stream_index;
		ist = &input_streams[ist_index];
		if (ist->discard)
			goto discard_packet;
		
		if (pkt.dts != (int64_t) AV_NOPTS_VALUE)
			pkt.dts += av_rescale_q(input_files[ist->file_index].ts_offset, AV_TIME_BASE_Q, ist->st->time_base);
		if (pkt.pts != (int64_t) AV_NOPTS_VALUE)
			pkt.pts += av_rescale_q(input_files[ist->file_index].ts_offset, AV_TIME_BASE_Q, ist->st->time_base);
		
		 if (ist->ts_scale) {
			if(pkt.pts != (int64_t) AV_NOPTS_VALUE)
				pkt.pts = (int64_t) (pkt.pts * ist->ts_scale);
			if(pkt.dts != (int64_t) AV_NOPTS_VALUE)
				pkt.dts = (int64_t) (pkt.dts * ist->ts_scale);
		}
		
		if (pkt.dts != (int64_t) AV_NOPTS_VALUE && ist->next_pts != (int64_t) AV_NOPTS_VALUE
				&& (is->iformat->flags & AVFMT_TS_DISCONT)) {
			int64_t pkt_dts= av_rescale_q(pkt.dts, ist->st->time_base, AV_TIME_BASE_Q);
			int64_t delta= pkt_dts - ist->next_pts;
			if ((FFABS(delta)> 1LL*dts_delta_threshold*AV_TIME_BASE || pkt_dts+1<ist->pts)
					&& !copy_ts) {
				input_files[ist->file_index].ts_offset-= delta;
//				if (verbose > 2)
//					wxLogMessage(wxT("timestamp discontinuity %"PRId64", new offset= %"PRId64""), delta, input_files[ist->file_index].ts_offset);
				pkt.dts-= av_rescale_q(delta, AV_TIME_BASE_Q , ist->st->time_base);
				if (pkt.pts != (int64_t) AV_NOPTS_VALUE)
					pkt.pts-= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base);
			}
		}
		
		//wxLogMessage(wxT("read #%d.%d size=%d"), ist->file_index, ist->index, pkt.size);
		if (!OutputPacket(ist, ist_index, output_streams, nb_output_streams, &pkt)) {
			if (verbose >= 0)
				wxLogMessage(wxT("Error while decoding stream #%d.%d"), ist->file_index, ist->st->index);
			if (exit_on_error)
				return false;
			av_free_packet(&pkt);
			goto redo;
		}
		
	discard_packet:
		av_free_packet(&pkt);
		
		/* dump report by using the output first video and audio streams */
		PrintReport(output_files, output_streams, nb_output_streams, 0);
	}
	
	/* at the end of stream, we must flush the decoder buffers */
	for (i = 0; i < nb_input_streams; i++) {
		ist = &input_streams[i];
		if (ist->decoding_needed)
			OutputPacket(ist, i, output_streams, nb_output_streams, NULL);
	}

	/* write the trailer if needed and close file */
	for (i=0; i<nb_output_files; i++) {
		os = output_files[i].ctx;
		av_write_trailer(os);
	}
	
	/* dump report by using the first video and audio streams */
	PrintReport(output_files, output_streams, nb_output_streams, 1);
	
	/* close each encoder */
	for (i=0; i<nb_output_streams; i++) {
		ost = &output_streams[i];
		if (ost->encoding_needed) {
			av_freep(&ost->st->codec->stats_in);
			avcodec_close(ost->st->codec);
		}
        avfilter_graph_free(&ost->graph);
	}
	
	/* close each decoder */
	for (i = 0; i < nb_input_streams; i++) {
		ist = &input_streams[i];
		if (ist->decoding_needed) {
			avcodec_close(ist->st->codec);
		}
	}
	
	/* finished ! */
	ret = 0;
fail:
	av_freep(&bit_buffer);
	av_freep(&no_packet);
	
	if (output_streams) {
		for (i = 0; i < nb_output_streams; i++) {
			ost = &output_streams[i];
			if (ost) {
				if (ost->st->stream_copy)
					av_freep(&ost->st->codec->extradata);
				if (ost->logfile) {
					fclose(ost->logfile);
					ost->logfile = NULL;
				}
				av_fifo_free(ost->fifo); /* works even if fifo is not initialized but set to zero */
				av_freep(&ost->st->codec->subtitle_header);
				av_free(ost->resample_frame.data[0]);
				av_free(ost->forced_kf_pts);
				if (ost->video_resample)
					sws_freeContext(ost->img_resample_ctx);
				if (ost->resample)
					audio_resample_close(ost->resample);
				if (ost->reformat_ctx)
					av_audio_convert_free(ost->reformat_ctx);
				av_dict_free(&ost->opts);
			}
		}
	}
	End();
	return ret == 0;
}

double wxFfmpegMediaTranscoder::get_sync_ipts(const OutputStream *ost) {
	const InputStream *ist = ost->sync_ist;
	return (double)(ist->pts - start_time)/AV_TIME_BASE;
}

/* pkt = NULL means EOF (needed to flush decoder buffers) */
bool wxFfmpegMediaTranscoder::OutputPacket(InputStream *ist, int ist_index, OutputStream *ost_table,
		int nb_output_streams, const AVPacket *pkt) {
	AVFormatContext *os;
	OutputStream *ost;
	int ret, i;
	int got_output;
	AVFrame picture;
	void *buffer_to_free = NULL;
	unsigned int samples_size = 0;
	AVSubtitle subtitle, *subtitle_to_free;
    int frame_available;
	
	AVPacket avpkt;
	int bps = av_get_bytes_per_sample(ist->st->codec->sample_fmt);
	
	if(ist->next_pts == (int64_t) AV_NOPTS_VALUE)
		ist->next_pts= ist->pts;

	if (pkt == NULL) {
		// EOF handling
		av_init_packet(&avpkt);
		avpkt.data = NULL;
		avpkt.size = 0;
		goto handle_eof;
	} else {
		avpkt = *pkt;
	}
	
	if(pkt->dts != (int64_t) AV_NOPTS_VALUE)
		ist->next_pts = ist->pts = av_rescale_q(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q);
	
	//while we have more to decode or while the decoder did output something on EOF
	while (avpkt.size > 0 || (!pkt && got_output)) {
		uint8_t *data_buf, *decoded_data_buf;
		int data_size, decoded_data_size;
	handle_eof:
		ist->pts= ist->next_pts;
		
		if(avpkt.size && avpkt.size != pkt->size &&
		   ((!ist->showed_multi_packet_warning && verbose>0) || verbose>1)){
			wxLogError(wxT("Multiple frames in a packet from stream %d"), pkt->stream_index);
			ist->showed_multi_packet_warning=1;
		}
		
		// decode the packet if needed
		decoded_data_buf = NULL; // fail safe
		decoded_data_size= 0;
		data_buf  = avpkt.data;
		data_size = avpkt.size;
		subtitle_to_free = NULL;
		if (ist->decoding_needed) {
			switch (ist->st->codec->codec_type) {
			case AVMEDIA_TYPE_AUDIO: {
				if(pkt && samples_size < FFMAX(pkt->size*sizeof(*samples), AVCODEC_MAX_AUDIO_FRAME_SIZE)) {
					samples_size = FFMAX(pkt->size*sizeof(*samples), AVCODEC_MAX_AUDIO_FRAME_SIZE);
					av_free(samples);
					samples= (short *) av_malloc(samples_size);
				}
				decoded_data_size= samples_size;
				// XXX: could avoid copy if PCM 16 bits with same endianness as CPU
				ret = avcodec_decode_audio3(ist->st->codec, samples, &decoded_data_size, &avpkt);
				if (ret < 0)
					return false;
				avpkt.data += ret;
				avpkt.size -= ret;
				data_size   = ret;
				got_output  = decoded_data_size > 0;
				// Some bug in mpeg audio decoder gives
				// decoded_data_size < 0, it seems they are overflows
				if (!got_output) {
					// no audio frame
					continue;
				}
				decoded_data_buf = (uint8_t *)samples;
				ist->next_pts += ((int64_t)AV_TIME_BASE/bps * decoded_data_size) /
						(ist->st->codec->sample_rate * ist->st->codec->channels);
				break;
			}
			case AVMEDIA_TYPE_VIDEO:
				decoded_data_size = (ist->st->codec->width * ist->st->codec->height * 3) / 2;
				// XXX: allocate picture correctly
				avcodec_get_frame_defaults(&picture);
				
				ret = avcodec_decode_video2(ist->st->codec, &picture, &got_output, &avpkt);
				ist->st->quality= picture.quality;
				if (ret < 0)
					return false;
				if (!got_output) {
					// no picture yet
					goto discard_packet;
				}
				if (ist->st->codec->time_base.num != 0) {
					int ticks= ist->st->parser ? ist->st->parser->repeat_pict+1 : ist->st->codec->ticks_per_frame;
					ist->next_pts += ((int64_t)AV_TIME_BASE
							* ist->st->codec->time_base.num * ticks)
							/ ist->st->codec->time_base.den;
				}
				avpkt.size = 0;
				buffer_to_free = NULL;
				pre_process_video_frame(ist, (AVPicture *)&picture, &buffer_to_free);
				break;
			case AVMEDIA_TYPE_SUBTITLE:
				ret = avcodec_decode_subtitle2(ist->st->codec, &subtitle, &got_output, &avpkt);
				if (ret < 0)
					return false;
				if (!got_output) {
					goto discard_packet;
				}
				subtitle_to_free = &subtitle;
				avpkt.size = 0;
				break;
			default:
				return false;
			}
		} else {
			switch (ist->st->codec->codec_type) {
			case AVMEDIA_TYPE_AUDIO:
				ist->next_pts += ((int64_t)AV_TIME_BASE
						* ist->st->codec->frame_size)
						/ ist->st->codec->sample_rate;
				break;
			case AVMEDIA_TYPE_VIDEO:
				if (ist->st->codec->time_base.num != 0) {
					int ticks= ist->st->parser ? ist->st->parser->repeat_pict+1 : ist->st->codec->ticks_per_frame;
					ist->next_pts += ((int64_t)AV_TIME_BASE
							* ist->st->codec->time_base.num * ticks)
							/ ist->st->codec->time_base.den;
				}
				break;
			default:
				break;
			}
			ret = avpkt.size;
			avpkt.size = 0;
		}
		
		if(ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
		if (start_time == 0 || ist->pts >= start_time) {
			for(i=0;i<nb_output_streams;i++) {
				ost = &ost_table[i];
				if (ost->input_video_filter && ost->source_index == ist_index) {
					if (!picture.sample_aspect_ratio.num)
						picture.sample_aspect_ratio = ist->st->sample_aspect_ratio;
					picture.pts = ist->pts;

					av_vsrc_buffer_add_frame(ost->input_video_filter, &picture, AV_VSRC_BUF_FLAG_OVERWRITE);
				}
			}
		}
        
		// preprocess audio (volume)
		if (ist->st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
			if (m_audioVolume.find(ist_index) != m_audioVolume.end() && m_audioVolume[ist_index] != 256) {
				short *volp;
				volp = samples;
				for (i=0; i<(int)(decoded_data_size / sizeof(short)); i++) {
					int v = ((*volp) * m_audioVolume[ist_index] + 128) >> 8;
					if (v < -32768)
						v = -32768;
					if (v > 32767)
						v = 32767;
					*volp++ = v;
				}
			}
		}
		
		// frame rate emulation
		if (rate_emu) {
			int64_t pts = av_rescale(ist->pts, 1000000, AV_TIME_BASE);
			int64_t now = av_gettime() - ist->start;
			if (pts > now)
				wxMicroSleep(pts - now);
		}
		
		// if output time reached then transcode raw format, encode packets and output them
		if (start_time == 0 || ist->pts >= start_time)
			for (i=0; i<nb_output_streams; i++) {
				OutputFile *of = &output_files[ost_table[i].file_index];
				int frame_size;
				
				ost = &ost_table[i];
				if (ost->source_index != ist_index)
					continue;
	
				if (of->recording_time != INT64_MAX && av_compare_ts(ist->pts, AV_TIME_BASE_Q,
						of->recording_time + start_time, (AVRational) {1, 1000000}) >= 0) {
					ost->is_past_recording_time = 1;
					continue;
				}
			
				frame_available = ist->st->codec->codec_type != AVMEDIA_TYPE_VIDEO ||
					!ost->output_video_filter || avfilter_poll_frame(ost->output_video_filter->inputs[0]);
				while (frame_available) {
					if (ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO && ost->output_video_filter) {
						AVRational ist_pts_tb = ost->output_video_filter->inputs[0]->time_base;
						if (av_vsink_buffer_get_video_buffer_ref(ost->output_video_filter, &ost->picref, 0) < 0)
							goto cont;
						if (ost->picref) {
							avfilter_fill_frame_from_video_buffer_ref(&picture, ost->picref);
							ist->pts = av_rescale_q(ost->picref->pts, ist_pts_tb, AV_TIME_BASE_Q);
						}
					}
					os = output_files[ost->file_index].ctx;
					
					// set the input output pts pairs
					//ost->sync_ipts = (double)(ist->pts + input_files[ist->file_index].ts_offset - start_time)/ AV_TIME_BASE;
					
					if (ost->encoding_needed) {
//						av_assert0(ist->decoding_needed);
						switch (ost->st->codec->codec_type) {
						case AVMEDIA_TYPE_AUDIO:
							if (!DoAudioOut(os, ost, ist,
									decoded_data_buf, decoded_data_size))
								return false;
							break;
						case AVMEDIA_TYPE_VIDEO:
                            if (ost->picref->video)
                                ost->st->codec->sample_aspect_ratio = ost->picref->video->sample_aspect_ratio;
							if (!DoVideoOut(os, ost, ist, &picture, &frame_size, bit_buffer_size, bit_buffer))
								return false;
//							if (vstats_filename && frame_size)
//								do_video_stats(os, ost, frame_size);
							break;
						case AVMEDIA_TYPE_SUBTITLE:
							if (!DoSubtitleOut(os, ost, ist,
									&subtitle, pkt->pts))
								return false;
							break;
						default:
							wxLogError(_("Unknown codec type"));
							return false;
						}
					} else {
						AVFrame avframe; //FIXME/XXX remove this
						AVPicture pict;
						AVPacket opkt;
						int64_t ost_tb_start_time= av_rescale_q(start_time, AV_TIME_BASE_Q, ost->st->time_base);
						
						av_init_packet(&opkt);
						
						if ((!ost->frame_number && !(pkt->flags & AV_PKT_FLAG_KEY)) && !copy_initial_nonkeyframes)
                            goto cont;
						
						// no reencoding needed : output the packet directly
						// force the input stream PTS
						
						avcodec_get_frame_defaults(&avframe);
						ost->st->codec->coded_frame= &avframe;
						avframe.key_frame = pkt->flags & AV_PKT_FLAG_KEY;
						
						if (ost->st->codec->codec_type == AVMEDIA_TYPE_AUDIO)
							audio_size += data_size;
						else if (ost->st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
							video_size += data_size;
							ost->sync_opts++;
						}
						
						opkt.stream_index= ost->index;
						if (pkt->pts != (int64_t) AV_NOPTS_VALUE)
							opkt.pts = av_rescale_q(pkt->pts, ist->st->time_base, ost->st->time_base) - ost_tb_start_time;
						else
							opkt.pts= AV_NOPTS_VALUE;
						
						if (pkt->dts == (int64_t) AV_NOPTS_VALUE)
							opkt.dts = av_rescale_q(ist->pts, AV_TIME_BASE_Q, ost->st->time_base);
						else
							opkt.dts = av_rescale_q(pkt->dts, ist->st->time_base, ost->st->time_base);
						opkt.dts -= ost_tb_start_time;
						
						opkt.duration = av_rescale_q(pkt->duration, ist->st->time_base, ost->st->time_base);
						opkt.flags = pkt->flags;
						
						//FIXME remove the following 2 lines they shall be replaced by the bitstream filters
						if(ost->st->codec->codec_id != CODEC_ID_H264
								&& ost->st->codec->codec_id != CODEC_ID_MPEG1VIDEO
								&& ost->st->codec->codec_id != CODEC_ID_MPEG2VIDEO) {
							if(av_parser_change(ist->st->parser, ost->st->codec, &opkt.data, &opkt.size,
									data_buf, data_size, pkt->flags & AV_PKT_FLAG_KEY))
								opkt.destruct= av_destruct_packet;
						} else {
							opkt.data = data_buf;
							opkt.size = data_size;
						}
						
						if (os->oformat->flags & AVFMT_RAWPICTURE) {
							/* store AVPicture in AVPacket, as expected by the output format */
							avpicture_fill(&pict, opkt.data, ost->st->codec->pix_fmt, ost->st->codec->width, ost->st->codec->height);
							opkt.data = (uint8_t *)&pict;
							opkt.size = sizeof(AVPicture);
							opkt.flags |= AV_PKT_FLAG_KEY;
						}
						
						if (!WriteFrame(os, &opkt, ost->st->codec, ost->bitstream_filters))
							return false;
						
						ost->st->codec->frame_number++;
						ost->frame_number++;
						av_free_packet(&opkt);
					}
					cont:
					frame_available = (ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO) &&
							ost->output_video_filter && avfilter_poll_frame(ost->output_video_filter->inputs[0]);
					avfilter_unref_buffer(ost->picref);
				}
		}
		av_free(buffer_to_free);
		// XXX: allocate the subtitles in the codec ?
		if (subtitle_to_free) {
			avsubtitle_free(subtitle_to_free);
			subtitle_to_free = NULL;
		}
	}
discard_packet:
	if (pkt == NULL) {
		// EOF handling
		
		for (i=0; i<nb_output_streams; i++) {
			ost = &ost_table[i];
			if (ost->source_index == ist_index) {
				AVCodecContext *enc= ost->st->codec;
				os = output_files[ost->file_index].ctx;
				
				if (ost->st->codec->codec_type == AVMEDIA_TYPE_AUDIO && enc->frame_size <=1)
					continue;
				if (ost->st->codec->codec_type == AVMEDIA_TYPE_VIDEO && (os->oformat->flags & AVFMT_RAWPICTURE))
					continue;
				
				if (ost->encoding_needed) {
					for (;;) {
						AVPacket pkt;
						int fifo_bytes;
						av_init_packet(&pkt);
						pkt.stream_index= ost->index;

						switch (ost->st->codec->codec_type) {
						case AVMEDIA_TYPE_AUDIO:
							fifo_bytes = av_fifo_size(ost->fifo);
							ret = 0;
							// encode any samples remaining in fifo
 							if (fifo_bytes > 0) {
								int osize = av_get_bytes_per_sample(enc->sample_fmt);
								int fs_tmp = enc->frame_size;
								av_fifo_generic_read(ost->fifo, audio_buf, fifo_bytes, NULL);
								if (enc->codec->capabilities & CODEC_CAP_SMALL_LAST_FRAME) {
									enc->frame_size = fifo_bytes / (osize * enc->channels);
								} else { // pad
									unsigned int frame_bytes = enc->frame_size*osize*enc->channels;
									if (allocated_audio_buf_size < frame_bytes)
										return false;
									memset(audio_buf+fifo_bytes, 0, frame_bytes - fifo_bytes);
								}

								ret = avcodec_encode_audio(enc, bit_buffer, bit_buffer_size, (short *)audio_buf);
								pkt.duration = av_rescale((int64_t)enc->frame_size*ost->st->time_base.den,
														  ost->st->time_base.num, enc->sample_rate);
								enc->frame_size = fs_tmp;
							}
							if (ret <= 0) {
								ret = avcodec_encode_audio(enc, bit_buffer, bit_buffer_size, NULL);
							}
							if (ret < 0) {
								wxLogError(wxT("Audio encoding failed"));
								return false;
							}
							audio_size += ret;
							pkt.flags |= AV_PKT_FLAG_KEY;
							break;
						case AVMEDIA_TYPE_VIDEO:
							ret = avcodec_encode_video(enc, bit_buffer, bit_buffer_size, NULL);
							if (ret < 0) {
								wxLogError(wxT("Video encoding failed"));
								return false;
							}
							video_size += ret;
							if (enc->coded_frame && enc->coded_frame->key_frame)
								pkt.flags |= AV_PKT_FLAG_KEY;
							if (ost->logfile && enc->stats_out) {
								fprintf(ost->logfile, "%s", enc->stats_out);
							}
							break;
						default:
							ret=-1;
						}

						if (ret<=0)
							break;
						pkt.data = bit_buffer;
						pkt.size = ret;
						if (enc->coded_frame && enc->coded_frame->pts != (int64_t) AV_NOPTS_VALUE)
							pkt.pts = av_rescale_q(enc->coded_frame->pts, enc->time_base, ost->st->time_base);
						if (!WriteFrame(os, &pkt, ost->st->codec, ost->bitstream_filters))
							return false;
					}
				}
			}
		}
	}
	return true;
}

bool wxFfmpegMediaTranscoder::WriteFrame(AVFormatContext *s, AVPacket *pkt,
		AVCodecContext *avctx, AVBitStreamFilterContext *bsfc) {
	int ret;
	
	while (bsfc) {
		AVPacket new_pkt= *pkt;
		int a= av_bitstream_filter_filter(bsfc, avctx,
				NULL, &new_pkt.data, &new_pkt.size,
				pkt->data, pkt->size, pkt->flags & AV_PKT_FLAG_KEY);
		if (a>0) {
			av_free_packet(pkt);
			new_pkt.destruct= av_destruct_packet;
		} else if (a<0) {
			wxLogError(wxT("%s failed for stream %d, codec %s"),
			bsfc->filter->name, pkt->stream_index,
			avctx->codec ? avctx->codec->name : "copy");
			PrintError(wxT(""), a);
		}
		*pkt= new_pkt;
		
		bsfc= bsfc->next;
	}
	
	ret = av_interleaved_write_frame(s, pkt);
	if (ret < 0) {
		PrintError(wxT("av_interleaved_write_frame()"), ret);
		return false;
	}
	return true;
}

bool wxFfmpegMediaTranscoder::DoAudioOut(AVFormatContext *s, OutputStream *ost,
		InputStream *ist, unsigned char *buf, int size) {
	uint8_t *buftmp;
	int64_t audio_out_size, audio_buf_size;
	int64_t allocated_for_size= size;
	
	int size_out, frame_bytes, ret, resample_changed;
	AVCodecContext *enc= ost->st->codec;
	AVCodecContext *dec= ist->st->codec;
	int osize= av_get_bytes_per_sample(enc->sample_fmt);
	int isize= av_get_bytes_per_sample(dec->sample_fmt);
	const int coded_bps = av_get_bits_per_sample(enc->codec->id);

need_realloc:
	audio_buf_size= (allocated_for_size + isize*dec->channels - 1) / (isize*dec->channels);
	audio_buf_size= (audio_buf_size*enc->sample_rate + dec->sample_rate) / dec->sample_rate;
	audio_buf_size= audio_buf_size*2 + 10000; //safety factors for the deprecated resampling API
	audio_buf_size= FFMAX(audio_buf_size, enc->frame_size);
	audio_buf_size*= osize*enc->channels;

	audio_out_size= FFMAX(audio_buf_size, enc->frame_size * osize * enc->channels);
	if(coded_bps > 8*osize)
		audio_out_size= audio_out_size * coded_bps / (8*osize);
	audio_out_size += FF_MIN_BUFFER_SIZE;
	
	if(audio_out_size > INT_MAX || audio_buf_size > INT_MAX){
		wxLogError(wxT("Buffer sizes too large"));
		return false;
	}
	
	av_fast_malloc(&audio_buf, &allocated_audio_buf_size, audio_buf_size);
	av_fast_malloc(&audio_out, &allocated_audio_out_size, audio_out_size);
	if (!audio_buf || !audio_out){
		wxLogError(wxT("Out of memory in do_audio_out"));
		return false;
	}
	
	if (enc->channels != dec->channels)
		ost->audio_resample = 1;
	
	resample_changed = ost->resample_sample_fmt  != dec->sample_fmt ||
					   ost->resample_channels    != dec->channels   ||
					   ost->resample_sample_rate != dec->sample_rate;
	
	if ((ost->audio_resample && !ost->resample) || resample_changed) {
		if (resample_changed) {
			av_log(NULL, AV_LOG_INFO, "Input stream #%d.%d frame changed from rate:%d fmt:%s ch:%d to rate:%d fmt:%s ch:%d\n",
				   ist->file_index, ist->st->index,
				   ost->resample_sample_rate, av_get_sample_fmt_name(ost->resample_sample_fmt), ost->resample_channels,
				   dec->sample_rate, av_get_sample_fmt_name(dec->sample_fmt), dec->channels);
			ost->resample_sample_fmt  = dec->sample_fmt;
			ost->resample_channels    = dec->channels;
			ost->resample_sample_rate = dec->sample_rate;
			if (ost->resample)
				audio_resample_close(ost->resample);
		}
		/* if audio_sync_method is >1 the resampler is needed for audio drift compensation */
		if (audio_sync_method <= 1 &&
				ost->resample_sample_fmt  == enc->sample_fmt &&
				ost->resample_channels    == enc->channels   &&
				ost->resample_sample_rate == enc->sample_rate) {
			ost->resample = NULL;
			ost->audio_resample = 0;
		} else {
			if (dec->sample_fmt != SAMPLE_FMT_S16)
				wxLogWarning(wxT("Warning, using s16 intermediate sample format for resampling"));
			ost->resample = av_audio_resample_init(enc->channels, dec->channels,
					enc->sample_rate, dec->sample_rate,
					enc->sample_fmt, dec->sample_fmt,
					16, 10, 0, 0.8);
			if (!ost->resample) {
				wxLogError(wxT("Can not resample %d channels @ %d Hz to %d channels @ %d Hz"),
						dec->channels, dec->sample_rate,
						enc->channels, enc->sample_rate);
				return false;
			}
		}
	}
	
	if (!ost->audio_resample && dec->sample_fmt!=enc->sample_fmt &&
		MAKE_SFMT_PAIR(enc->sample_fmt,dec->sample_fmt)!=ost->reformat_pair) {
		if (ost->reformat_ctx)
			av_audio_convert_free(ost->reformat_ctx);
		ost->reformat_ctx = av_audio_convert_alloc(enc->sample_fmt, 1,
												   dec->sample_fmt, 1, NULL, 0);
		if (!ost->reformat_ctx) {
			wxLogError(wxT("Cannot convert %s sample format to %s sample format"),
					av_get_sample_fmt_name(dec->sample_fmt),
					av_get_sample_fmt_name(enc->sample_fmt));
			return false;
		}
		ost->reformat_pair=MAKE_SFMT_PAIR(enc->sample_fmt,dec->sample_fmt);
	}
	
	if (audio_sync_method) {
		double delta = get_sync_ipts(ost) * enc->sample_rate - ost->sync_opts
				- av_fifo_size(ost->fifo)/(ost->st->codec->channels * 2);
		double idelta= delta*ist->st->codec->sample_rate / enc->sample_rate;
		int byte_delta= ((int)idelta)*2*ist->st->codec->channels;
		
		//FIXME resample delay
		if (fabs(delta) > 50) {
			if (ist->is_start || fabs(delta) > audio_drift_threshold *enc->sample_rate) {
				if (byte_delta < 0) {
					byte_delta= FFMAX(byte_delta, -size);
					size += byte_delta;
					buf -= byte_delta;
					if (verbose > 2)
						wxLogMessage(wxT("discarding %d audio samples"), (int)-delta);
					if (!size)
						return true;
					ist->is_start=0;
				} else {
					static uint8_t *input_tmp= NULL;
					input_tmp = (uint8_t*) av_realloc(input_tmp, byte_delta + size);
					
					if(byte_delta > allocated_for_size - size){
						allocated_for_size= byte_delta + (int64_t)size;
						goto need_realloc;
					}
					ist->is_start=0;
					
					memset(input_tmp, 0, byte_delta);
					memcpy(input_tmp + byte_delta, buf, size);
					buf= input_tmp;
					size += byte_delta;
					if (verbose > 2)
						wxLogMessage(wxT("adding %d audio samples of silence"), (int)delta);
				}
			} else if (audio_sync_method>1) {
				int comp= av_clip((int) delta, -audio_sync_method, audio_sync_method);
				assert(ost->audio_resample);
				if (verbose > 2)
					wxLogMessage(wxT("compensating audio timestamp drift:%f compensation:%d in:%d"), delta, comp, enc->sample_rate);
				av_resample_compensate(*(struct AVResampleContext**)ost->resample, comp, enc->sample_rate);
			}
		}
	} else
		ost->sync_opts= lrintf(get_sync_ipts(ost) * enc->sample_rate)
				- av_fifo_size(ost->fifo)/(ost->st->codec->channels * 2); //FIXME wrong
	
	if (ost->audio_resample) {
		buftmp = audio_buf;
		size_out = audio_resample(ost->resample,
				(short *)buftmp, (short *)buf,
				size / (ist->st->codec->channels * isize));
		size_out = size_out * enc->channels * osize;
	} else {
		buftmp = buf;
		size_out = size;
	}
	
	if (!ost->audio_resample && dec->sample_fmt!=enc->sample_fmt) {
		const void *ibuf[6]= {buftmp};
		void *obuf[6]= {audio_buf};
		int istride[6]= {isize};
		int ostride[6]= {osize};
		int len= size_out/istride[0];
		if (av_audio_convert(ost->reformat_ctx, obuf, ostride, ibuf, istride, len)<0) {
			wxLogError(wxT("av_audio_convert() failed"));
			if (exit_on_error)
				return false;
			return true;
		}
		buftmp = audio_buf;
		size_out = len*osize;
	}

	// now encode as many frames as possible
	if (enc->frame_size > 1) {
		// output resampled raw samples
		if (av_fifo_realloc2(ost->fifo, av_fifo_size(ost->fifo) + size_out) < 0) {
			wxLogError(wxT("av_fifo_realloc2() failed"));
			return false;
		}
		av_fifo_generic_write(ost->fifo, buftmp, size_out, NULL);

		frame_bytes = enc->frame_size * osize * enc->channels;

		while (av_fifo_size(ost->fifo) >= frame_bytes) {
			AVPacket pkt;
			av_init_packet(&pkt);
			
#if LIBAVUTIL_VERSION_INT > AV_VERSION_INT(49, 15, 0)
			av_fifo_generic_read(ost->fifo, audio_buf, frame_bytes, NULL);
#else
			av_fifo_generic_read(ost->fifo, frame_bytes, NULL, audio_buf);
#endif

			//FIXME pass ost->sync_opts as AVFrame.pts in avcodec_encode_audio()
			
			ret = avcodec_encode_audio(enc, audio_out, audio_out_size, (short *)audio_buf);
			if (ret < 0) {
				wxLogError(wxT("Audio encoding failed"));
				return false;
			}
			audio_size += ret;
			pkt.stream_index= ost->index;
			pkt.data= audio_out;
			pkt.size= ret;
			if(enc->coded_frame && enc->coded_frame->pts != (int64_t) AV_NOPTS_VALUE)
				pkt.pts= av_rescale_q(enc->coded_frame->pts, enc->time_base, ost->st->time_base);
			pkt.flags |= AV_PKT_FLAG_KEY;
			if (!WriteFrame(s, &pkt, ost->st->codec, ost->bitstream_filters))
				return false;
			
			ost->sync_opts += enc->frame_size;
		}
	} else {
		AVPacket pkt;
		av_init_packet(&pkt);
		
		ost->sync_opts += size_out / (osize * enc->channels);
		
		// output a pcm frame
		// determine the size of the coded buffer
		size_out /= osize;
		if (coded_bps)
			size_out = size_out*coded_bps/8;

		if(size_out > audio_out_size){
			wxLogError(wxT("Internal error, buffer size too small"));
			return false;
		}

		//FIXME pass ost->sync_opts as AVFrame.pts in avcodec_encode_audio()
		ret = avcodec_encode_audio(enc, audio_out, size_out, (short *)buftmp);
		if (ret < 0) {
			wxLogError(wxT("Audio encoding failed"));
			return false;
		}
		audio_size += ret;
		pkt.stream_index= ost->index;
		pkt.data= audio_out;
		pkt.size= ret;
		if (enc->coded_frame && enc->coded_frame->pts
				!= (int64_t) AV_NOPTS_VALUE)
			pkt.pts= av_rescale_q(
					enc->coded_frame->pts,
					enc->time_base, ost->st->time_base);
		pkt.flags |= AV_PKT_FLAG_KEY;
		if (!WriteFrame(s, &pkt, ost->st->codec, ost->bitstream_filters))
			return false;
	}
	return true;
}

void wxFfmpegMediaTranscoder::pre_process_video_frame(InputStream *ist, AVPicture *picture, void **bufp) {
	AVCodecContext *dec;
	AVPicture *picture2;
	AVPicture picture_tmp;
	uint8_t *buf = 0;

	dec = ist->st->codec;

	/* deinterlace : must be done before any resize */
	if (do_deinterlace) {
		int size;

		/* create temporary picture */
		size = avpicture_get_size(dec->pix_fmt, dec->width, dec->height);
		buf = (uint8_t*) av_malloc(size);
		if (!buf)
			return;

		picture2 = &picture_tmp;
		avpicture_fill(picture2, buf, dec->pix_fmt, dec->width, dec->height);
		
		if(avpicture_deinterlace(picture2, picture, dec->pix_fmt, dec->width, dec->height) < 0) {
			/* if error, do not deinterlace */
			wxLogError(wxT("Deinterlacing failed"));
			av_free(buf);
			buf = NULL;
			picture2 = picture;
		}
	} else {
		picture2 = picture;
	}

	if (picture != picture2)
		*picture = *picture2;
	*bufp = buf;
}

void do_video_resample(OutputStream *ost, InputStream *ist, AVFrame *in_picture, AVFrame **out_picture) {
	int resample_changed = 0;
	AVCodecContext *dec = ist->st->codec;
	*out_picture = in_picture;
	
	resample_changed = ost->resample_width != dec->width || ost->resample_height != dec->height
			|| ost->resample_pix_fmt != dec->pix_fmt;
	if (resample_changed) {
		ost->resample_width = dec->width;
		ost->resample_height = dec->height;
		ost->resample_pix_fmt = dec->pix_fmt;
	}
}

bool wxFfmpegMediaTranscoder::DoVideoOut(AVFormatContext *s, OutputStream *ost,
		InputStream *ist, AVFrame *in_picture, int *frame_size,
		int bit_buffer_size, uint8_t* bit_buffer) {
	int nb_frames, i, ret, format_video_sync;
	AVFrame *final_picture;
	AVCodecContext *enc, *dec;
	double sync_ipts;
	
	enc = ost->st->codec;
	dec = ist->st->codec;
	
	sync_ipts = get_sync_ipts(ost) / av_q2d(enc->time_base);

	/* by default, we output a single frame */
	nb_frames = 1;
	
	*frame_size = 0;
	
	format_video_sync = video_sync_method;
	if (format_video_sync < 0)
		format_video_sync = (s->oformat->flags & AVFMT_VARIABLE_FPS) ? 2 : 1;
	
	if (format_video_sync) {
		double vdelta = sync_ipts - ost->sync_opts;
		//FIXME set to 0.5 after we fix some dts/pts bugs like in avidec.c
		if (vdelta < -1.1)
			nb_frames = 0;
		else if (video_sync_method == 2) {
			if (vdelta<=-0.6){
				nb_frames=0;
			} else if(vdelta>0.6)
				ost->sync_opts= lrintf(sync_ipts);
		}else if (vdelta > 1.1)
			nb_frames = lrintf(vdelta);
//fprintf(stderr, "vdelta:%f, ost->sync_opts:%"PRId64", ost->sync_ipts:%f nb_frames:%d\n", vdelta, ost->sync_opts, get_sync_ipts(ost), nb_frames);
		if (nb_frames == 0){
			++nb_frames_drop;
			if (verbose>2)
				fprintf(stderr, "*** drop!\n");
		}else if (nb_frames > 1) {
			nb_frames_dup += nb_frames - 1;
			if (verbose>2)
				fprintf(stderr, "*** %d dup!\n", nb_frames-1);
		}
	}else
		ost->sync_opts= lrintf(sync_ipts);

	nb_frames= FFMIN(nb_frames, max_frames[AVMEDIA_TYPE_VIDEO] - ost->frame_number);
	if (nb_frames <= 0)
		return true;
	
	do_video_resample(ost, ist, in_picture, &final_picture);
	
	// duplicates frame if needed
	for (i=0; i<nb_frames; i++) {
		AVPacket pkt;
		av_init_packet(&pkt);
		pkt.stream_index= ost->index;
		
		if (s->oformat->flags & AVFMT_RAWPICTURE) {
            /* raw pictures are written as AVPicture structure to
               avoid any copies. We support temorarily the older
               method. */
			AVFrame* old_frame = enc->coded_frame;
			enc->coded_frame = dec->coded_frame; //FIXME/XXX remove this hack
			pkt.data= (uint8_t *)final_picture;
			pkt.size= sizeof(AVPicture);
			pkt.pts= av_rescale_q(ost->sync_opts, enc->time_base, ost->st->time_base);
			pkt.flags |= AV_PKT_FLAG_KEY;
			
			if (!WriteFrame(s, &pkt, ost->st->codec, ost->bitstream_filters))
				return false;
			enc->coded_frame = old_frame;
		} else {
			AVFrame big_picture;
			
			big_picture= *final_picture;
			// better than nothing: use input picture interlaced settings
			big_picture.interlaced_frame = in_picture->interlaced_frame;
			if (ost->st->codec->flags & (CODEC_FLAG_INTERLACED_DCT|CODEC_FLAG_INTERLACED_ME)) {
				if(top_field_first == -1)
					big_picture.top_field_first = in_picture->top_field_first;
				else
					big_picture.top_field_first = top_field_first;
			}
			
			// handles sameq here. This is not correct because it may not be a global option
			big_picture.quality = same_quant ? (int) ist->st->quality : (int) ost->st->quality;
			if (!me_threshold)
				big_picture.pict_type = (AVPictureType) 0;
			big_picture.pts= ost->sync_opts;
			if (ost->forced_kf_index < ost->forced_kf_count &&
				big_picture.pts >= ost->forced_kf_pts[ost->forced_kf_index]) {
				big_picture.pict_type = AV_PICTURE_TYPE_I;
				ost->forced_kf_index++;
			}
			ret = avcodec_encode_video(enc, bit_buffer, bit_buffer_size, &big_picture);
			if (ret < 0) {
				wxLogError(wxT("Video encoding failed"));
				return false;
			}
			if (ret>0) {
				pkt.data= bit_buffer;
				pkt.size= ret;
				if(enc->coded_frame->pts != (int64_t) AV_NOPTS_VALUE)
					pkt.pts= av_rescale_q(enc->coded_frame->pts, enc->time_base, ost->st->time_base);
				
				if(enc->coded_frame->key_frame)
					pkt.flags |= AV_PKT_FLAG_KEY;
				WriteFrame(s, &pkt, ost->st->codec, ost->bitstream_filters);
				*frame_size = ret;
				video_size += ret;
				// if two pass, output log
				if (ost->logfile && enc->stats_out) {
					fprintf(ost->logfile, "%s", enc->stats_out);
				}
			}
		}
		ost->sync_opts++;
		ost->frame_number++;
	}
	return true;
}

bool wxFfmpegMediaTranscoder::DoSubtitleOut(AVFormatContext *s, OutputStream *ost,
		InputStream *ist, AVSubtitle *sub, int64_t pts) {
	static uint8_t *subtitle_out= NULL;
	int subtitle_out_max_size = 1024 * 1024;;
	int subtitle_out_size, nb, i;
	AVCodecContext *enc;
	AVPacket pkt;
	
	if (pts == (int64_t) AV_NOPTS_VALUE) {
		wxLogError(wxT("Subtitle packets must have a pts"));
		if (exit_on_error)
			return false;
		return true;
	}
	
	enc = ost->st->codec;
	
	if (!subtitle_out) {
		subtitle_out = (uint8_t*) av_malloc(subtitle_out_max_size);
	}
	
	// Note: DVB subtitle need one packet to draw them and one other packet to clear them
	// XXX: signal it in the codec context ?
	if (enc->codec_id == CODEC_ID_DVB_SUBTITLE)
		nb = 2;
	else
		nb = 1;
	
	for (i = 0; i < nb; i++) {
		sub->pts = av_rescale_q(pts, ist->st->time_base, AV_TIME_BASE_Q);
		// start_display_time is required to be 0
		sub->pts += av_rescale_q(sub->start_display_time, (AVRational){1, 1000}, AV_TIME_BASE_Q);
		sub->end_display_time -= sub->start_display_time;
		sub->start_display_time = 0;
		subtitle_out_size = avcodec_encode_subtitle(enc, subtitle_out,
													subtitle_out_max_size, sub);
		if (subtitle_out_size < 0) {
			wxLogError(wxT("Subtitle encoding failed"));
			return false;
		}

		av_init_packet(&pkt);
		pkt.stream_index = ost->index;
		pkt.data = subtitle_out;
		pkt.size = subtitle_out_size;
		pkt.pts = av_rescale_q(sub->pts, AV_TIME_BASE_Q, ost->st->time_base);
		if (enc->codec_id == CODEC_ID_DVB_SUBTITLE) {
			/* XXX: the pts correction is handled here. Maybe handling
			 it in the codec would be better */
			if (i == 0)
				pkt.pts += 90 * sub->start_display_time;
			else
				pkt.pts += 90 * sub->end_display_time;
		}
		if (!WriteFrame(s, &pkt, ost->st->codec, ost->bitstream_filters))
			return false;
	}
	return true;
}

void wxFfmpegMediaTranscoder::PrintReport(OutputFile *output_files,
		OutputStream *ost_table, int nb_output_streams, int is_last_report) {
	OutputStream *ost;
	AVFormatContext *oc;
	int64_t total_size;
	AVCodecContext *enc;
	int frame_number, vid, i;
	double bitrate;
	int64_t pts = INT64_MAX;
	static int64_t last_time = -1;
	static int qp_histogram[52];
	
	if (!is_last_report) {
		int64_t cur_time;
		// display the report every 0.5 seconds
		cur_time = av_gettime();
		if (last_time == -1) {
			last_time = cur_time;
			return;
		}
		if ((cur_time - last_time) < 500000)
			return;
		last_time = cur_time;
	}
	
	
	oc = output_files[0].ctx;
	total_size = avio_size(oc->pb);
	if (total_size < 0) // FIXME improve avio_size() so it works with non seekable output too
		total_size= avio_tell(oc->pb); 
	
	wxString msg;
	vid = 0;
	for (i=0; i<nb_output_streams; i++) {
		float q = -1;
		ost = &ost_table[i];
		enc = ost->st->codec;
		if (!ost->st->stream_copy && enc->coded_frame)
			q = enc->coded_frame->quality/(float)FF_QP2LAMBDA;
		if (vid && enc->codec_type == AVMEDIA_TYPE_VIDEO) {
			msg += wxString::Format(wxT("q=%2.1f "),
					!ost->st->stream_copy ?	enc->coded_frame->quality/(float)FF_QP2LAMBDA : -1);
		}
		if (!vid && enc->codec_type == AVMEDIA_TYPE_VIDEO) {
			float t = (av_gettime()-timer_start) / 1000000.0;
			
			frame_number = ost->frame_number;
			msg += wxString::Format(wxT("frame=%5d fps=%3d q=%3.1f "),
			frame_number, (t>1)?(int)(frame_number/t+0.5) : 0,
					!ost->st->stream_copy ? enc->coded_frame->quality/(float)FF_QP2LAMBDA : -1);
			if (is_last_report)
				msg += wxString::Format(wxT("L"));
			if (qp_hist) {
				int j;
				int qp = lrintf(q);
				if (qp>=0 && qp < (int) (FF_ARRAY_ELEMS(qp_histogram)))
					qp_histogram[qp]++;
				for (j=0; j<32; j++)
					msg += wxString::Format(wxT("%X"), (int)rint(log(qp_histogram[j]+1)/log(2)));
			}
			if (enc->flags&CODEC_FLAG_PSNR) {
				int j;
				double error, error_sum=0;
				double scale, scale_sum=0;
				char type[3]= { 'Y', 'U', 'V' };
				msg += wxString::Format(wxT("PSNR="));
				for (j=0; j<3; j++) {
					if (is_last_report) {
						error= enc->error[j];
						scale= enc->width*enc->height*255.0*255.0*frame_number;
					} else {
						error = enc->coded_frame->error[j];
						scale = enc->width*enc->height *255.0*255.0;
					}
					if (j)
						scale/=4;
					error_sum += error;
					scale_sum += scale;
					msg += wxString::Format(wxT("%c:%2.2f "), type[j], psnr(error/scale));
				}
				msg += wxString::Format(wxT("*:%2.2f "), psnr(error_sum/scale_sum));
			}
			vid = 1;
		}
		// compute min output value
		pts = FFMIN(pts, av_rescale_q(ost->st->pts.val, ost->st->time_base, AV_TIME_BASE_Q));
	}
	
	if (verbose || is_last_report) {
		int hours, mins, secs, us;
		secs = pts / AV_TIME_BASE;
		us = pts % AV_TIME_BASE;
		mins = secs / 60;
		secs %= 60;
		hours = mins / 60;
		mins %= 60;

		bitrate = pts ? total_size * 8 / (pts / 1000.0) : 0;
		
		msg += wxString::Format(wxT("size=%8.0fkB time=%02d:%02d:%02d.%02d bitrate=%6.1fkbits/s"),
				(double) total_size / 1024, hours, mins, secs, (100 * us) / AV_TIME_BASE, bitrate);
		
		if (nb_frames_dup || nb_frames_drop)
			msg += wxString::Format(wxT(" dup=%d drop=%d"), nb_frames_dup, nb_frames_drop);
		
		if (verbose >= 0)
			wxLogMessage(msg);
	}
	
	if (is_last_report && verbose >= 0) {
		int64_t raw= audio_size + video_size + extra_size;
		wxLogMessage(wxT("video:%1.0fkB audio:%1.0fkB global headers:%1.0fkB muxing overhead %f%%"),
				video_size/1024.0,
				audio_size/1024.0,
				extra_size/1024.0,
				100.0*(total_size - raw)/raw);
	}
	wxYieldIfNeeded();
}

void wxFfmpegMediaTranscoder::End() {
	// close files
	for (int i=0; i<nb_output_files; i++) {
		AVFormatContext *s = output_files[i].ctx;
		if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb)
			avio_close(s->pb);
		avformat_free_context(s);
		av_dict_free(&output_files[i].opts);
	}
	for (int i=0; i<nb_input_files; i++) {
		av_close_input_file(input_files[i].ctx);
	}
	for (int i = 0; i < nb_input_streams; i++)
		av_dict_free(&input_streams[i].opts);
	
	av_freep(&intra_matrix);
	av_freep(&inter_matrix);
	
	if (vstats_file)
		fclose(vstats_file);
	av_freep(&vstats_filename);
	
	av_free(streamid_map);
	nb_streamid_map = 0;
	av_freep(&stream_maps);
	nb_stream_maps = 0;
	
	av_freep(&input_streams);
	nb_input_streams = 0;
	av_freep(&input_files);
	nb_input_files = 0;
	av_freep(&output_streams);
	nb_output_streams = 0;
	av_freep(&output_files);
	nb_output_files = 0;
	
	av_freep(&audio_buf);
	av_freep(&audio_out);
	allocated_audio_out_size = allocated_audio_buf_size = 0;
	av_freep(&samples);
	nb_meta_data_maps = 0;
	
	av_dict_free(&format_opts);
	av_dict_free(&codec_opts);
}
