//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2018 by PDSoft (Attila Padar)                *
//*                http://mpxplay.sourceforge.net                          *
//*                  email: mpxplay@freemail.hu                            *
//**************************************************************************
//*  This program is distributed in the hope that it will be useful,       *
//*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *
//*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                  *
//*  Please contact with the author (with me) if you want to use           *
//*  or modify this source.                                                *
//**************************************************************************
//function: FFMPEG library parser

#include "ffmpgdec.h"
#include <control/control.h>
#include <diskdriv/diskdriv.h>

#ifdef MPXPLAY_LINK_INFILE_FF_MPEG

extern unsigned int playcontrol;
#ifdef MPXPLAY_LINK_DXVA
extern unsigned long mpxplay_config_videoplayer_control;
#endif
extern int mpxplay_control_startup_programid_select;
extern char mpxplay_config_video_preferred_language[MPXPLAY_STREAMTYPEINDEX_NUM][MPXINI_MAX_CHARDATA_LEN];
extern char *mpxplay_config_video_preffered_audiotype_ptrs[MPXINI_MAX_EXTRA_FILETYPES];
extern unsigned long mpxplay_config_video_audiovisualization_type;

static void in_ffmpgdec_deinit_hwdec(AVCodecContext *codec_ctx);
static int inffmpg_streams_count(struct ffmpg_demuxer_data_s *ffmpi, int streamtype_index, int *select_stream_number, int *selected_ff_streamindex, AVCodec **codec_av_new);

//------------------------------------------------------------------------------------------------------------------
static void inffmpg_avsync_assign(struct ffmpg_demuxer_data_s *ffmpi, int64_t pts)
{
	ffmpi->avsync_timestamp = pts;
	ffmpi->avsync_clocktime = pds_gettimeu();
}

mpxp_bool_t in_ffmpgdec_content_has_programs(struct ffmpg_demuxer_data_s *ffmpi)
{
	if(ffmpi->fctx && ffmpi->fctx->programs && (ffmpi->fctx->nb_programs > 1))
		return TRUE;
	return FALSE;
}

// convert stream time based dts/pts to AV_TIME_BASEd
int64_t in_ffmpgdec_get_avpacket_ts_timestamp(struct ffmpg_demuxer_data_s *ffmpi, unsigned int streamtype_index, int64_t pkt_ts)
{
	AVStream *stream = (ffmpi && (streamtype_index < MPXPLAY_STREAMTYPEINDEX_NUM))? ffmpi->selected_avstream[streamtype_index] : NULL;
//	AVStream *stream = ffmpi->selected_avstream[streamtype_index];
	if(stream)
	{
		if(stream->time_base.num && stream->time_base.den)
			pkt_ts = pkt_ts * AV_TIME_BASE * stream->time_base.num / stream->time_base.den;
		else
			pkt_ts = pkt_ts * AV_TIME_BASE / 90000.0; // !!! MPEG like to us
		pkt_ts += INFFMPG_TIMESTAMP_SHIFT;  // first timestamp can be 0 (-audio_delay gives negative sync)
	}
	return pkt_ts; // in AV_TIME_BASE
}

static int64_t inffmpg_get_avpacket_pts(struct ffmpg_demuxer_data_s *ffmpi, unsigned int streamtype_index, AVPacket *pkt)
{
	int64_t pts = ((pkt->pts >= 0) && ((pkt->pts >= pkt->dts) || (pkt->dts < 0)))? pkt->pts : pkt->dts;
	return in_ffmpgdec_get_avpacket_ts_timestamp(ffmpi, streamtype_index, pts);
}

static int64_t inffmpg_select_avframe_pts(AVFrame *avframe)
{
	int64_t pts = (avframe->pts >= 0)? avframe->pts : (((avframe->pkt_dts >= 0) && ((avframe->pkt_dts <= avframe->pkt_pts) || (avframe->pkt_pts < 0)))? avframe->pkt_dts : avframe->pkt_pts);
	return pts;
}

// get pts for audio & video
static int64_t inffmpg_get_avframe_pts(struct ffmpg_demuxer_data_s *ffmpi, unsigned int streamtype_index, AVFrame *avframe)
{
	int64_t pts = inffmpg_select_avframe_pts(avframe);
	if(pts < 0)
		return 0;
	return in_ffmpgdec_get_avpacket_ts_timestamp(ffmpi, streamtype_index, pts);
}

static int64_t inffmpg_get_avframe_pts_corrected(struct ffmpg_demuxer_data_s *ffmpi, unsigned int streamtype_index, AVFrame *avframe)
{
	int64_t dts = inffmpg_get_avframe_pts(ffmpi, streamtype_index, avframe);
#ifdef INFFMPG_USE_VIDEOPTS_CORRECTON
	int64_t dts_video_last = ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_VIDEO];

	if((streamtype_index == MPXPLAY_STREAMTYPEINDEX_VIDEO) && (dts_video_last > INFFMPG_TIMESTAMP_SHIFT))  // TODO: wide test for non-streams too
	{
		AVStream *st = ffmpi->selected_avstream[streamtype_index];
		int64_t dts_file = dts;
		int64_t avduration = av_frame_get_pkt_duration(avframe);
		int64_t duration_us = (st && st->time_base.num && st->time_base.den)? (avduration * AV_TIME_BASE * st->time_base.num / st->time_base.den) : (avduration * AV_TIME_BASE);

		if(((duration_us <= 1000) || (duration_us >= (AV_TIME_BASE / 2))) && st && st->avg_frame_rate.num && st->avg_frame_rate.den)  // TODO: max 999fps / 2fps is allowed
			duration_us = AV_TIME_BASE * st->avg_frame_rate.den / st->avg_frame_rate.num;
		if(duration_us <= 1000) // TODO: max 999 fps (because some MKVs contain invalid 1000 fps value)
			duration_us = 40000; // TODO: fake 25 fps

		if((dts <= INFFMPG_TIMESTAMP_SHIFT) || (dts <= dts_video_last) || (dts > (dts_video_last + duration_us * 2))) // TODO: more tests for this workaround if video pts is not sequential
		{
			int64_t dts_audio = ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_AUDIO];
			unsigned int seq_corr = 1;

			if( (dts > INFFMPG_TIMESTAMP_SHIFT)
			 && ((dts < (dts_video_last - INFFMPG_TIMESTAMP_MAX_AVDIFFERENCE)) || (dts > (dts_video_last + INFFMPG_TIMESTAMP_MAX_AVDIFFERENCE)))
			 && (dts_audio > INFFMPG_TIMESTAMP_SHIFT)
			){
				int64_t dts_adiff = dts_video_last - dts_audio;

				if(dts_adiff < 0)
					dts_adiff = -dts_adiff;

				if(dts_adiff > INFFMPG_TIMESTAMP_MAX_AVDIFFERENCE) // TODO: more test for this workaround if video dts/pts is completely false
				{
					dts = dts_audio;
					seq_corr = 0;
					mpxplay_debugf(MPXPLAY_DEBUG_QUEUEGET,"get_avpacket_dts_corrected AVSYNC dts:%lld diff:%lld adi:%llu cd:%llu cp:%llu dur:%llu",
						dts, (dts - dts_video_last), dts_adiff, (avframe->pkt_dts), (avframe->pkt_pts), duration_us);
				}
				else
				{
//					mpxplay_debugf(MPXPLAY_DEBUG_QUEUEGET,"get_avpacket_dts_corrected NODIFF dts:%llu diff:%lld vdts:%llu vpts:%llu",
//							dts, (dts - dts_video_last),
//							((avpkt)? avpkt->dts : avframe->pkt_dts), ((avpkt)? avpkt->pts : avframe->pkt_pts));
				}
			}

			if(seq_corr){
				int64_t correction = duration_us >> 4; // a smaller correction to file/frame dts from calculated
				int64_t dvl = dts_video_last + duration_us;
				dts = dvl + ((dts <= dts_video_last)? -correction : correction);
			}
			mpxplay_debugf(MPXPLAY_DEBUG_QUEUEGET,"BADSEQ dr:%llu dts:%lld dtsf:%lld last:%lld diff:%lld df:%lld da:%lld dtsa:%lld pdts:%lld ppts:%lld",
				duration_us, dts, dts_file, dts_video_last, (dts - dts_video_last),
				(dts_file - dts_video_last), (dts_video_last - dts_audio), dts_audio, (avframe->pkt_dts), (avframe->pkt_pts));
		}
	}
#endif
	return dts; // in AV_TIME_BASE (usec)
}

int mpxplay_ffmpgdec_packetqueue_get(void *demuxer_data, mpxplay_packetqueue_t *packet_queue, mpxplay_packetlist_t **pktlist_elem_dest, int wait_ms)
{
	int retcode = -1;

	if(!packet_queue || (packet_queue->nb_packets <= 0))
		return retcode;

	if(PDS_THREADS_MUTEX_LOCK(&packet_queue->mutexhnd_queue, INFFMPG_MUTEX_QUEUEGET_TIMEOUT) != MPXPLAY_ERROR_OK)
		return retcode;

	do{
		mpxplay_packetlist_t *first_pktlist_elem = packet_queue->first_pkt;
		if (first_pktlist_elem){
			unsigned int pop_elem = 1;
			switch(first_pktlist_elem->packet_type)
			{
				case MPXPLAY_PACKETTYPE_AVCODECCTX:
					if(packet_queue->last_pkt)
						first_pktlist_elem = packet_queue->last_pkt; // we always give back the last pushed codec_ctx
					pop_elem = 0;  // we always keep the top elem(s) of codec queue
					retcode = MPXPLAY_ERROR_OK;
					if(packet_queue->streamtype_index == MPXPLAY_STREAMTYPEINDEX_AUDIO)
					{
						mpxplay_debugf(MPXPLAY_DEBUG_QUEUEGET,"packetqueue_get AVCODECCTX fpe:%8.8X", (unsigned int)first_pktlist_elem);
					}
					break;
				case MPXPLAY_PACKETTYPE_AVPACKET:
					{
						AVPacket *avpkt = (AVPacket *)(first_pktlist_elem->frame_pkt);
						struct ffmpg_demuxer_data_s *ffmpi;
						if(!avpkt || (packet_queue->streamtype_index >= MPXPLAY_STREAMTYPEINDEX_NUM))
							break;
						retcode = MPXPLAY_ERROR_OK;
						if(packet_queue->streamtype_index == MPXPLAY_STREAMTYPEINDEX_VIDEO)
							break;
						ffmpi = (struct ffmpg_demuxer_data_s *)demuxer_data;
						if(!ffmpi)
							break;
						if(first_pktlist_elem->flags & MPXPLAY_PACKETLISTFLAG_CLEARBUF)
							ffmpi->stream_last_pts[packet_queue->streamtype_index] = INFFMPG_INVALID_PTS;
						first_pktlist_elem->timestamp_us = inffmpg_get_avpacket_pts(ffmpi, packet_queue->streamtype_index, avpkt);
						if(first_pktlist_elem->flags & MPXPLAY_PACKETLISTFLAG_CLEARBUF)
						{
							mpxplay_debugf(MPXPLAY_DEBUG_QUEUEGET,"packetqueue_get AVPACKET %s i:%d nb:%d us:%llu diff:%lld dts:%llu pts:%llu",
								((first_pktlist_elem->flags & MPXPLAY_PACKETLISTFLAG_CLEARBUF)? "CLEAR" : ""),
								packet_queue->streamtype_index, packet_queue->nb_packets, first_pktlist_elem->timestamp_us,
								(first_pktlist_elem->timestamp_us - ffmpi->stream_last_pts[packet_queue->streamtype_index]),
								avpkt->dts, avpkt->pts);
						}
						//ffmpi->stream_last_pts[packet_queue->streamtype_index] = first_pktlist_elem->timestamp_us; // FIXME: ??? (audio and video use avframe pts, subtitle doesn't need this)
					}
					break;
				case MPXPLAY_PACKETTYPE_AVFRAME_AUDIO:
				case MPXPLAY_PACKETTYPE_AVFRAME_VIDEO:
					{
						AVFrame *avframe = (AVFrame *)(first_pktlist_elem->frame_pkt);
						struct ffmpg_demuxer_data_s *ffmpi;
						if(!avframe || (packet_queue->streamtype_index >= MPXPLAY_STREAMTYPEINDEX_NUM))
							break;
						first_pktlist_elem->timestamp_us = inffmpg_select_avframe_pts(avframe);
						retcode = MPXPLAY_ERROR_OK;
#if 0
						if(funcbit_test(first_pktlist_elem->flags, MPXPLAY_PACKETLISTFLAG_IGNORE_TS))
						{
							break;
						}
						ffmpi = (struct ffmpg_demuxer_data_s *)demuxer_data; // FIXME: demuxer_data can be different structure
						if(!ffmpi)
							break;
						if(first_pktlist_elem->flags & MPXPLAY_PACKETLISTFLAG_CLEARBUF)
							ffmpi->stream_last_pts[packet_queue->streamtype_index] = INFFMPG_INVALID_PTS;
						first_pktlist_elem->timestamp_us = inffmpg_get_avframe_pts_corrected(ffmpi, packet_queue->streamtype_index, avframe);
						if(first_pktlist_elem->flags & MPXPLAY_PACKETLISTFLAG_CLEARBUF)
						{
							mpxplay_debugf(MPXPLAY_DEBUG_QUEUEGET_AVFRAME,"packetqueue_get AVFRAME %s i:%d nb:%d us:%llu diff:%lld pts:%lld ppts:%llu pdts:%llu cpn:%d ts:%lld",
								((first_pktlist_elem->flags & MPXPLAY_PACKETLISTFLAG_CLEARBUF)? "CLEAR" : ""),
								packet_queue->streamtype_index, packet_queue->nb_packets, first_pktlist_elem->timestamp_us,
								(first_pktlist_elem->timestamp_us - ffmpi->stream_last_pts[packet_queue->streamtype_index]),
								avframe->pts, avframe->pkt_pts, avframe->pkt_dts, avframe->coded_picture_number,
								(pds_gettimeu() - ffmpi->avsync_clocktime + ffmpi->avsync_timestamp));
						}
						ffmpi->stream_last_pts[packet_queue->streamtype_index] = first_pktlist_elem->timestamp_us;
#endif
					}
					break;
			}
			if(pop_elem){
				packet_queue->first_pkt = first_pktlist_elem->next;
				if(!packet_queue->first_pkt)
					packet_queue->last_pkt = NULL;
				packet_queue->nb_packets--;
			}
			*pktlist_elem_dest = first_pktlist_elem;
			break;
		}else{
			packet_queue->nb_packets = 0;
		}
		if(wait_ms <= 0) {
			retcode = -1;
			break;
		}
	    //pds_threads_cond_timedwait(&packet_queue->condhnd_queue, &packet_queue->mutexhnd_queue, wait_ms);
		wait_ms = 0;
	}while(1);

	PDS_THREADS_MUTEX_UNLOCK(&packet_queue->mutexhnd_queue);

	return retcode;
}

int mpxplay_ffmpgdec_packetqueue_put(mpxplay_packetqueue_t *packet_queue, unsigned int packet_type, unsigned int packet_flags, void *codec_ctx, void *pkt_new)
{
	mpxplay_packetlist_t *pktlist_new;
	int lock_result;

	if(!packet_queue || (packet_type >= MPXPLAY_PACKETTYPE_MAX))
		return -1;

	pktlist_new = av_mallocz(sizeof(mpxplay_packetlist_t));
	if(!pktlist_new)
		return -1;

	pktlist_new->packet_type = packet_type;
	pktlist_new->flags = packet_flags;
	pktlist_new->codec_ctx = codec_ctx;

	if(packet_flags){
		mpxplay_debugf(MPXPLAY_DEBUG_QUEUEPUT,"mpxplay_ffmpgdec_packetqueue_put CLEAR i:%d t:%d nb:%d",
		packet_queue->streamtype_index, packet_type, packet_queue->nb_packets);
	}

	switch(packet_type)
	{
		case MPXPLAY_PACKETTYPE_AVCODECCTX:
			break;
		case MPXPLAY_PACKETTYPE_AVPACKET:
			if(pkt_new){
				AVPacket *pkt_clone;
				if(((AVPacket *)pkt_new)->size <= 0)
					goto err_out_pkt_put;
				pkt_clone = av_packet_clone((AVPacket *)pkt_new);
				if(!pkt_clone)
					goto err_out_pkt_put;
				pktlist_new->frame_pkt = (void *)pkt_clone;
			} // else we put NULL avpackets too (for clear buffer flag)
			break;
		case MPXPLAY_PACKETTYPE_AVFRAME_AUDIO:
		case MPXPLAY_PACKETTYPE_AVFRAME_VIDEO:
			pktlist_new->frame_pkt = pkt_new;
			break;
	}

	lock_result = PDS_THREADS_MUTEX_LOCK(&packet_queue->mutexhnd_queue, INFFMPG_MUTEX_TIMEOUT);

	if (packet_queue->last_pkt)
		packet_queue->last_pkt->next = pktlist_new;
	else
		packet_queue->first_pkt = pktlist_new;
	packet_queue->last_pkt = pktlist_new;
	packet_queue->nb_packets++;

	//pds_threads_cond_signal(&packet_queue->condhnd_queue);

	if(lock_result == MPXPLAY_ERROR_OK)
	{
		PDS_THREADS_MUTEX_UNLOCK(&packet_queue->mutexhnd_queue);
	}

	if(packet_queue->streamtype_index == MPXPLAY_STREAMTYPEINDEX_AUDIO){
		mpxplay_debugf(MPXPLAY_DEBUG_QUEUEPUT,"mpxplay_ffmpgdec_packetqueue_put END i:%d t:%d nb:%d",
				packet_queue->streamtype_index, packet_type, packet_queue->nb_packets);
	}

	return 0;

err_out_pkt_put:
	av_free(pktlist_new);
	return -1;
}

void mpxplay_ffmpgdec_queuelistelem_clear(mpxplay_packetlist_t *list_elem)
{
	if(!list_elem)
		return;
	switch(list_elem->packet_type)
	{
		case MPXPLAY_PACKETTYPE_AVCODECCTX:
			if(list_elem->codec_ctx){
				AVCodecContext *ctx = (AVCodecContext *)(list_elem->codec_ctx);
				list_elem->codec_ctx = NULL;
				in_ffmpgdec_deinit_hwdec(ctx);
				mpxplay_debugf(MPXPLAY_DEBUG_OPENCLSDTL, "mpxplay_ffmpgdec_queuelistelem_clear avcodec_free_context BEGIN ctx:%8.8X tht:%8.8X", (unsigned int)ctx, ctx->active_thread_type);
				avcodec_free_context(&ctx);
				//mpxplay_debugf(MPXPLAY_DEBUG_OPENCLSDTL, "mpxplay_ffmpgdec_queuelistelem_clear avcodec_free_context END");
			}
			break;
		case MPXPLAY_PACKETTYPE_AVPACKET:
			if(list_elem->frame_pkt)
				av_packet_free((AVPacket **)(&(list_elem->frame_pkt)));
			break;
		case MPXPLAY_PACKETTYPE_AVFRAME_AUDIO:
		case MPXPLAY_PACKETTYPE_AVFRAME_VIDEO:
			if(list_elem->frame_pkt)
				av_frame_free((AVFrame **)&(list_elem->frame_pkt));
			break;
	}
	pds_memset(list_elem, 0, sizeof(*list_elem));
	av_free(list_elem);
}

unsigned int mpxplay_ffmpgdec_packetqueue_clear(mpxplay_packetqueue_t *packet_queue, int flag_test)
{
	mpxplay_packetlist_t *list_elem;
	unsigned int found_next, found_flag = 0, retry = INFFMPG_QUEUE_PACKETNUM_MAX, sc;
	int lock_result;

	if(!packet_queue)
		return found_flag;

	lock_result = PDS_THREADS_MUTEX_LOCK(&packet_queue->mutexhnd_queue, INFFMPG_MUTEX_TIMEOUT);

	do{
		found_next = 0;
		if(flag_test){ // clear all elems till the last flagged elem
			list_elem = packet_queue->first_pkt;
			sc = 0;
			do{
				if(!list_elem)
					break;
				if((list_elem->flags & flag_test) && (list_elem != packet_queue->first_pkt)){
					found_next = 1;
					mpxplay_debugf(MPXPLAY_DEBUG_QUEUEGET,"mpxplay_ffmpgdec_packetqueue_clear SEARCH i:%d nb:%d sc:%d",
							packet_queue->streamtype_index, packet_queue->nb_packets, sc);
					break;
				}
				list_elem = list_elem->next;
				sc++;
			}while(1);
			if(!found_next)
				break;
		}

		sc = 0;
		do{
			list_elem = packet_queue->first_pkt;
			if(!list_elem){
				packet_queue->last_pkt = NULL;
				packet_queue->nb_packets = 0;
				break;
			}
			if(list_elem->flags & flag_test){
				if(!found_next){
					found_flag = 1;
					mpxplay_debugf(MPXPLAY_DEBUG_QUEUEGET,"mpxplay_ffmpgdec_packetqueue_clear CLEAR i:%d nb:%d sc:%d",
							packet_queue->streamtype_index, packet_queue->nb_packets, sc);
					break;
				}
			}
			found_next = 0;
			packet_queue->first_pkt = list_elem->next;
			packet_queue->nb_packets--;
			mpxplay_ffmpgdec_queuelistelem_clear(list_elem);
			sc++;
		}while(1);
	}while(flag_test && (--retry));

	if(flag_test && found_flag){
		mpxplay_debugf(MPXPLAY_DEBUG_QUEUEGET,"mpxplay_ffmpgdec_packetqueue_clear END sti:%d pt:%d nb:%d",
				packet_queue->streamtype_index, packet_queue->first_pkt->packet_type, packet_queue->nb_packets);
	}

	if(lock_result == MPXPLAY_ERROR_OK)
	{
		PDS_THREADS_MUTEX_UNLOCK(&packet_queue->mutexhnd_queue);
	}

	return found_flag;
}

// search for ctx (depend on packet type), clear list elems before it (made for codec_ctx consoldidation)
unsigned int mpxplay_ffmpgdec_queue_clear_to_elemctx(mpxplay_packetqueue_t *packet_queue, void *ctx)
{
	mpxplay_packetlist_t *list_elem;
	unsigned int found = 0;

	if(!packet_queue)
		return found;

	do{
		list_elem = packet_queue->first_pkt;
		if(!list_elem){
			packet_queue->last_pkt = NULL;
			packet_queue->nb_packets = 0;
			break;
		}
		if(ctx){
			switch(list_elem->packet_type){
				case MPXPLAY_PACKETTYPE_AVCODECCTX:
					if((list_elem->codec_ctx == ctx) || (packet_queue->nb_packets <= 1))  // we always keep the last elem in the codec_ctx queue
						found = 1;
					break;
				default:
					if(list_elem->frame_pkt == ctx)
						found = 1;
					break;
			}
		}
		if(found)
			break;
		packet_queue->first_pkt = list_elem->next;
		packet_queue->nb_packets--;
		mpxplay_ffmpgdec_queuelistelem_clear(list_elem);
	}while(!found);

	return found;
}

void in_ffmpgdec_packet_queues_clear(struct ffmpg_demuxer_data_s *ffmpi, mpxplay_packetqueue_t *packet_queue)
{
	int i;
	for(i = 0; i < MPXPLAY_STREAMTYPEINDEX_NUM; i++, packet_queue++)
		mpxplay_ffmpgdec_packetqueue_clear(packet_queue, 0);
}

void mpxplay_ffmpgdec_packetqueue_init(mpxplay_packetqueue_t *packet_queue, unsigned int streamtype_index)
{
	if(!packet_queue)
		return;
	pds_memset(packet_queue, 0, sizeof(mpxplay_packetqueue_t));
	packet_queue->streamtype_index = streamtype_index;
	pds_threads_mutex_new(&packet_queue->mutexhnd_queue);
	//pds_threads_cond_new(&packet_queue->condhnd_queue);
}

void mpxplay_ffmpgdec_packetqueue_close(mpxplay_packetqueue_t *packet_queue)
{
	if(!packet_queue)
		return;
	mpxplay_ffmpgdec_packetqueue_clear(packet_queue, 0);
	pds_threads_mutex_del(&packet_queue->mutexhnd_queue);
	//pds_threads_cond_del(&packet_queue->condhnd_queue);
}

void in_ffmpgdec_packet_queues_init(mpxplay_packetqueue_t *packet_queue)
{
	int i;
	for(i = 0; i < MPXPLAY_STREAMTYPEINDEX_NUM; i++, packet_queue++)
		mpxplay_ffmpgdec_packetqueue_init(packet_queue, i);
}

void in_ffmpgdec_packet_queues_close(struct ffmpg_demuxer_data_s *ffmpi, mpxplay_packetqueue_t *packet_queue)
{
	int i;
	for(i = 0; i < MPXPLAY_STREAMTYPEINDEX_NUM; i++, packet_queue++)
		mpxplay_ffmpgdec_packetqueue_close(packet_queue);
}

//------------------------------------------------------------------------------------------------------------------
#define INFFMPG_STREAMINFO_MAX_LEN 128

static struct mpxplay_programinfo_t *inffmpg_programinfolist_fill(struct ffmpg_demuxer_data_s *ffmpi)
{
	struct mpxplay_programinfo_t *program_list;
	int i, pc;

	if(!in_ffmpgdec_content_has_programs(ffmpi))
		return NULL;

	program_list = pds_calloc(1, sizeof(*program_list));
	if(!program_list)
		return program_list;

	for(i = 0; i < ffmpi->fctx->nb_programs; i++)
	{
		AVProgram *prg = ffmpi->fctx->programs[i];
		if(prg)
		{
			AVDictionaryEntry *t = av_dict_get(prg->metadata, "service_name", NULL, 0); // TODO: check, program elements without service names are ignored!
			if(t && t->value)
				program_list->nb_programs++;  // number of valid programs
		}
	}

	if(program_list->nb_programs <= 1)
		goto err_out_ipf;

	program_list->program_names = pds_calloc(program_list->nb_programs, sizeof(program_list->program_names[0]));
	program_list->program_ids = pds_calloc(program_list->nb_programs, sizeof(program_list->program_ids[0]));
	if(!program_list->program_names || !program_list->program_ids)
		goto err_out_ipf;

	for(i = 0, pc = 0; (i < ffmpi->fctx->nb_programs) && (pc < program_list->nb_programs); i++)
	{
		AVProgram *prg = ffmpi->fctx->programs[i];
		AVDictionaryEntry *t;

		if(!prg)
			continue;

		t = av_dict_get(prg->metadata, "service_name", NULL, 0);
		if(!t || !t->value)
			continue;

		if(prg->id == ffmpi->program_number_current)
			program_list->selected_prognum = pc;
		program_list->program_ids[pc] = prg->id;
		program_list->program_names[pc] = malloc(pds_strlen(t->value) + 1);
		if(program_list->program_names[pc])
			pds_strcpy(program_list->program_names[pc], t->value);
		pc++;
	}

	return program_list;

err_out_ipf:
	if(program_list){
		pds_free(program_list->program_names);
		pds_free(program_list->program_ids);
		pds_free(program_list);
	}
	return NULL;
}

static struct mpxplay_streaminfo_t *inffmpg_streaminfolist_fill(struct ffmpg_demuxer_data_s *ffmpi)
{
	struct mpxplay_streaminfo_t *stream_list = pds_calloc(1, sizeof(*stream_list));
	int i, j;
	if(!stream_list)
		return stream_list;
	for(i = 0; i < MPXPLAY_STREAMTYPEINDEX_NUM; i++)
	{
		unsigned int nb_streams;
		int stream_number = 0, stream_ff_idx = INFFMPG_STREAM_INDEX_DISABLE;
		inffmpg_streams_count(ffmpi, i, &stream_number, &stream_ff_idx, NULL);
		nb_streams = ffmpi->nb_streams[i];
		mpxplay_debugf(MPXPLAY_DEBUG_STRMLIST, "inffmpg_streams %d n:%d", i, nb_streams);
		if(nb_streams)
		{
			if(i == MPXPLAY_STREAMTYPEINDEX_SUBTITLE)
				nb_streams++; // "Disable" selection
			stream_list->stream_names[i] = pds_calloc(nb_streams, sizeof(stream_list->stream_names[i]));
			stream_list->stream_idx[i] = pds_calloc(nb_streams, sizeof(stream_list->stream_idx[i]));
			if(stream_list->stream_names[i] && stream_list->stream_idx[i])
			{
				int j = 0;
				stream_list->nb_entries[i] = nb_streams;
				stream_list->selected_stream_index[i] = (ffmpi->selected_ff_stream_index[i] == INFFMPG_INVALID_STREAM_INDEX)? INFFMPG_STREAM_INDEX_DISABLE : ffmpi->selected_ff_stream_index[i];
				if(i == MPXPLAY_STREAMTYPEINDEX_SUBTITLE)
				{
					stream_list->stream_names[i][0] = pds_malloc(INFFMPG_STREAMINFO_MAX_LEN);
					pds_strcpy(stream_list->stream_names[i][0], "S:  Disable");
					stream_list->stream_idx[i][0] = INFFMPG_STREAM_INDEX_DISABLE;
					j++;
				}
				stream_number = 0;
				for( ;j < nb_streams; j++, stream_number++)
				{
					int sn_save;
					AVStream *st;
					AVDictionaryEntry *t;
					char language[32] = "", bit_rate_str[32] = "", channel_str[32];

					sn_save = stream_number;
					stream_ff_idx = INFFMPG_STREAM_INDEX_DISABLE;
					if(inffmpg_streams_count(ffmpi, i, &stream_number, &stream_ff_idx, NULL) <= 0)
					{
						mpxplay_debugf(MPXPLAY_DEBUG_WARNING, "inffmpg_streams_count FAILED %d", i);
						break;
					}
					if((sn_save > stream_number) || (stream_ff_idx < 0) || (stream_ff_idx >= ffmpi->fctx->nb_streams))
					{
						mpxplay_debugf(MPXPLAY_DEBUG_WARNING, "inffmpg_streams_count stream_ff_idx FAILED i:%d sn:%d idx:%d", i, stream_number, stream_ff_idx);
						break;
					}

					st = ffmpi->fctx->streams[stream_ff_idx];
					if(!st)
					{
						continue;
					}

					stream_list->stream_names[i][j] = pds_malloc(INFFMPG_STREAMINFO_MAX_LEN);
					if(!stream_list->stream_names[i][j])
					{
						break;
					}

					t = av_dict_get(st->metadata, "language", NULL, 0);

					//mpxplay_debugf(MPXPLAY_DEBUG_STRMLIST, "inffmpg_streaminfolist_fill i:%d ffi:%d", i, stream_ff_idx);

					if(t && t->value && t->value[0] && ((i == MPXPLAY_STREAMTYPEINDEX_SUBTITLE) || (pds_stricmp(t->value,"und") != 0)))
						snprintf(language, sizeof(language), " [%s]", t->value);

					if(st->codecpar)
					{
						int bit_rate = ((st->codecpar->bit_rate)? st->codecpar->bit_rate : st->codec->bit_rate) / 1000, channel_num;
						if(bit_rate > 0)
							snprintf(bit_rate_str, sizeof(bit_rate_str), ", %d kb/s", bit_rate);
						switch(i)
						{
							case MPXPLAY_STREAMTYPEINDEX_AUDIO:
								channel_num = st->codecpar->channels;
								switch(channel_num){
									case 1: pds_strcpy(channel_str, "Mono"); break;
									case 2: pds_strcpy(channel_str, "Stereo"); break;
									case 6: pds_strcpy(channel_str, "5.1 chans"); break;
									default: snprintf(channel_str, sizeof(channel_str), "%d chans", channel_num); break;
								}
								snprintf(stream_list->stream_names[i][j], INFFMPG_STREAMINFO_MAX_LEN, "A: %s %s, %d Hz, %s%s",
									language, avcodec_get_name(st->codecpar->codec_id), st->codecpar->sample_rate, channel_str, bit_rate_str);
								break;
							case MPXPLAY_STREAMTYPEINDEX_VIDEO:
								snprintf(stream_list->stream_names[i][j], INFFMPG_STREAMINFO_MAX_LEN, "V: %s %s, %dx%d, %2.2f fps%s",
									language, avcodec_get_name(st->codecpar->codec_id),
									st->codecpar->width, st->codecpar->height,
									(st->avg_frame_rate.num && st->avg_frame_rate.den)? (float)(float)st->avg_frame_rate.num / (float)st->avg_frame_rate.den : (float)0,
									bit_rate_str);
								break;
							case MPXPLAY_STREAMTYPEINDEX_SUBTITLE:
								snprintf(stream_list->stream_names[i][j], INFFMPG_STREAMINFO_MAX_LEN, "S: %s", language);
								break;
						}
					}
					else
					{
						char *shorttypename = (i == MPXPLAY_STREAMTYPEINDEX_AUDIO)? "A:" : ((i == MPXPLAY_STREAMTYPEINDEX_VIDEO)? "V:" : "S:");
						snprintf(stream_list->stream_names[i][j], INFFMPG_STREAMINFO_MAX_LEN, "%s Track %d%s", shorttypename, (j + 1), language); // TODO: better stream names
					}
					stream_list->stream_idx[i][j] = stream_ff_idx; // FIXME: stream_idx is used in context menu
					mpxplay_debugf(MPXPLAY_DEBUG_STRMLIST, "MPXPLAY_INFILE_CBKCTRL_GET_STREAMLIST %d/%d %d/%d %s", i, j, stream_number, stream_ff_idx, stream_list->stream_names[i][j]);
				}
				if(j <= ((i == MPXPLAY_STREAMTYPEINDEX_SUBTITLE)? 1 : 0))  // number of streams has changed or no valid stream found
				{
					stream_list->nb_entries[i] = 0;
					stream_list->selected_stream_index[i] = INFFMPG_STREAM_INDEX_DISABLE;
					for(j = 0; j < nb_streams; j++)
						if(stream_list->stream_names[i][j])
							pds_free(stream_list->stream_names[i][j]);
					pds_free(stream_list->stream_names[i]);
					pds_free(stream_list->stream_idx[i]);
					stream_list->stream_names[i] = NULL;
					stream_list->stream_idx[i] = NULL;
				}
			}
		}
	}
	return stream_list;
}

static void inffmpg_pictureinfo_metadata_update(struct ffmpg_demuxer_data_s *ffmpi, AVDictionary *metadata)
{
	if((ffmpi->flags & INFFMPG_FLAG_STILLPICTURE) && ffmpi->miis)
	{
		AVDictionaryEntry *e_make = av_dict_get(metadata, "Make", NULL, 0);
		AVDictionaryEntry *e_model = av_dict_get(metadata, "Model", NULL, 0);
		AVDictionaryEntry *e_fnum = av_dict_get(metadata, "FNumber", NULL, 0);
		AVDictionaryEntry *e_etime = av_dict_get(metadata, "ExposureTime", NULL, 0);
		AVDictionaryEntry *e_iso = av_dict_get(metadata, "ISOSpeedRatings", NULL, 0);
		int len = -1, val1, val2;
		float f_fnum = 0.0f, f_etime = 0.0f;
		char str[256];

		if(((!e_make || !e_make->value) && (!e_model || !e_model->value)) || !e_fnum || !e_etime || !e_fnum->value || !e_etime->value)
			return;

		if(e_fnum){
			val1 = val2 = 1;
			sscanf(e_fnum->value, "%d:%d", &val1, &val2);
			if(val2 > 0)
				f_fnum = (float) val1 / (float)val2;
		}
		if(e_etime){
			val1 = val2 = 1;
			sscanf(e_etime->value, "%d:%d", &val1, &val2);
			if(val2 > 0)
				f_etime = 1.0f / ((float) val1 / (float)val2);
		}
		snprintf(str, sizeof(str), "%s %s, F%.1f, 1/%.0f, ISO%d", ((e_make)? e_make->value : ""), ((e_model)? e_model->value : ""), f_fnum, f_etime, ((e_iso)? pds_atol(e_iso->value) : 0));
		ffmpi->miis->control_cb(ffmpi->fbds,MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_PACKFUNCTTI3I(MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_PUT,MPXPLAY_TEXTCONV_TYPE_UTF8,I3I_COMMENT),str,&len);
	}
}

#ifdef MPXPLAY_LINK_DXVA
static enum AVPixelFormat in_ffmpgdec_GetHwFormat(AVCodecContext *s, const enum AVPixelFormat *pix_fmts)
{
    InputStream* ist = (InputStream*)s->opaque;
    ist->active_hwaccel_id = HWACCEL_DXVA2;
    ist->hwaccel_pix_fmt = AV_PIX_FMT_DXVA2_VLD;
    return ist->hwaccel_pix_fmt;
}
#endif

static void in_ffmpgdec_deinit_hwdec(AVCodecContext *codec_ctx)
{
#ifdef MPXPLAY_LINK_DXVA
	if(codec_ctx->opaque)  // TODO: !!! assumed InputStream / videohwdec_stream
	{
		InputStream *videohwdec_stream = (InputStream *)codec_ctx->opaque;
		if(videohwdec_stream->hwaccel_uninit && videohwdec_stream->dec_ctx)
			videohwdec_stream->hwaccel_uninit(videohwdec_stream->dec_ctx);
		av_freep((void *)&codec_ctx->opaque);
	}
#endif
}

static int in_ffmpgdec_init_hwdec(AVCodecContext *codec_ctx_new, const AVCodec *codec_av_new)
{
	int dxva_result = -1;

#ifdef MPXPLAY_LINK_DXVA
	if(funcbit_test(mpxplay_config_videoplayer_control, MPXPLAY_CONFIG_VIDEOPLAYERCONTROL_USEHWDECODING))
	{
		if(codec_ctx_new->coded_width <= 0)
			codec_ctx_new->coded_width = codec_ctx_new->width;
		if(codec_ctx_new->coded_height <= 0)
			codec_ctx_new->coded_height = codec_ctx_new->height;
		if((codec_ctx_new->coded_width > 0) && (codec_ctx_new->coded_height > 0)
		 && ( !funcbit_test(mpxplay_config_videoplayer_control, MPXPLAY_CONFIG_VIDEOPLAYERCONTROL_DISABLESDHWDEC)
			  || (codec_ctx_new->coded_width > 720) || (codec_ctx_new->coded_height > 540))
		){
			InputStream *videohwdec_stream = av_mallocz(sizeof(InputStream));
			if(videohwdec_stream){
				videohwdec_stream->hwaccel_id = HWACCEL_AUTO;
				videohwdec_stream->hwaccel_device = "dxva2";
				videohwdec_stream->dec = codec_av_new;
				videohwdec_stream->dec_ctx = codec_ctx_new;
				codec_ctx_new->opaque = (void *)videohwdec_stream;
				dxva_result = dxva2_init(codec_ctx_new);
				if(dxva_result == 0){
					codec_ctx_new->get_buffer2 = videohwdec_stream->hwaccel_get_buffer;
					codec_ctx_new->get_format = in_ffmpgdec_GetHwFormat;
					codec_ctx_new->thread_safe_callbacks = 1;
					codec_ctx_new->thread_count = 1;
				}else{
					if(videohwdec_stream->hwaccel_uninit && videohwdec_stream->dec_ctx)
						videohwdec_stream->hwaccel_uninit(videohwdec_stream->dec_ctx);
					codec_ctx_new->opaque = NULL;
					av_free((void *)videohwdec_stream);
				}
			}
		}
	}
#endif

	return dxva_result;
}

int in_ffmpgdec_infile_callback(void *demuxer_data, unsigned int control, void *arg1, int *arg2, int timeout_ms)
{
	struct ffmpg_demuxer_data_s *ffmpi = demuxer_data;
	int retcode = MPXPLAY_ERROR_INFILE_NODATA;
	mpxplay_packetlist_t *pktlist_elem;
	mpxp_uint32_t arg1_num, arg2_num;

	if(!ffmpi)
		return retcode;
	if(!arg1)
		return retcode;

	if(timeout_ms != INFFMPG_MUTEXTIME_NOLOCK)
	{
		retcode = PDS_THREADS_MUTEX_LOCK(&ffmpi->mutexhnd_ffmpeg, timeout_ms);
		if(retcode != MPXPLAY_ERROR_OK){
			if(timeout_ms > 10){
				mpxplay_debugf(MPXPLAY_DEBUG_WARNING, "in_ffmpgdec_infile_callback pds_threads_mutex_lock FAILED cmd:%8.8X pd:%8.8X ret:%d tms:%d", control, (int)demuxer_data, retcode, timeout_ms);
			}
			return retcode;
		}
	}

	switch(control){
		case MPXPLAY_INFILE_CBKCTRL_GET_PROGRAMLIST:
			*((struct mpxplay_programinfo_t **)arg1) = inffmpg_programinfolist_fill(ffmpi);
			retcode = MPXPLAY_ERROR_OK;
			break;
		case MPXPLAY_INFILE_CBKCTRL_GET_STREAMLIST:
			*((struct mpxplay_streaminfo_t **)arg1) = inffmpg_streaminfolist_fill(ffmpi);
			retcode = MPXPLAY_ERROR_OK;
			break;
		case MPXPLAY_INFILE_CBKCTRL_GET_TIMESYNC:
			if( (ffmpi->avsync_timestamp < 0)
			 && (!ffmpi->codec_ctx[MPXPLAY_STREAMTYPEINDEX_AUDIO] || (ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].nb_packets >= INFFMPG_QUEUE_PACKETNUM_MAX))
			){
				inffmpg_avsync_assign(ffmpi, ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_VIDEO]);
				mpxplay_debugf(MPXPLAY_DEBUG_TIMESTAMP, "in_ffmpgdec_infile_callback TIMESYNC: %llu", ffmpi->avsync_timestamp);
			}
			if(ffmpi->avsync_timestamp >= 0){
				*((mpxp_int64_t *)arg1) = pds_gettimeu() - ffmpi->avsync_clocktime + ffmpi->avsync_timestamp;
				//mpxplay_debugf(MPXPLAY_DEBUG_TIMESTAMP, "in_ffmpgdec_infile_callback TIMESTAMP: %llu vt:%llu", *((mpxp_int64_t *)arg1), ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_VIDEO]);
				retcode = MPXPLAY_ERROR_OK;
			}
			break;
		case MPXPLAY_INFILE_CBKCTRL_GET_VIDEOTIMESTAMP:
			*((mpxp_int64_t *)arg1) = ffmpi->avsync_videotimestamp;
			mpxplay_debugf(MPXPLAY_DEBUG_TIMESTAMP, "in_ffmpgdec_infile_callback GET VIDEOTIMESTAMP: %llu", ffmpi->avsync_videotimestamp);
			retcode = MPXPLAY_ERROR_OK;
			break;
		case MPXPLAY_INFILE_CBKCTRL_SET_VIDEOTIMESTAMP:
			ffmpi->avsync_videotimestamp = inffmpg_get_avframe_pts_corrected(ffmpi, MPXPLAY_STREAMTYPEINDEX_VIDEO, (AVFrame *)arg1);
			mpxplay_debugf(MPXPLAY_DEBUG_TIMESTAMP, "in_ffmpgdec_infile_callback SET VIDEOTIMESTAMP: %llu", ffmpi->avsync_videotimestamp);
			retcode = MPXPLAY_ERROR_OK;
			break;
		case MPXPLAY_INFILE_CBKCTRL_SET_SELECT_PROGRAM:
			ffmpi->select_new_program_number = *((mpxp_uint32_t *)arg1);
			retcode = MPXPLAY_ERROR_OK;
			mpxplay_debugf(MPXPLAY_DEBUG_PRGCHG, "in_ffmpgdec_infile_callback SELECT_PROGRAM %d", ffmpi->select_new_program_number);
			break;
		case MPXPLAY_INFILE_CBKCTRL_SET_VIDFRAME_METADATA:
			inffmpg_pictureinfo_metadata_update(ffmpi, (AVDictionary *)arg1);
			retcode = MPXPLAY_ERROR_OK;
			break;
		default:
			if(!arg2)  // other controls need 2 arguments
				goto err_out_cbk;
	}

	switch(control)
	{
		case MPXPLAY_INFILE_CBKCTRL_GET_CORRECTEDVTIMESTAMP:
			//ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_VIDEO] = INFFMPG_INVALID_PTS;
			*((mpxp_int64_t *)arg2) = inffmpg_get_avframe_pts_corrected(ffmpi, MPXPLAY_STREAMTYPEINDEX_VIDEO, ((AVFrame *)arg1));
			//if(first_pktlist_elem->flags & MPXPLAY_PACKETLISTFLAG_CLEARBUF)
			{
				mpxplay_debugf(MPXPLAY_DEBUG_TIMESTAMP,"CBKCTRL_GET_CORRECTEDVTIMESTAMP us:%llu diff:%lld pts:%lld ppts:%lld pdts:%lld cpn:%d ts:%lld",
					*((mpxp_int64_t *)arg2), (*((mpxp_int64_t *)arg2) - ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_VIDEO]),
					((AVFrame *)arg1)->pts, ((AVFrame *)arg1)->pkt_pts, ((AVFrame *)arg1)->pkt_dts, ((AVFrame *)arg1)->coded_picture_number,
					(pds_gettimeu() - ffmpi->avsync_clocktime + ffmpi->avsync_timestamp));
			}
			ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_VIDEO] = *((mpxp_int64_t *)arg2);
			retcode = MPXPLAY_ERROR_OK;
			break;
		case MPXPLAY_INFILE_CBKCTRL_SET_SELECT_STREAM:
			arg1_num = *((mpxp_uint32_t *)arg1);
			//mpxplay_debugf(MPXPLAY_DEBUG_CALLBACK, "MPXPLAY_INFILE_CBKCTRL_SET_SELECT_STREAM %d -> %d", arg1_num, *arg2);
			if(arg1_num < MPXPLAY_STREAMTYPEINDEX_NUM){
				arg2_num = *arg2;
				ffmpi->select_new_stream_number[arg1_num] = arg2_num;
				retcode = MPXPLAY_ERROR_OK;
			}
			break;
		case MPXPLAY_INFILE_CBKCTRL_GET_CODECCTXINIT_FFMPEG:
			arg2_num = *arg2;
			if(arg2_num < MPXPLAY_STREAMTYPEINDEX_NUM){
				//mpxplay_debugf(MPXPLAY_DEBUG_CALLBACK, "MPXPLAY_INFILE_CBKCTRL_GET_CODECCTXINIT_FFMPEG %d", arg2_num);
				pktlist_elem = NULL;
				mpxplay_ffmpgdec_packetqueue_get((void *)ffmpi, &ffmpi->codecctx_queues[arg2_num], &pktlist_elem, 0);
				if(pktlist_elem){
					if((pktlist_elem->flags & MPXPLAY_PACKETLISTFLAG_INIT) && pktlist_elem->codec_ctx){
						AVCodecContext *codec_ctx_new = (AVCodecContext *)pktlist_elem->codec_ctx;
						const AVCodec *codec_av_new = codec_ctx_new->codec;
						int dxva_result = -1;
						if(arg2_num == MPXPLAY_STREAMTYPEINDEX_VIDEO){
							dxva_result = in_ffmpgdec_init_hwdec(codec_ctx_new, codec_av_new);
							if(dxva_result != 0)
								codec_ctx_new->thread_count = (ffmpi->flags & INFFMPG_FLAG_STILLPICTURE)? 1 : pds_threads_get_number_of_processors();
						}
						mpxplay_debugf(MPXPLAY_DEBUG_OPENCLSDTL, "in_ffmpgdec_infile_callback avcodec_open2 BEGIN ffmpi:%8.8X ctx:%8.8X cav:%8.8X i:%d dxva:%d w:%d h:%d",
								(unsigned int)ffmpi, (unsigned int)codec_ctx_new, (unsigned int)codec_av_new, arg2_num, dxva_result, codec_ctx_new->coded_width, codec_ctx_new->coded_height);
						if(avcodec_open2(codec_ctx_new, codec_av_new, NULL) < 0)
						{
							mpxplay_debugf(MPXPLAY_DEBUG_ERROR, "in_ffmpgdec_infile_callback avcodec_open2 FAILED! ffmpi:%8.8X ctx:%8.8X cav:%8.8X i:%d",
								(unsigned int)ffmpi, (unsigned int)codec_ctx_new, (unsigned int)codec_av_new, arg2_num);
							break;
						}
						mpxplay_debugf(MPXPLAY_DEBUG_OPENCLSDTL, "in_ffmpgdec_infile_callback avcodec_open2 DONE ffmpi:%8.8X ctx:%8.8X cav:%8.8X i:%d dxva:%d w:%d h:%d",
								(unsigned int)ffmpi, (unsigned int)codec_ctx_new, (unsigned int)codec_av_new, arg2_num, dxva_result, codec_ctx_new->coded_width, codec_ctx_new->coded_height);
					}
					if(ffmpi->flags & INFFMPG_FLAG_STILLPICTURE)
						funcbit_enable(pktlist_elem->flags, MPXPLAY_PACKETLISTFLAG_STILLPICT);
//					if(pktlist_elem->flags & (MPXPLAY_PACKETLISTFLAG_INIT | MPXPLAY_PACKETLISTFLAG_CLEARBUF)) // TODO: INIT ???
					if(pktlist_elem->flags & MPXPLAY_PACKETLISTFLAG_CLEARBUF)
					{
						mpxplay_debugf(MPXPLAY_DEBUG_CALLBACK, "in_ffmpgdec_infile_callback mpxplay_ffmpgdec_packetqueue_clear BEGIN ffmpi:%8.8X i:%d", (unsigned int)ffmpi, arg2_num);
						mpxplay_ffmpgdec_packetqueue_clear(&ffmpi->packet_queues[arg2_num], MPXPLAY_PACKETLISTFLAG_CLEARBUF);
						mpxplay_debugf(MPXPLAY_DEBUG_CALLBACK, "in_ffmpgdec_infile_callback mpxplay_ffmpgdec_packetqueue_clear END   ffmpi:%8.8X", (unsigned int)ffmpi);
					}
					funcbit_disable(pktlist_elem->flags, MPXPLAY_PACKETLISTFLAG_INIT);
					*((mpxplay_packetlist_t **)arg1) = pktlist_elem;
					retcode = MPXPLAY_ERROR_OK;
				}
			}
			break;
		case MPXPLAY_INFILE_CBKCTRL_GET_CODECCTX_FFMPEG:
			arg2_num = *arg2;
			if(arg2_num < MPXPLAY_STREAMTYPEINDEX_NUM){
				mpxplay_debugf(MPXPLAY_DEBUG_CALLBACK, "in_ffmpgdec_infile_callback MPXPLAY_INFILE_CBKCTRL_GET_CODECCTX_FFMPEG ffmpi:%8.8X sti:%d", (unsigned int)ffmpi, arg2_num);
				pktlist_elem = NULL;
				mpxplay_ffmpgdec_packetqueue_get((void *)ffmpi, &ffmpi->codecctx_queues[arg2_num], &pktlist_elem, 0);
				if(pktlist_elem && pktlist_elem->codec_ctx){
					*((mpxplay_packetlist_t **)arg1) = pktlist_elem;
					retcode = MPXPLAY_ERROR_OK;
				}
			}
			break;
		case MPXPLAY_INFILE_CBKCTRL_GET_PACKETLIST_ELEM:
			arg2_num = *arg2;
			if(arg2_num < MPXPLAY_STREAMTYPEINDEX_NUM){
				pktlist_elem = NULL;
				retcode = mpxplay_ffmpgdec_packetqueue_get((void *)ffmpi, &ffmpi->packet_queues[arg2_num], &pktlist_elem, 0);
				if(pktlist_elem && (pktlist_elem->flags & MPXPLAY_PACKETLISTFLAG_CLEARBUF) && pktlist_elem->codec_ctx){
					AVCodecContext *packet_ctx = (AVCodecContext *)pktlist_elem->codec_ctx;
					if(avcodec_is_open(packet_ctx) && packet_ctx->codec)
						avcodec_flush_buffers(packet_ctx);  // TODO: right place?
				}
				*((mpxplay_packetlist_t **)arg1) = pktlist_elem;
				retcode = MPXPLAY_ERROR_OK;
			}
			break;
		case MPXPLAY_INFILE_CBKCTRL_SET_PACKETQUEUE_CLEAR:
			arg1_num = *((mpxp_uint32_t *)arg1);
			if(arg1_num < MPXPLAY_STREAMTYPEINDEX_NUM)
				retcode = mpxplay_ffmpgdec_packetqueue_clear(&ffmpi->packet_queues[arg1_num], *arg2);
			break;
		case MPXPLAY_INFILE_CBKCTRL_SET_CODECCTX_CLEAR:
			arg1_num = *((mpxp_uint32_t *)arg1);
			if(arg1_num < MPXPLAY_STREAMTYPEINDEX_NUM)
				retcode = mpxplay_ffmpgdec_packetqueue_clear(&ffmpi->codecctx_queues[arg1_num], *arg2);
			break;
		case MPXPLAY_INFILE_CBKCTRL_SET_CODECCTX_CONSOLIDATE:
			arg2_num = *arg2;
			if(arg2_num < MPXPLAY_STREAMTYPEINDEX_NUM)
				retcode = mpxplay_ffmpgdec_queue_clear_to_elemctx(&ffmpi->codecctx_queues[arg2_num], arg1);
			break;
	}

err_out_cbk:
	if(timeout_ms != INFFMPG_MUTEXTIME_NOLOCK)
	{
		PDS_THREADS_MUTEX_UNLOCK(&ffmpi->mutexhnd_ffmpeg);
	}

	return retcode;
}

//------------------------------------------------------------------------------------------------------------------
static enum AVMediaType inffmpg_streamtypeindex_to_avmediatype(unsigned int streamtype_index)
{
	enum AVMediaType ff_mediatype;
	switch(streamtype_index){
		case MPXPLAY_STREAMTYPEINDEX_AUDIO: ff_mediatype = AVMEDIA_TYPE_AUDIO; break;
		case MPXPLAY_STREAMTYPEINDEX_VIDEO: ff_mediatype = AVMEDIA_TYPE_VIDEO; break;
		case MPXPLAY_STREAMTYPEINDEX_SUBTITLE: ff_mediatype = AVMEDIA_TYPE_SUBTITLE; break;
		default: ff_mediatype = AVMEDIA_TYPE_UNKNOWN; break;
	}

	return ff_mediatype;
}

static mpxp_bool_t inffmpg_skip_program_auto(struct ffmpg_demuxer_data_s *ffmpi, int prognum_current_id)
{
	mpxp_bool_t use_next = FALSE;
	int i;

	if(!in_ffmpgdec_content_has_programs(ffmpi))
	{
		return FALSE;
	}

	for(i = 0; i < ffmpi->fctx->nb_programs; i++)
	{
		int prog_id = ffmpi->fctx->programs[i]->id;
		//mpxplay_debugf(MPXPLAY_DEBUG_PRGCHG, "inffmpg_skip_program_auto i:%d c:%d id:%d", i, prognum_current_id, prog_id);
		if(prog_id == prognum_current_id)
		{
			if(funcbit_test(playcontrol, PLAYC_NOAUTOPRGSKIP))
			{
				return FALSE;
			}
			use_next = TRUE;
		}
		else if(use_next)
		{
			ffmpi->select_new_program_number = prog_id;
			break;
		}
	}

	if(ffmpi->select_new_program_number < 0)
	{
		ffmpi->select_new_program_number = ffmpi->fctx->programs[0]->id;
	}
	mpxplay_debugf(MPXPLAY_DEBUG_PRGCHG, "inffmpg_skip_program_auto END cid:%d nid:%d", prognum_current_id, ffmpi->select_new_program_number);
	return TRUE;
}

static int inffmpg_streams_count(struct ffmpg_demuxer_data_s *ffmpi, int streamtype_index, int *select_stream_number, int *selected_ff_streamindex, AVCodec **codec_av_new)
{
	int max_stindex = MPXPLAY_STREAMTYPEINDEX_AUDIO, ff_stindex, new_stream_number = INFFMPG_STREAM_INDEX_DISABLE, new_ff_streamindex = INFFMPG_STREAM_INDEX_DISABLE;
	int program_number_save = ffmpi->program_number_current, found = 0, retry = ffmpi->fctx->nb_programs + 2, retval = 0;
	enum AVMediaType ff_mediatype;

	ffmpi->nb_streams[streamtype_index] = 0;

#ifdef MPXPLAY_GUI_QT
	if(mpxplay_dispqt_ffmpegvideo_configcbk_check(MPXPLAY_INFILE_CBKCFG_SRVFFMV_OPEN_CALLBACK) == 0)
		max_stindex = MPXPLAY_STREAMTYPEINDEX_SUBTITLE;
#endif
	if(streamtype_index > max_stindex)
		return -1;

	ff_mediatype = inffmpg_streamtypeindex_to_avmediatype(streamtype_index);

	do{
		for(ff_stindex = 0; (ff_stindex < ffmpi->fctx->nb_streams) && (ff_stindex < INFFMPG_MAX_STREAMS); ff_stindex++)
		{
			AVStream *st = ffmpi->fctx->streams[ff_stindex];
			enum AVMediaType codec_type;
			//mpxplay_debugf(MPXPLAY_DEBUG_STRMCHG, "inffmpg_streams_count i:%d st:%8.8X cp:%8.8X", streamtype_index, (mpxp_uint32_t)st, (st)? (mpxp_uint32_t)st->codecpar : 0);
			//if(!st || (!st->codecpar && !st->codec)){
			if(!st || !st->codecpar){
				retval = INFFMPG_ERROR_STREAM_NOTPARSED;
				mpxplay_debugf(MPXPLAY_DEBUG_STRMCHG, "inffmpg_streams_count STFAIL st:%8.8X cp:%8.8X", (mpxp_uint32_t)st, (st)? (mpxp_uint32_t)st->codecpar : 0);
				continue;
			}
			codec_type = (st->codecpar && (st->codecpar->codec_type || !st->codec))? st->codecpar->codec_type : st->codec->codec_type;
			if(codec_type != ff_mediatype){
				//mpxplay_debugf(MPXPLAY_DEBUG_STRMCHG, "inffmpg_streams_count CTFAIL ct:%d mt:%d sti:%d", (mpxp_uint32_t)codec_type, (mpxp_uint32_t)ff_mediatype, (mpxp_uint32_t)streamtype_index);
				continue;
			}
			if(ffmpi->program_number_current >= 0)
			{
				AVProgram *program = av_find_program_from_stream(ffmpi->fctx, NULL, ff_stindex);
				if(!program || (program->id != ffmpi->program_number_current))
					continue;
				if(streamtype_index == MPXPLAY_STREAMTYPEINDEX_AUDIO)
				{
					mpxplay_debugf(MPXPLAY_DEBUG_STRMCHG, "inffmpg_streams_count PROGRAM cp:%d pr:%8.8X pid:%d ffi:%d sti:%d", ffmpi->program_number_current, program, ((program)? program->id : 0), ff_stindex, streamtype_index);
				}
			}
			if(select_stream_number)
			{
				if(!found || ((int)ffmpi->nb_streams[streamtype_index] <= *select_stream_number))
				{
					enum AVCodecID  codec_id = (st->codecpar && (st->codecpar->codec_id || !st->codec))? st->codecpar->codec_id : ((st->codec)? st->codec->codec_id : AV_CODEC_ID_NONE);
					AVCodec *codecavnew;
					if((codec_id == AV_CODEC_ID_NONE)){// || ((st->codecpar->format < 0) && (streamtype_index == MPXPLAY_STREAMTYPEINDEX_VIDEO))){
						retval = INFFMPG_ERROR_STREAM_NOTPARSED;
						mpxplay_debugf(MPXPLAY_DEBUG_STRMCHG, "inffmpg_streams_count CIFAIL ci:%8.8X stc:%8.8X np:%d fd:%d rp:%d",
								(mpxp_uint32_t)codec_id, (mpxp_uint32_t)st->codec, st->need_parsing, ((st->info)? st->info->found_decoder : 0), st->request_probe);
						st->request_probe = 5;
						continue;
					}
//					if((st->codecpar->format < 0) && (streamtype_index == MPXPLAY_STREAMTYPEINDEX_VIDEO))
//						st->codecpar->format = AV_PIX_FMT_YUV420P;  // FIXME: hack!
#if 0
					if(codec_id == AV_CODEC_ID_MP3)
						codecavnew = avcodec_find_decoder_by_name("mp3float"); // FIXME: not required if integer MP3 decoder is removed from FFMPEG lib
					else
#endif
						codecavnew = avcodec_find_decoder(codec_id);
					if(codecavnew){
						if(codec_av_new)
							*codec_av_new = codecavnew;
						new_ff_streamindex = ff_stindex;
						new_stream_number = ffmpi->nb_streams[streamtype_index];
						found = 1;
					}
				}
			}else{
				found = 1;
			}
			ffmpi->nb_streams[streamtype_index]++;
		}

#ifdef INFFMP_SUPPORT_DVB_AUDIO
		if(found || (ffmpi->program_number_current < 0) || (streamtype_index == MPXPLAY_STREAMTYPEINDEX_VIDEO) || (streamtype_index == MPXPLAY_STREAMTYPEINDEX_SUBTITLE))
#else
		if(found || (ffmpi->program_number_current < 0) || (streamtype_index == MPXPLAY_STREAMTYPEINDEX_SUBTITLE))
#endif
			break;
		if(!(--retry))
		{
			if(!funcbit_test(playcontrol, PLAYC_NOAUTOPRGSKIP))
				ffmpi->program_number_current = INFFMPG_INVALID_STREAM_INDEX; // program is invalid / lost
			break;
		}
		program_number_save = ffmpi->program_number_current;
		if(inffmpg_skip_program_auto(ffmpi, program_number_save))  // program is invalid / lost (and not the selected one, and auto skip is enabled)
		{
			ffmpi->program_number_current = ffmpi->select_new_program_number;
		}
		ffmpi->select_new_program_number = INFFMPG_INVALID_STREAM_INDEX;
		if(ffmpi->program_number_current > 0)
			funcbit_enable(ffmpi->flags, INFFMPG_FLAG_DEMUX_BUFREWIND);
	}while(TRUE);

	if(select_stream_number && (new_stream_number >= 0))
		*select_stream_number = new_stream_number;
	if(selected_ff_streamindex && (new_ff_streamindex >= 0))
		*selected_ff_streamindex = new_ff_streamindex;

	if(ffmpi->fctx->nb_programs > 0) // verify program selection (on 1 program element too)
	{
		if((ffmpi->program_number_current < 0) && (new_ff_streamindex >= 0)) // valid stream is selected without program
		{
			AVProgram *program = av_find_program_from_stream(ffmpi->fctx, NULL, new_ff_streamindex);
			if(program)  // try to correct program number to selected stream
				ffmpi->program_number_current = program->id;
			mpxplay_debugf(MPXPLAY_DEBUG_PRGCHG, "inffmpg_streams_count new_ff_streamindex pold:%d pnew:%d nfs:%d", program_number_save, ffmpi->program_number_current, new_ff_streamindex);
		}

		if(!funcbit_test(ffmpi->flags, INFFMPG_FLAG_LOADHEADONLY) && in_ffmpgdec_content_has_programs(ffmpi) && (ffmpi->program_number_current >= 0) && codec_av_new) // codec_av_new -> called from in_ffmpgdec_initialize_stream
		{
			mpxplay_control_startup_programid_select = ffmpi->program_number_current;
			mpxplay_debugf(MPXPLAY_DEBUG_TEMP, "mpxplay_control_startup_programid_select %d", mpxplay_control_startup_programid_select);
		}
	}

	if((streamtype_index == MPXPLAY_STREAMTYPEINDEX_AUDIO) || (streamtype_index == MPXPLAY_STREAMTYPEINDEX_AUDIO))
	{
		mpxplay_debugf(MPXPLAY_DEBUG_STRMCHG, "inffmpg_streams_count END sti:%d found:%d nbs:%d", streamtype_index, found, ffmpi->fctx->nb_streams);
	}

	if(found)
		retval = found;

	return retval;
}

// give a priority value using the stream codec name, comparing with the preffered audiotypes list (lower number means higher priority)
static int inffmpg_get_audio_priority(AVStream *st)
{
	int priority = MPXINI_MAX_EXTRA_FILETYPES, i;
	char *codec_name;
	if(!st || !st->codecpar)
	{
		mpxplay_debugf(MPXPLAY_DEBUG_STRMPRIO, "inffmpg_get_audio_priority NO ST");
		return priority;
	}
	codec_name = (char *)avcodec_get_name(st->codecpar->codec_id);
	if(!codec_name)
	{
		mpxplay_debugf(MPXPLAY_DEBUG_STRMPRIO, "inffmpg_get_audio_priority NO NAME");
		return priority;
	}
	i = 0;
	do
	{
		char *audiotype_ptr = mpxplay_config_video_preffered_audiotype_ptrs[i];
		if(!audiotype_ptr)
			break;
		if(pds_filename_wildcard_cmp(codec_name, audiotype_ptr))
		{
			priority = i;
			mpxplay_debugf(MPXPLAY_DEBUG_STRMPRIO, "inffmpg_get_audio_priority i:%d p:%d a:\"%s\" cn:\"%s\"", i, priority, audiotype_ptr, codec_name);
			break;
		}
		i++;
	}while(i < MPXINI_MAX_EXTRA_FILETYPES);
	return priority;
}

// enable stream initially, if DEFAULT or FORCED stream found (return stream_number for that stream)
int in_ffmpgdec_stream_select_default(struct ffmpg_demuxer_data_s *ffmpi, int streamtype_index, mpxp_bool_t forced_stream_only)
{
	int stream_ff_idx = INFFMPG_INVALID_STREAM_INDEX, best_stream_num = INFFMPG_STREAM_INDEX_DISABLE, best_audio_priority = MPXINI_MAX_EXTRA_FILETYPES;
	int i, sc, stream_number = 0, selected_stream_num = INFFMPG_STREAM_INDEX_DISABLE, selected_audio_priority = MPXINI_MAX_EXTRA_FILETYPES + 1;

	sc = inffmpg_streams_count(ffmpi, streamtype_index, &stream_number, &stream_ff_idx, NULL);
	if(sc <= 0)
	{
		return selected_stream_num;
	}
	if(streamtype_index == MPXPLAY_STREAMTYPEINDEX_AUDIO)
	{
		mpxplay_debugf(MPXPLAY_DEBUG_STRMPRIO, "in_ffmpgdec_select_default_stream BEGIN t:%d nb:%d sc:%d pr:%d sn:%d ffid:%d",
				streamtype_index, ffmpi->nb_streams[streamtype_index], sc, ffmpi->program_number_current, stream_number, stream_ff_idx);
	}
	stream_number = 0;
	for(i = 0; i < ffmpi->nb_streams[streamtype_index]; i++)
	{
		stream_ff_idx = INFFMPG_INVALID_STREAM_INDEX;
		sc = inffmpg_streams_count(ffmpi, streamtype_index, &stream_number, &stream_ff_idx, NULL);
		if((sc > 0) && (stream_ff_idx >= 0) && (stream_ff_idx < ffmpi->fctx->nb_streams))
		{
			AVStream *st = ffmpi->fctx->streams[stream_ff_idx];
			if(st)
			{
				int audio_priority_stream = MPXINI_MAX_EXTRA_FILETYPES;
				if(best_stream_num < 0)
				{
					best_stream_num = stream_number;
				}
				if(streamtype_index == MPXPLAY_STREAMTYPEINDEX_AUDIO)
				{
					audio_priority_stream = inffmpg_get_audio_priority(st);
					mpxplay_debugf(MPXPLAY_DEBUG_STRMPRIO, "in_ffmpgdec_stream_select_default PRIO i:%d sn:%d p:%d", i, stream_number, audio_priority_stream);
					if(audio_priority_stream < best_audio_priority) // we've found a better audio format (by the preffered audiotypes list)
					{
						best_audio_priority = audio_priority_stream;
						best_stream_num = stream_number;
					}
				}
				if(!forced_stream_only || (st->disposition & (AV_DISPOSITION_DEFAULT | AV_DISPOSITION_FORCED)))
				{
					AVDictionaryEntry *t = av_dict_get(st->metadata, "language", NULL, 0);
					if(t && t->value && (pds_stricmp(t->value, mpxplay_config_video_preferred_language[streamtype_index]) == 0)) // found language for this streamtype
					{
						if(streamtype_index == MPXPLAY_STREAMTYPEINDEX_AUDIO)
						{
							mpxplay_debugf(MPXPLAY_DEBUG_STRMPRIO, "in_ffmpgdec_stream_select_default LANG sn:%d ssn:%d bsn:%d sap:%d ap:%d", stream_number, selected_stream_num, best_stream_num, selected_audio_priority, audio_priority_stream);
						}
						if((selected_stream_num < 0) || (audio_priority_stream < selected_audio_priority) || ((streamtype_index == MPXPLAY_STREAMTYPEINDEX_SUBTITLE) && !forced_stream_only && !(st->disposition & (AV_DISPOSITION_DEFAULT | AV_DISPOSITION_FORCED)))) // (audio) language not found yet or we've found a better audio format for this language OR non-forced subtitle found
						{
							selected_stream_num = stream_number;
							selected_audio_priority = audio_priority_stream;
						}
						if(streamtype_index == MPXPLAY_STREAMTYPEINDEX_VIDEO) // we don't search further the language at video streams
						{
							break;
						}
					}
				}
			}
			stream_number++;
		}
	}

	if((selected_stream_num < 0) && ((streamtype_index != MPXPLAY_STREAMTYPEINDEX_SUBTITLE) || !forced_stream_only)) // language not found at all
	{
		selected_stream_num = best_stream_num; // use the first found stream (or the highest-priority audio stream)
		if(streamtype_index == MPXPLAY_STREAMTYPEINDEX_AUDIO)
		{
			mpxplay_debugf(MPXPLAY_DEBUG_STRMPRIO, "in_ffmpgdec_stream_select_default FINAL st:%d sn:%d", streamtype_index, selected_stream_num);
		}
	}

	mpxplay_debugf(MPXPLAY_DEBUG_STRMCHG, "in_ffmpgdec_select_default_stream END sti:%d nbs:%d sn:%d ffi:%d", streamtype_index, ffmpi->fctx->nb_streams, selected_stream_num, stream_ff_idx);

	return selected_stream_num;
}

static int inffmpg_stream_language_cmp(struct ffmpg_demuxer_data_s *ffmpi, int streamtype_index, int stream_number, char *language)
{
	int sn = stream_number, stream_ff_idx = INFFMPG_INVALID_STREAM_INDEX;
	AVDictionaryEntry *t;
	AVStream *st;

	if(inffmpg_streams_count(ffmpi, streamtype_index, &sn, &stream_ff_idx, NULL) <= 0)
	{
		return -1;
	}
	if((stream_ff_idx < 0) || (stream_ff_idx >= ffmpi->fctx->nb_streams))
	{
		return -1;
	}
	st = ffmpi->fctx->streams[stream_ff_idx];
	if(!st)
	{
		return -1;
	}
	t = av_dict_get(st->metadata, "language", NULL, 0);
	if(!t || !t->value)
	{
		return -1;
	}
	return pds_stricmp(t->value, language);
}

void in_ffmpgdec_stream_select_all_default(struct ffmpg_demuxer_data_s *ffmpi)
{
	int i;

	for(i = 0; i <= ffmpi->streamtype_limit; i++)
	{
		mpxp_bool_t forced_stream_only = FALSE;
		ffmpi->select_new_stream_number[i] = INFFMPG_INVALID_STREAM_INDEX;
		ffmpi->selected_ff_stream_index[i] = INFFMPG_INVALID_STREAM_INDEX;
		switch(i)
		{
			case MPXPLAY_STREAMTYPEINDEX_SUBTITLE:
				if( (ffmpi->select_new_stream_number[MPXPLAY_STREAMTYPEINDEX_AUDIO] >= 0)
				 && (inffmpg_stream_language_cmp(ffmpi, MPXPLAY_STREAMTYPEINDEX_AUDIO, ffmpi->select_new_stream_number[MPXPLAY_STREAMTYPEINDEX_AUDIO], mpxplay_config_video_preferred_language[MPXPLAY_STREAMTYPEINDEX_SUBTITLE]) == 0)
				){
					forced_stream_only = TRUE; // TODO: default/forced flag is used at subtitle only
				}
			case MPXPLAY_STREAMTYPEINDEX_AUDIO:
			case MPXPLAY_STREAMTYPEINDEX_VIDEO:
				ffmpi->select_new_stream_number[i] = in_ffmpgdec_stream_select_default(ffmpi, i, forced_stream_only);
				break;
		}
	}

}

static int in_ffmpgdec_initialize_stream(struct ffmpg_demuxer_data_s *ffmpi, int streamtype_index, int select_stream_number)
{
	int stream_number = select_stream_number, selected_ff_streamindex = 0, retval = 0, stream_count;
	unsigned int packet_flags;
	AVCodecContext *codec_ctx_new = NULL;
	AVStream *selected_av_st;
	AVCodec *codec_av_new;

	if(stream_number == INFFMPG_STREAM_INDEX_DISABLE)
	{
		if(ffmpi->codecctx_queues[streamtype_index].nb_packets)
			mpxplay_ffmpgdec_packetqueue_put(&ffmpi->codecctx_queues[streamtype_index], MPXPLAY_PACKETTYPE_AVCODECCTX, MPXPLAY_PACKETLISTFLAG_CLEARBUF, NULL, NULL);
		if(ffmpi->packet_queues[streamtype_index].nb_packets)  // TODO: ???
			mpxplay_ffmpgdec_packetqueue_put(&ffmpi->packet_queues[streamtype_index], MPXPLAY_PACKETTYPE_AVPACKET, MPXPLAY_PACKETLISTFLAG_CLEARBUF, NULL, NULL); //
		goto err_out_stinit;
	}

	stream_count = inffmpg_streams_count(ffmpi, streamtype_index, &stream_number, &selected_ff_streamindex, &codec_av_new);
	if(stream_count <= 0)
	{
		retval = stream_count;
		goto err_out_stinit;
	}

	codec_ctx_new = avcodec_alloc_context3(codec_av_new);
	if(!codec_ctx_new)
	{
		retval = AVERROR(ENOMEM);
		goto err_out_stinit;
	}
	selected_av_st = ffmpi->fctx->streams[selected_ff_streamindex];
	retval = avcodec_parameters_to_context(codec_ctx_new, selected_av_st->codecpar);
	if(retval < 0)
		goto err_out_stinit;

	if(streamtype_index == MPXPLAY_STREAMTYPEINDEX_VIDEO) // video codec ctx is opened from video_worker -> in_ffmpgdec_infile_callback (for multithread safety)
	{
		codec_ctx_new->codec = codec_av_new;
	}
	else
	{
		if(streamtype_index == MPXPLAY_STREAMTYPEINDEX_AUDIO)
		{
			codec_ctx_new->request_sample_fmt = AV_SAMPLE_FMT_FLT;
#ifndef INFFMP_USE_DEMUXER_AUINIT
			codec_ctx_new->codec = codec_av_new;
		}
		if(streamtype_index == MPXPLAY_STREAMTYPEINDEX_SUBTITLE)
#else
		}
#endif
		{
			retval = avcodec_open2(codec_ctx_new, codec_av_new, NULL);
			if(retval < 0)
				goto err_out_stinit;
        }
	}

	ffmpi->selected_stream_number[streamtype_index] = stream_number;
	ffmpi->selected_ff_stream_index[streamtype_index] = selected_ff_streamindex;
	ffmpi->selected_avstream[streamtype_index] = selected_av_st;
	ffmpi->avcodecs[streamtype_index] = codec_av_new;
	ffmpi->codec_ctx[streamtype_index] = codec_ctx_new;

	packet_flags = 0;
#ifdef INFFMP_USE_DEMUXER_AUINIT
	if(streamtype_index == MPXPLAY_STREAMTYPEINDEX_VIDEO)
#endif
		funcbit_enable(packet_flags, MPXPLAY_PACKETLISTFLAG_INIT);
	if(ffmpi->stream_flags[streamtype_index] & INFFMPG_STREAMFLAG_CLEARBUF_DEMUX) // TODO: check CLEARBUF
		funcbit_enable(packet_flags, MPXPLAY_PACKETLISTFLAG_CLEARBUF);

	mpxplay_ffmpgdec_packetqueue_put(&ffmpi->codecctx_queues[streamtype_index], MPXPLAY_PACKETTYPE_AVCODECCTX, packet_flags, codec_ctx_new, NULL);

	if(!funcbit_test(ffmpi->flags, INFFMPG_FLAG_LOADHEADONLY)){
		mpxplay_debugf(MPXPLAY_DEBUG_STRMCHG, "in_ffmpgdec_initialize_stream %8.8X i:%d ctx:%8.8X", (unsigned int)ffmpi, streamtype_index, (unsigned int)codec_ctx_new);
	}

	//av_dump_format(ffmpi->fctx, ffmpi->selected_ff_stream_index[streamtype_index], NULL, 0);
	return stream_count;

err_out_stinit:
	if(codec_ctx_new)
		avcodec_free_context(&codec_ctx_new);
	ffmpi->selected_stream_number[streamtype_index] = INFFMPG_INVALID_STREAM_INDEX;
	ffmpi->selected_ff_stream_index[streamtype_index] = INFFMPG_INVALID_STREAM_INDEX;
	ffmpi->selected_avstream[streamtype_index] = NULL;
	ffmpi->avcodecs[streamtype_index] = NULL;
	ffmpi->codec_ctx[streamtype_index] = NULL;
	mpxplay_ffmpgdec_packetqueue_put(&ffmpi->codecctx_queues[streamtype_index], MPXPLAY_PACKETTYPE_AVCODECCTX, MPXPLAY_PACKETLISTFLAG_CLEARBUF, NULL, NULL);
	mpxplay_ffmpgdec_packetqueue_put(&ffmpi->packet_queues[streamtype_index], MPXPLAY_PACKETTYPE_AVPACKET, MPXPLAY_PACKETLISTFLAG_CLEARBUF, NULL, NULL);
	if(streamtype_index != MPXPLAY_STREAMTYPEINDEX_SUBTITLE)
	{
		mpxplay_debugf(MPXPLAY_DEBUG_ERROR, "in_ffmpgdec_initialize_stream FAILED %8.8X i:%d", (unsigned int)ffmpi, streamtype_index);
	}
	return retval;
}

mpxp_bool_t in_ffmpgdec_streams_initialize_all(struct ffmpg_demuxer_data_s *ffmpi, mpxp_bool_t initial)
{
	mpxp_bool_t is_video_stream_change = FALSE;
	int i;
	for(i = 0; i <= ffmpi->streamtype_limit; i++)
	{
		if(!in_ffmpgdec_content_has_programs(ffmpi))
		{
			if(ffmpi->select_new_stream_number[i] == INFFMPG_STREAM_INDEX_LOST)
				ffmpi->select_new_stream_number[i] = 0; // TODO: use like in_ffmpgdec_stream_select_all_default
		}
		if(ffmpi->select_new_stream_number[i] > INFFMPG_INVALID_STREAM_INDEX)
		{
			int retval, old_stream_num = ffmpi->selected_stream_number[i];
			if(i == MPXPLAY_STREAMTYPEINDEX_AUDIO)
			{
				mpxplay_debugf(MPXPLAY_DEBUG_STRMCHG, "in_ffmpgdec_streams_initialize_all id:%d -> sn:%d", i, ffmpi->select_new_stream_number[i]);
			}
			retval = in_ffmpgdec_initialize_stream(ffmpi, i, ffmpi->select_new_stream_number[i]);
			if(retval > 0)
			{
				if(!initial)
					funcbit_enable(ffmpi->stream_flags[i], INFFMPG_STREAMFLAG_CLEARBUF_DEMUX);
#ifdef INFFMP_USE_VIDEO_SYNC
//				else if(i == MPXPLAY_STREAMTYPEINDEX_AUDIO)
//					funcbit_enable(ffmpi->stream_flags[i], INFFMPG_STREAMFLAG_AVSYNC);  // FIXME: initial sync doesn't work
#endif
			}
			if((retval != INFFMPG_ERROR_STREAM_NOTPARSED) || (i >= MPXPLAY_STREAMTYPEINDEX_SUBTITLE))
				ffmpi->select_new_stream_number[i] = INFFMPG_INVALID_STREAM_INDEX;
			if(i <= MPXPLAY_STREAMTYPEINDEX_VIDEO)
			{
				ffmpi->avsync_timestamp = INFFMPG_INVALID_PTS;
				ffmpi->avsync_videotimestamp = INFFMPG_INVALID_PTS;
				if((i == MPXPLAY_STREAMTYPEINDEX_VIDEO) && (old_stream_num != ffmpi->selected_stream_number[i]))
					is_video_stream_change = TRUE; // a video stream has changed
			}
		}
	}
	return is_video_stream_change;
}

static mpxp_bool_t inffmp_demux_stream_reconfig(struct ffmpg_demuxer_data_s *ffmpi)
{
	mpxp_bool_t is_video_stream_change;
	if(ffmpi->select_new_program_number >= 0)
	{
		ffmpi->program_number_current = ffmpi->select_new_program_number;
		ffmpi->select_new_program_number = INFFMPG_INVALID_STREAM_INDEX;
		in_ffmpgdec_stream_select_all_default(ffmpi);
		funcbit_enable(ffmpi->flags, INFFMPG_FLAG_DEMUX_BUFREWIND);
		mpxplay_debugf(MPXPLAY_DEBUG_STRMCHG, "select_new_program_number %d", ffmpi->program_number_current);
	}
	is_video_stream_change = in_ffmpgdec_streams_initialize_all(ffmpi, FALSE);

//	AVStream *video_stream = ffmpi->selected_avstream[MPXPLAY_STREAMTYPEINDEX_VIDEO];
//	if(video_stream && (!video_stream->avg_frame_rate.num || !video_stream->avg_frame_rate.den))
//	{
//		video_stream->codec_info_nb_frames++;
//		video_stream->avg_frame_rate = av_guess_frame_rate(ffmpi->fctx, video_stream, NULL);
//	}

	return is_video_stream_change;
}

//----------------------------------------------------------------------------------------------------------------

static int inffmp_demux_read_frame(struct ffmpg_demuxer_data_s *ffmpi)
{
	int retcode;
#ifdef INFFMPG_USE_MPXPLAY_IOCTX
	struct mpxpframe_s *frp = (struct mpxpframe_s *)ffmpi->fbds;
	unsigned int buffertype_save;
	long pbbf_save;
	AVIOContext *pb;

	if(!frp || !ffmpi->fctx)
		return AVERROR(EAGAIN);
	pb = ffmpi->fctx->pb;
	if(!pb)
		return AVERROR(EAGAIN);

	buffertype_save = frp->buffertype;
	if(funcbit_test(ffmpi->flags, INFFMPG_FLAG_DEMUX_THREAD)) // file and DTV (else http audio)
	{
		funcbit_disable(frp->buffertype, PREBUFTYPE_INT);     // allow file read, out of prebuffer
	}
	else if((ffmpi->flags & INFFMPG_FLAG_IS_LIVESTREAM) && (frp->buffertype & PREBUFTYPE_INT) && (frp->prebufferbytes_forward <= frp->prebufferblocksize)) // TODO: check
	{
		return AVERROR(EAGAIN);
	}

	pb->opaque = ffmpi->fbds;
	pbbf_save = frp->prebufferbytes_forward;
#endif

	av_packet_unref(ffmpi->pkt);
	//mpxplay_debugf(MPXPLAY_DEBUG_DEMUX,"av_read_frame BEGIN frp:%8.8X br:%d bf:%d", frp, frp->prebufferbytes_rewind, frp->prebufferbytes_forward);
	retcode = av_read_frame(ffmpi->fctx, ffmpi->pkt);

#ifdef INFFMPG_USE_MPXPLAY_IOCTX
	if(funcbit_test(ffmpi->flags, INFFMPG_FLAG_DEMUX_THREAD))
	{
		funcbit_smp_copy(frp->buffertype, buffertype_save, PREBUFTYPE_INT);
	}

	if(retcode < 0)
	{
		if((ffmpi->flags & INFFMPG_FLAG_IS_LIVESTREAM) && (((pbbf_save >= (frp->prebufferblocksize * 8)) && (pbbf_save == frp->prebufferbytes_forward)) || (frp->prebufferbytes_forward >= (frp->prebuffersize - frp->prebufferblocksize)))) // TODO: check
		{
			if(++ffmpi->errorcount_demux > (INFFMPG_MAX_FRAME_MISMACTHES * 4))
			{
				if(!frp->mvp->adone)
				{
					frp->mvp->adone = ADONE_REOPEN; // FIXME: ffmpeg (mkv) pipe error cannot be recovered on other way?
					mpxplay_debugf(MPXPLAY_DEBUG_ERROR,"inffmp_demux_read_frame REOPEN! ro:%d", frp->mvp->adone);
				}
			}
		}
		else
		{
			if(ffmpi->errorcount_demux > (INFFMPG_MAX_FRAME_MISMACTHES * 4))
			{
				frp->mvp->adone = 0;
			}
			ffmpi->errorcount_demux = 0;
		}
		mpxplay_debugf(MPXPLAY_DEBUG_WARNING,"av_read_frame r:%d s:%4d id:%2d br:%7d bf:%7d bs:%d bls:%d nba:%2d nbv:%d", retcode,
					((ffmpi->pkt)? ffmpi->pkt->size : -1), ((ffmpi->pkt)? ffmpi->pkt->stream_index : -1),
					frp->prebufferbytes_rewind, frp->prebufferbytes_forward, frp->prebuffersize, frp->prebufferblocksize,
					ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO].nb_packets, ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].nb_packets);
	}
#endif // INFFMPG_USE_MPXPLAY_IOCTX
	return retcode;
}

void in_ffmpgdec_demux_read_packets(struct ffmpg_demuxer_data_s *ffmpi)
{
	int i, retry, max_retry, first, fill_max, stt_limit, nb_queue_packets_start[MPXPLAY_STREAMTYPEINDEX_NUM];
	mpxp_bool_t is_video_stream_change;
	struct mpxpframe_s *frp;
#if defined(INFFMP_USE_DEMUXER_THREAD) && defined(MPXINBUF_USE_FILLBUF_MUTEX)
	int lock_fillbuf_result = -1;
#endif
#if INFFMP_USE_DEMUXER_MUTEX
	int lock_result = -1;
#endif

	if(!ffmpi)
		return;

	if(!ffmpi->fctx || !ffmpi->fctx->pb || !ffmpi->fbds || funcbit_test(ffmpi->flags, INFFMPG_FLAG_DEMUX_TERMINATE)){
		//mpxplay_debugf(MPXPLAY_DEBUG_DEMUX,"in_ffmpgdec_demux_read_packets ff:%8.8X fbds:%8.8X", (unsigned int)ffmpi, (unsigned int)ffmpi->fbds);
		goto err_out_packetsread;
	}

	frp = (struct mpxpframe_s *)ffmpi->fbds;

#if defined(INFFMP_USE_DEMUXER_THREAD) && defined(MPXINBUF_USE_FILLBUF_MUTEX)
	mpxplay_debugf(MPXPLAY_DEBUG_DEMUX,"in_ffmpgdec_demux_read_packets START %8.8X pbr:%7d pbf:%7d nba:%2d nbv:%d dt:%d is:%d", (unsigned int)frp,
				frp->prebufferbytes_rewind, frp->prebufferbytes_forward,
				ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO].nb_packets, ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].nb_packets,
				((funcbit_test(ffmpi->flags, INFFMPG_FLAG_DEMUX_THREAD))? 1 : 0), ((funcbit_test(ffmpi->flags, INFFMPG_FLAG_INITIAL_SEEK))? 1 : 0));
	if(funcbit_test(ffmpi->flags, INFFMPG_FLAG_DEMUX_THREAD) && !funcbit_test(ffmpi->flags, INFFMPG_FLAG_INITIAL_SEEK))
	{
		lock_fillbuf_result = PDS_THREADS_MUTEX_LOCK(&frp->mutexhnd_fillbuffer, INFFMPG_MUTEX_TIMEOUT);
		mpxplay_debugf(MPXPLAY_DEBUG_DEMUX,"in_ffmpgdec_demux_read_packets mutexhnd_fillbuffer LOCK end frp:%8.8X mtx:%8.8X res:%d", (unsigned int)frp, frp->mutexhnd_fillbuffer, lock_fillbuf_result);
	}
#endif // defined(INFFMP_USE_DEMUXER_THREAD) && defined(MPXINBUF_USE_FILLBUF_MUTEX)

#ifdef INFFMP_USE_DEMUXER_MUTEX
	mpxplay_debugf(MPXPLAY_DEBUG_SEEK,"in_ffmpgdec_demux_read_packets LOCK begin");
	lock_result = PDS_THREADS_MUTEX_LOCK(&ffmpi->mutexhnd_demuxer, INFFMPG_MUTEX_TIMEOUT);
	mpxplay_debugf(MPXPLAY_DEBUG_SEEK,"in_ffmpgdec_demux_read_packets LOCK end res:%d", lock_result);
#endif

	ffmpi->fctx->pb->opaque = (void *)frp;

#ifdef INFFMP_USE_DEMUXER_SEEK
	if(ffmpi->seek_request_pos != INFFMPG_SEEKREQUEST_INVALID_POS)
	{
		infmpeg_infile_seek_process(ffmpi);
	}
#endif

	if(funcbit_test(ffmpi->flags, INFFMPG_FLAG_SEEK_CLEAR)){
		avio_flush(ffmpi->fctx->pb);
		avformat_flush(ffmpi->fctx);
		funcbit_disable(ffmpi->flags, INFFMPG_FLAG_SEEK_CLEAR);
		funcbit_enable(ffmpi->flags, INFFMPG_FLAG_DEMUX_CLEAR);
	}

	if(funcbit_test(ffmpi->flags, INFFMPG_FLAG_DEMUX_THREAD))
		if(PDS_THREADS_MUTEX_LOCK(&ffmpi->mutexhnd_ffmpeg, 100) != MPXPLAY_ERROR_OK)
			goto err_out_packetsread;

	if(funcbit_test(ffmpi->flags, INFFMPG_FLAG_DEMUX_CLEAR)){
		for(i = 0; i <= ffmpi->streamtype_limit; i++)
			funcbit_enable(ffmpi->stream_flags[i], INFFMPG_STREAMFLAG_CLEARBUF_DEMUX);
		funcbit_disable(ffmpi->flags, INFFMPG_FLAG_DEMUX_CLEAR);
	}

	is_video_stream_change = inffmp_demux_stream_reconfig(ffmpi);

	if(funcbit_test(playcontrol, PLAYC_RUNNING) && (ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].nb_packets >= (INFFMPG_QUEUE_PACKETNUM_MAX - INFFMPG_QUEUE_PACKETNUM_FILL_MIN)))
		if((ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO].nb_packets < INFFMPG_QUEUE_PACKETNUM_FILL_MIN) && ffmpi->codecctx_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].last_pkt){
			funcbit_enable(ffmpi->codecctx_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].last_pkt->flags, MPXPLAY_PACKETLISTFLAG_CLEARBUF);
			funcbit_enable(ffmpi->stream_flags[MPXPLAY_STREAMTYPEINDEX_AUDIO], INFFMPG_STREAMFLAG_AVRESYNC);
		}

	if(funcbit_test(ffmpi->flags, INFFMPG_FLAG_DEMUX_THREAD))
	{
		PDS_THREADS_MUTEX_UNLOCK(&ffmpi->mutexhnd_ffmpeg);
#ifdef MPXPLAY_GUI_QT
		if(is_video_stream_change && funcbit_test(ffmpi->flags, INFFMPG_FLAG_CALLBACKVIDEOCFG))
		{
			mpxplay_dispqt_ffmpegvideo_config_callback(MPXPLAY_INFILE_CBKCFG_SRVFFMV_REFRESH_CALLBACK, in_ffmpgdec_infile_callback, (void *)ffmpi);
		}
#endif
	}

	if((ffmpi->flags & INFFMPG_FLAG_IS_LIVESTREAM) && funcbit_test(ffmpi->flags, INFFMPG_FLAG_DEMUX_BUFREWIND) && infmpeg_is_fileformat_real_stream(ffmpi) && ffmpi->fbfs && ffmpi->fbfs->fseek)
	{
		long seek_back_size = min(frp->prebufferbytes_rewind, (frp->prebuffersize / 4)); // TODO: seek back size? (and hack of frp)
		avio_flush(ffmpi->fctx->pb);
		avformat_flush(ffmpi->fctx);
		if((seek_back_size > 0) && (frp->prebufferbytes_forward < (frp->prebuffersize / 2)))
		{
			if(ffmpi->fbfs->fseek((void *)frp, -seek_back_size, SEEK_CUR) < 0)
			{
				mpxplay_debugf(MPXPLAY_DEBUG_ERROR, "in_ffmpgdec_demux_read_packets SEEK BACK FAILED br:%d bf:%d bs:%d back:%d", frp->prebufferbytes_rewind, frp->prebufferbytes_forward, frp->prebuffersize, seek_back_size);
			}
			else
			{
				mpxplay_debugf(MPXPLAY_DEBUG_PRGCHG,"in_ffmpgdec_demux_read_packets SEEK BACK br:%d bf:%d bs:%d", frp->prebufferbytes_rewind, frp->prebufferbytes_forward, frp->prebuffersize);
			}
		}
		else
		{
			mpxplay_debugf(MPXPLAY_DEBUG_PRGCHG,"in_ffmpgdec_demux_read_packets SEEK BACK NONE br:%d bf:%d bs:%d", frp->prebufferbytes_rewind, frp->prebufferbytes_forward, frp->prebuffersize);
		}
	}
	funcbit_disable(ffmpi->flags, INFFMPG_FLAG_DEMUX_BUFREWIND);

#ifdef INFFMP_USE_DEMUXER_BUFILL
	if(!funcbit_test(ffmpi->flags, INFFMPG_FLAG_INITIAL_SEEK))
		frp->mvp->fdone = mpxplay_mpxinbuf_buffer_check(frp, FALSE);  // FIXME: hack
#endif

	nb_queue_packets_start[MPXPLAY_STREAMTYPEINDEX_AUDIO] = ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO].nb_packets;
	nb_queue_packets_start[MPXPLAY_STREAMTYPEINDEX_VIDEO] = ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].nb_packets;

	stt_limit = min(ffmpi->streamtype_limit, MPXPLAY_STREAMTYPEINDEX_VIDEO);
	fill_max = (ffmpi->flags & INFFMPG_FLAG_DEMUX_THREAD)? ((ffmpi->flags & INFFMPG_FLAG_IS_LIVESTREAM)? INFFMPG_QUEUE_PACKETNUM_FILL_MIN : INFFMPG_QUEUE_PACKETNUM_FILL_MAX) : INFFMPG_QUEUE_PACKETNUM_MIN;
	max_retry = (ffmpi->fctx->nb_streams + 2) * INFFMPG_QUEUE_PACKETNUM_FILL_MIN * 2;
	retry = first = 1;

	while(retry && (--max_retry) && !funcbit_test(ffmpi->flags, INFFMPG_FLAG_DEMUX_TERMINATE)
		&& (ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO].nb_packets < INFFMPG_QUEUE_PACKETNUM_MAX)
		&& (ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].nb_packets < INFFMPG_QUEUE_PACKETNUM_MAX)
	){
		int ff_stream_index, i;

		retry = 0;
		for(i = 0; i <= stt_limit; i++){
			if(ffmpi->codec_ctx[i] && (ffmpi->packet_queues[i].nb_packets < fill_max)
			  && ((ffmpi->packet_queues[i].nb_packets < INFFMPG_QUEUE_PACKETNUM_FILL_MIN) || ((ffmpi->packet_queues[i].nb_packets - nb_queue_packets_start[i]) < INFFMPG_QUEUE_PACKETNUM_FILL_STEP))
			){
				retry++;
			}
		}
		if(!retry)
			break;
		ffmpi->retcode_demux = inffmp_demux_read_frame(ffmpi);
		if((ffmpi->retcode_demux != 0) || !ffmpi->pkt)
		{
			if(ffmpi->flags & INFFMPG_FLAG_INITIAL_SEEK)
				continue;
			break;
		}
		ff_stream_index = ffmpi->pkt->stream_index;
		if((ffmpi->pkt->size > 0) && (ff_stream_index >= 0) && (ff_stream_index < ffmpi->fctx->nb_streams))
		{
			AVStream *avstrm = ffmpi->fctx->streams[ff_stream_index];
			enum AVMediaType pkt_mediatype = (avstrm && avstrm->codecpar)? avstrm->codecpar->codec_type : AVMEDIA_TYPE_UNKNOWN;
			for(i = 0; i <= ffmpi->streamtype_limit; i++){
				if(pkt_mediatype != inffmpg_streamtypeindex_to_avmediatype(i))
					continue;
				if(ffmpi->pkt->stream_index == ffmpi->selected_ff_stream_index[i]){
					unsigned int packet_flags = 0;
					if(funcbit_test(ffmpi->stream_flags[i], INFFMPG_STREAMFLAG_CLEARBUF_DEMUX)){
						packet_flags = MPXPLAY_PACKETLISTFLAG_CLEARBUF;
						if(ffmpi->codecctx_queues[i].last_pkt
						 && ((i == MPXPLAY_STREAMTYPEINDEX_AUDIO)
							|| ((i == MPXPLAY_STREAMTYPEINDEX_VIDEO)
							   && (!funcbit_test(playcontrol, PLAYC_RUNNING)
							      || (ffmpi->codecctx_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO].last_pkt
							      && !funcbit_test(ffmpi->codecctx_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO].last_pkt->flags, packet_flags)
							      && !funcbit_test(ffmpi->stream_flags[MPXPLAY_STREAMTYPEINDEX_AUDIO], INFFMPG_STREAMFLAG_CLEARBUF_DEMUX)
							)  )  )  )
						){
							funcbit_enable(ffmpi->codecctx_queues[i].last_pkt->flags, packet_flags);
						}
						funcbit_disable(ffmpi->stream_flags[i], INFFMPG_STREAMFLAG_CLEARBUF_DEMUX);
					}
					mpxplay_ffmpgdec_packetqueue_put(&ffmpi->packet_queues[i], MPXPLAY_PACKETTYPE_AVPACKET, packet_flags, ffmpi->codec_ctx[i], ffmpi->pkt);
					if((i == MPXPLAY_STREAMTYPEINDEX_AUDIO))// && (ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO].nb_packets < fill_max))
					{
						struct mpxpframe_s *frp = (struct mpxpframe_s *)ffmpi->fbds;
						mpxplay_debugf(MPXPLAY_DEBUG_DEMUX,"in_ffmpgdec_demux_read_packets PUTPACK frp:%8.8X nba:%3d nbv:%3d br:%6d bf:%5d pts:%lld", (unsigned int)frp,
								 ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO].nb_packets, ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].nb_packets,
								 frp->prebufferbytes_rewind, frp->prebufferbytes_forward, ffmpi->pkt->pts);
					}
					ffmpi->nb_frame_mismatches[i] = 0;
					break;
				}
				else if((ffmpi->flags & INFFMPG_FLAG_IS_STREAM) && ((++ffmpi->nb_frame_mismatches[i]) > (INFFMPG_MAX_FRAME_MISMACTHES * (ffmpi->fctx->nb_programs + 1))) // this seems to be better for single TS with stream changes
//				else if(!(ffmpi->flags & INFFMPG_FLAG_INITIAL_SEEK) && (ffmpi->flags & INFFMPG_FLAG_IS_STREAM) && ((++ffmpi->nb_frame_mismatches[i]) > (INFFMPG_MAX_FRAME_MISMACTHES * (ffmpi->fctx->nb_programs + 1)))
				){
					ffmpi->select_new_stream_number[i] = INFFMPG_STREAM_INDEX_LOST; // change stream if the current has lost
					ffmpi->nb_frame_mismatches[i] = 0;
					retry = 0;
					if(i == MPXPLAY_STREAMTYPEINDEX_AUDIO)
					{
						mpxplay_debugf(MPXPLAY_DEBUG_WARNING,"in_ffmpgdec_demux_read_packets stream lost!");
					}
				}
			}
			if((ffmpi->select_new_stream_number[MPXPLAY_STREAMTYPEINDEX_AUDIO] == INFFMPG_STREAM_INDEX_LOST) && (ffmpi->select_new_stream_number[MPXPLAY_STREAMTYPEINDEX_VIDEO] == INFFMPG_STREAM_INDEX_LOST))
			{
				inffmpg_skip_program_auto(ffmpi, ffmpi->program_number_current);  // change program if the current lost
				retry = 0;
			}
		}
		av_packet_unref(ffmpi->pkt);
		first = 0;
	}
	if(!first && (ffmpi->retcode_demux != 0 || !max_retry))
	{
		mpxplay_debugf(MPXPLAY_DEBUG_DEMUX,"in_ffmpgdec_demux_read_packets %8.8X rd:%d mr:%d nba:%d nbv:%d da:%d dv:%d pb:%8.8X",
			(unsigned int)ffmpi, ffmpi->retcode_demux, max_retry,
			ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO].nb_packets, ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].nb_packets,
			ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO].nb_packets - nb_queue_packets_start[MPXPLAY_STREAMTYPEINDEX_AUDIO],
			ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].nb_packets - nb_queue_packets_start[MPXPLAY_STREAMTYPEINDEX_VIDEO],
			(unsigned int)ffmpi->fctx->pb->opaque);
	}

	for(i = 0; i <= stt_limit; i++){
		if(ffmpi->selected_avstream[i] && (ffmpi->selected_avstream[i]->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED)){  // TODO: move it to demux thread
			inffmpg_tags_update(ffmpi->miis, ffmpi->fbds, ffmpi->selected_avstream[i]->metadata, MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_UPDATE);
			funcbit_disable(ffmpi->selected_avstream[i]->event_flags, AVSTREAM_EVENT_FLAG_METADATA_UPDATED);
		}
	}
	if(ffmpi->fctx->event_flags & AVFMT_EVENT_FLAG_METADATA_UPDATED){
		inffmpg_tags_update(ffmpi->miis, ffmpi->fbds, ffmpi->fctx->metadata, MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_UPDATE);
		funcbit_disable(ffmpi->fctx->event_flags, AVFMT_EVENT_FLAG_METADATA_UPDATED);
	}

err_out_packetsread:
	if(funcbit_test(ffmpi->flags, INFFMPG_FLAG_DEMUX_THREAD))
	{
		ffmpi->fbds = NULL;
	}
#if defined(INFFMP_USE_DEMUXER_THREAD) && defined(MPXINBUF_USE_FILLBUF_MUTEX)
	if(lock_fillbuf_result == MPXPLAY_ERROR_OK)
	{
		PDS_THREADS_MUTEX_UNLOCK(&frp->mutexhnd_fillbuffer);
	}
	mpxplay_debugf(MPXPLAY_DEBUG_DEMUX,"in_ffmpgdec_demux_read_packets mutexhnd_fillbuffer UNLOCK %8.8X res:%d pl:%d", (unsigned int)frp, lock_fillbuf_result, ((playcontrol&PLAYC_RUNNING)? 1 : 0));
#endif
#ifdef INFFMP_USE_DEMUXER_MUTEX
	if(lock_result == MPXPLAY_ERROR_OK)
	{
		mpxplay_debugf(MPXPLAY_DEBUG_SEEK,"in_ffmpgdec_demux_read_packets UNLOCK");
		PDS_THREADS_MUTEX_UNLOCK(&ffmpi->mutexhnd_demuxer);
	}
#endif
}

//----------------------------------------------------------------------------------------------------------------

static int inffmpgdec_decodeaudio_clearbuf(struct ffmpg_demuxer_data_s *ffmpi, mpxplay_packetlist_t *pktlist_ctx, AVCodecContext *audio_ctx)
{
	int retcode_audio = 0;

	if(pktlist_ctx->flags & MPXPLAY_PACKETLISTFLAG_CLEARBUF){
		av_frame_unref(ffmpi->audio_frame);
		ffmpi->audioframe_samples_per_channel = 0;
		ffmpi->audioframe_copy_sampleblocksize = 0;
		ffmpi->audioframe_copy_samplepos = 0;
#ifdef INFFMP_USE_DEMUXER_AUINIT
		mpxplay_ffmpgdec_packetqueue_clear(&ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO], MPXPLAY_PACKETLISTFLAG_CLEARBUF);
#endif
		if(avcodec_is_open(audio_ctx) && audio_ctx->codec)
			avcodec_flush_buffers(audio_ctx);
		funcbit_disable(pktlist_ctx->flags, MPXPLAY_PACKETLISTFLAG_CLEARBUF);
#ifdef INFFMP_USE_VIDEO_SYNC
		funcbit_enable(ffmpi->stream_flags[MPXPLAY_STREAMTYPEINDEX_AUDIO], INFFMPG_STREAMFLAG_AVSYNC);
#endif
		if(ffmpi->codecctx_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].last_pkt)
			funcbit_enable(ffmpi->codecctx_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].last_pkt->flags, MPXPLAY_PACKETLISTFLAG_CLEARBUF);  // FIXME: not thread safe
		mpxplay_debugf(MPXPLAY_DEBUG_DEMUX, "in_ffmpgdec_decode_audio CLEARBUF");
	}

	if((audio_ctx->codec_id == AV_CODEC_ID_MP2) && (ffmpi->retcode_demux != 0) && (ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO].nb_packets < 1)){  // FIXME: hack (to not play last, possibly invalid mp2 frame)
		retcode_audio = AVERROR(ESPIPE);
		mpxplay_debugf(MPXPLAY_DEBUG_WARNING, "inffmpgdec_decodeaudio_clearbuf MP2 discarded frame");
	}

	return retcode_audio;
}

#ifdef INFFMP_USE_VIDEO_SYNC
static int inffmpgdec_decodeaudio_silentblock_by_vsync(struct ffmpg_demuxer_data_s *ffmpi, struct mpxplay_streampacket_info_s *spi, mpxp_int64_t *avsync_vidtimestamp)
{
	int retcode_audio = 0;

	if( funcbit_test(ffmpi->stream_flags[MPXPLAY_STREAMTYPEINDEX_AUDIO], (INFFMPG_STREAMFLAG_AVSYNC | INFFMPG_STREAMFLAG_AVRESYNC))
		&& (funcbit_test(ffmpi->stream_flags[MPXPLAY_STREAMTYPEINDEX_AUDIO], INFFMPG_STREAMFLAG_AVRESYNC) || in_ffmpgdec_content_has_programs(ffmpi))
		&& (ffmpi->selected_stream_number[MPXPLAY_STREAMTYPEINDEX_VIDEO] >= 0)
	){
		if(in_ffmpgdec_infile_callback((void *)ffmpi, MPXPLAY_INFILE_CBKCTRL_GET_VIDEOTIMESTAMP, avsync_vidtimestamp, NULL, INFFMPG_MUTEXTIME_NOLOCK) == MPXPLAY_ERROR_OK)
		{
			if( (*avsync_vidtimestamp <= 0)
			 || ( funcbit_test(ffmpi->stream_flags[MPXPLAY_STREAMTYPEINDEX_AUDIO], INFFMPG_STREAMFLAG_AVRESYNC)
			      && ((ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_AUDIO] + 20000) > *avsync_vidtimestamp) )
			){
				spi->bs_leftbytes = PCM_OUTSAMPLES * spi->block_align;
				pds_memset(spi->bitstreambuf, 0, spi->bs_leftbytes);
				funcbit_enable(spi->flags,MPXPLAY_SPI_FLAG_SILENTBLOCK);
				retcode_audio = AVERROR(EAGAIN);
			}
			else
			{
				funcbit_disable(ffmpi->stream_flags[MPXPLAY_STREAMTYPEINDEX_AUDIO], INFFMPG_STREAMFLAG_AVRESYNC);
			}
			//mpxplay_debugf(MPXPLAY_DEBUG_TIMESTAMP,"in_ffmpgdec_decode_audio 3. TS:%lld", avsync_vidtimestamp);
		}
	}

	return retcode_audio;
}
#endif // INFFMP_USE_VIDEO_SYNC

static int inffmpgdec_decodeaudio_load_next_packet(struct ffmpg_demuxer_data_s *ffmpi, struct mpxplay_infile_info_s *miis, AVCodecContext **audio_ctx)
{
	int retcode_audio = 0;
	mpxplay_packetlist_t *pktlist_elem = NULL;
	mpxplay_ffmpgdec_packetqueue_get((void *)ffmpi, &ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO], &pktlist_elem, 0);
	if(pktlist_elem)
	{
		AVCodecContext *packet_ctx = (AVCodecContext *)pktlist_elem->codec_ctx;
		if(packet_ctx)
		{
			AVPacket *pkt_audio;
			if((packet_ctx->codec_type == AVMEDIA_TYPE_AUDIO) && (packet_ctx->codec_id != AV_CODEC_ID_NONE) && avcodec_is_open(packet_ctx) && packet_ctx->codec)
			{
				if(pktlist_elem->flags & MPXPLAY_PACKETLISTFLAG_CLEARBUF){
					avcodec_flush_buffers(packet_ctx);
					//in_fmpeg_audio_codecid_to_waveid(ffmpi, miis, NULL);
					ffmpi->avsync_timestamp = INFFMPG_INVALID_PTS;
					mpxplay_debugf(MPXPLAY_DEBUG_DEMUX, "in_ffmpgdec_decode_audio CLEARBUF 2.");
				}
				pkt_audio = (AVPacket *)(pktlist_elem->frame_pkt);
				if(pkt_audio && (pkt_audio->size > 0)){
					retcode_audio = avcodec_send_packet((AVCodecContext *)pktlist_elem->codec_ctx, pkt_audio);
					if(retcode_audio == 0)
					{
						*audio_ctx = pktlist_elem->codec_ctx;
					}
					else // this can happen normally at new stream or at seeking (continuity error)
					{
						mpxplay_debugf(MPXPLAY_DEBUG_WARNING, "audio avcodec_send_packet %8.8X r:%d s:%d ts:%lld cx:%8.8X px:%8.8X", (int)ffmpi,
								retcode_audio, pkt_audio->size, pktlist_elem->timestamp_us, (int)audio_ctx, (int)pktlist_elem->codec_ctx );
					}
				}else{
					mpxplay_debugf(MPXPLAY_DEBUG_ERROR, "audio avcodec_send_packet %8.8X s:0", (unsigned int)ffmpi);
				}
			}
			else if(++ffmpi->audioctx_init_error_count < 100)
			{
				retcode_audio = AVERROR(ESPIPE);
				mpxplay_debugf(MPXPLAY_DEBUG_WARNING, "mpxplay_ffmpgdec_packetqueue_get CTX UNINIT");
			} // else we drop the packet (containing the incorrect ctx) after a time
		}
		mpxplay_ffmpgdec_queuelistelem_clear(pktlist_elem);
		ffmpi->audioctx_init_error_count = 0;
	}
	else
	{
		retcode_audio = AVERROR(ESPIPE);
		mpxplay_debugf(MPXPLAY_DEBUG_ADECODE, "mpxplay_ffmpgdec_packetqueue_get failed");
	}

	return retcode_audio;
}

static void inffmpgdec_decodeaudio_calculate_sampleoutblocksize(struct ffmpg_demuxer_data_s *ffmpi, int sample_rate, int bytes_per_sample)
{
	mpxp_int64_t pkt_duration;

	if((sample_rate > 0) && (bytes_per_sample > 0))
	{
		ffmpi->audioframe_samples_per_channel = ffmpi->audio_frame->linesize[0] / bytes_per_sample;
		if((ffmpi->audioframe_samples_per_channel <= 0) || ((ffmpi->audio_frame->nb_samples > 0) && (ffmpi->audioframe_samples_per_channel > ffmpi->audio_frame->nb_samples)))
			ffmpi->audioframe_samples_per_channel = ffmpi->audio_frame->nb_samples;
		if(ffmpi->audioframe_samples_per_channel > INFFMPG_MAX_OUTPUT_SAMPLENUM){
			mpxplay_debugf(MPXPLAY_DEBUG_ERROR,"in_ffmpgdec_decode_audio BUFOVERFLOW sp:%d > ms:%d", ffmpi->audioframe_samples_per_channel, INFFMPG_MAX_OUTPUT_SAMPLENUM);
			ffmpi->audioframe_samples_per_channel = INFFMPG_MAX_OUTPUT_SAMPLENUM;
		}

		pkt_duration = (mpxp_int64_t)AV_TIME_BASE * (mpxp_int64_t)ffmpi->audioframe_samples_per_channel / (mpxp_int64_t)sample_rate;
		if(pkt_duration < (AV_TIME_BASE / 20)) // more than 20 audio fps
			ffmpi->audioframe_copy_sampleblocksize = ffmpi->audioframe_samples_per_channel;
		else
			ffmpi->audioframe_copy_sampleblocksize = (AV_TIME_BASE / 25) * (mpxp_int64_t)ffmpi->audioframe_samples_per_channel / pkt_duration;
	}
	else
	{
		ffmpi->audioframe_copy_sampleblocksize = 0;
	}
	ffmpi->audioframe_copy_samplepos = 0;
}

static int inffmpgdec_decodeaudio_receive_next_frame(struct ffmpg_demuxer_data_s *ffmpi, struct mpxplay_infile_info_s *miis, AVCodecContext *audio_ctx)
{
	int retcode_audio = 0;
	if(!ffmpi->audio_frame->data[0] || !ffmpi->audio_frame->linesize[0] || !ffmpi->audioframe_copy_sampleblocksize || (ffmpi->audioframe_copy_samplepos >= ffmpi->audioframe_samples_per_channel))
	{
		retcode_audio = avcodec_receive_frame(audio_ctx, ffmpi->audio_frame);
		if(retcode_audio == 0)
		{
			retcode_audio = in_fmpeg_audio_codecid_to_waveid(ffmpi, miis, ffmpi->audio_frame);
			if(retcode_audio >= 0)
			{
				int64_t new_pts = inffmpg_get_avframe_pts(ffmpi, MPXPLAY_STREAMTYPEINDEX_AUDIO, ffmpi->audio_frame);
				if(new_pts > 0)
					ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_AUDIO] = new_pts;
				inffmpgdec_decodeaudio_calculate_sampleoutblocksize(ffmpi, miis->audio_decoder_infos->freq, ffmpi->outsample_size);
				mpxplay_debugf(MPXPLAY_DEBUG_ADECODE, "avcodec_receive_frame ch:%d chl:%16.16llX ptsa:%lld ptsl:%lld", ffmpi->audio_frame->channels,
				  ffmpi->audio_frame->channel_layout, ffmpi->audio_frame->pts, ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_AUDIO]);
			}
		}
	}
	return retcode_audio;
}

static int inffmpgdec_decodeaudio_copy_audio(struct ffmpg_demuxer_data_s *ffmpi, struct mpxplay_infile_info_s *miis, mpxp_int64_t avsync_vidtimestamp, int sample_blocksize, int channels, int bytes_per_sample)
{
	struct mpxplay_streampacket_info_s *spi = miis->audio_stream;
	int retcode_audio = 0;
#ifdef INFFMP_USE_VIDEO_SYNC
	mpxp_int64_t diff = avsync_vidtimestamp - ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_AUDIO];
	if(ffmpi->audio_frame->data[0] &&
	   (!funcbit_test(ffmpi->stream_flags[MPXPLAY_STREAMTYPEINDEX_AUDIO], INFFMPG_STREAMFLAG_AVSYNC)
		|| !in_ffmpgdec_content_has_programs(ffmpi)
		|| (ffmpi->selected_stream_number[MPXPLAY_STREAMTYPEINDEX_VIDEO] < 0)
		|| ( (avsync_vidtimestamp > 0) && ((ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_AUDIO] + 20000) >= avsync_vidtimestamp)
		   && (diff > -(AV_TIME_BASE * 100)) && (diff < (AV_TIME_BASE * 100)) )  // TODO: 100s diff check ???
	   )
	){
#endif
		if(ffmpi->flags & INFFMPG_FLAG_PLANARAUDIO){
			int ch, sn;
			for(ch = 0; (ch < channels) && ffmpi->audio_frame->data[ch]; ch++){
				uint8_t *framedata = ffmpi->audio_frame->data[ch] + ffmpi->audioframe_copy_samplepos * bytes_per_sample;
				for(sn = 0; sn < sample_blocksize; sn++, framedata += bytes_per_sample)
					pds_memcpy(spi->bitstreambuf + ((sn * channels) + ch) * bytes_per_sample, framedata, bytes_per_sample);
			}
			spi->bs_leftbytes = sample_blocksize * channels * bytes_per_sample;
			mpxplay_debugf(MPXPLAY_DEBUG_ADECODE,"INFFMPG_FLAG_PLANARAUDIO ns:%d ls:%d lb:%d bb:%d", ffmpi->audio_frame->nb_samples, (ffmpi->audio_frame->linesize[0] / bytes_per_sample), spi->bs_leftbytes, spi->bs_bufsize);
		} else {
			int datapos_in = ffmpi->audioframe_copy_samplepos * channels * bytes_per_sample;
			int datasize = sample_blocksize * channels * bytes_per_sample;
			if((datapos_in + datasize) > ffmpi->audio_frame->linesize[0]){
				mpxplay_debugf(MPXPLAY_DEBUG_ERROR,"in_ffmpgdec_decode_audio BUFOVERFLOW-IN ds:%d > bs:%d", (datapos_in + datasize), ffmpi->audio_frame->linesize[0]);
				datasize = ffmpi->audio_frame->linesize[0] - datapos_in;
			}
			if(datasize > spi->bs_bufsize){
				mpxplay_debugf(MPXPLAY_DEBUG_ERROR,"in_ffmpgdec_decode_audio BUFOVERFLOW-OUT ds:%d > bs:%d", datasize, spi->bs_bufsize);
				datasize = spi->bs_bufsize;
			}
			if(datasize > 0)
			{
				spi->bs_leftbytes = datasize;
				pds_memcpy(spi->bitstreambuf, ffmpi->audio_frame->data[0] + datapos_in, datasize);
			}
		}
		if(funcbit_test(ffmpi->stream_flags[MPXPLAY_STREAMTYPEINDEX_AUDIO], INFFMPG_STREAMFLAG_AVSYNC))
		{
			mpxp_int64_t diff = avsync_vidtimestamp - ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_AUDIO];
			mpxplay_debugf(MPXPLAY_DEBUG_TIMESTAMP,"in_ffmpgdec_decode_audio AVSYNC START v:%lld a:%lld dur:%lld diff:%lld", avsync_vidtimestamp,
							ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_AUDIO], ffmpi->audio_frame->pkt_duration, diff);
			funcbit_disable(ffmpi->stream_flags[MPXPLAY_STREAMTYPEINDEX_AUDIO], INFFMPG_STREAMFLAG_AVSYNC);
		}
		funcbit_disable(spi->flags,MPXPLAY_SPI_FLAG_SILENTBLOCK);
#ifdef INFFMP_USE_VIDEO_SYNC
	}
	else
	{
		spi->bs_leftbytes = sample_blocksize * channels;
		pds_memset(spi->bitstreambuf, 0, spi->bs_leftbytes);
		funcbit_enable(spi->flags,MPXPLAY_SPI_FLAG_SILENTBLOCK);
	}
#endif
	return retcode_audio;
}

static void inffmpgdec_decodeaudio_correct_currtimestamp(struct ffmpg_demuxer_data_s *ffmpi, struct mpxplay_streampacket_info_s *spi, int sample_rate)
{
#ifdef MPXPLAY_GUI_QT
	if(ffmpi->codec_ctx[MPXPLAY_STREAMTYPEINDEX_VIDEO] || ffmpi->codec_ctx[MPXPLAY_STREAMTYPEINDEX_SUBTITLE] || mpxplay_config_video_audiovisualization_type)
	{
		mpxp_int64_t timestamp_new = mpxplay_infile_callback_check_audio_timesync(ffmpi->fbds, ffmpi->avsync_clocktime, &ffmpi->avsync_timestamp, ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_AUDIO], spi->bs_leftbytes, sample_rate);
		if(ffmpi->avsync_timestamp <= 0)
		{
			inffmpg_avsync_assign(ffmpi, timestamp_new);
			mpxplay_debugf(MPXPLAY_DEBUG_WARNING, "decodeaudio RESYNC s:%d sy:%llu nba:%d nbv:%d", ffmpi->audioframe_samples_per_channel,
				ffmpi->avsync_timestamp, ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO].nb_packets, ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].nb_packets);
		}
	}
#endif
}

static void inffmpgdec_decodeaudio_audiovisualization_prepare(struct ffmpg_demuxer_data_s *ffmpi, AVCodecContext *audio_ctx, int sample_rate, int sample_blocksize, int channels, int bytes_per_sample)
{
	if( mpxplay_config_video_audiovisualization_type && !ffmpi->codec_ctx[MPXPLAY_STREAMTYPEINDEX_VIDEO] && funcbit_test(ffmpi->flags, INFFMPG_FLAG_CALLBACKVIDEOCFG)
	 && (ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].nb_packets < INFFMPG_QUEUE_PACKETNUM_FILL_MAX) && ffmpi->audio_frame->data[0]
	){
		int retVal;

		AVFrame *visa_frame = av_frame_alloc();
		visa_frame->format = ffmpi->audio_frame->format;
		visa_frame->sample_rate = sample_rate;
		visa_frame->nb_samples = sample_blocksize;
		visa_frame->channels = channels;
		visa_frame->channel_layout = ffmpi->audio_frame->channel_layout;
		if(visa_frame->channel_layout == 0)
			visa_frame->channel_layout = av_get_default_channel_layout(channels);
		visa_frame->pts = ffmpi->avsync_timestamp; // FIXME: false hack, but works
//		visa_frame->pts = ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_AUDIO]; // this have to be the correct, but it doesn't work properly

		if((retVal = av_frame_get_buffer(visa_frame, bytes_per_sample)) == 0)
		{
			int ch;
			sample_blocksize *= bytes_per_sample; // in bytes from here
			if(funcbit_test(ffmpi->flags, INFFMPG_FLAG_PLANARAUDIO))
			{
				for(ch = 0; (ch < channels) && ffmpi->audio_frame->data[ch]; ch++)
				{
					pds_memcpy(visa_frame->data[ch], ffmpi->audio_frame->data[ch] + ffmpi->audioframe_copy_samplepos * bytes_per_sample, sample_blocksize);
					visa_frame->linesize[ch] = sample_blocksize;
				}
			}
			else
			{
				sample_blocksize *= channels;
				pds_memcpy(visa_frame->data[0], ffmpi->audio_frame->data[0] + ffmpi->audioframe_copy_samplepos * channels * bytes_per_sample, sample_blocksize);
				visa_frame->linesize[0] = sample_blocksize;
			}
			mpxplay_ffmpgdec_packetqueue_put(&ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO], MPXPLAY_PACKETTYPE_AVFRAME_AUDIO, 0, audio_ctx, visa_frame); // FIXME: dirty hack
		}
		mpxplay_debugf(MPXPLAY_DEBUG_TIMESTAMP,"inffmpgdec_decodeaudio_audiovisualization_prepare PUT np:%d is:%d os:%d pts:%lld diff:%lld ret:%d if:%d of:%d", ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].nb_packets, ffmpi->audio_frame->nb_samples,
				visa_frame->nb_samples, visa_frame->pts, (ffmpi->avsync_timestamp + pds_gettimeu() - ffmpi->avsync_clocktime - visa_frame->pts),
				retVal, ffmpi->audio_frame->format, visa_frame->format);
	}
}

int in_ffmpgdec_decode_audio(struct ffmpg_demuxer_data_s *ffmpi, struct mpxplay_infile_info_s *miis)
{
	struct mpxplay_streampacket_info_s *spi = miis->audio_stream;
	int retcode_audio = 0, retry, streamtype_index = MPXPLAY_STREAMTYPEINDEX_AUDIO;
	AVCodecContext *audio_ctx;

	if(spi->bs_leftbytes){
		mpxplay_debugf(MPXPLAY_DEBUG_ADECODE,"inffmp_decode_audio LEFT: %d sy: %llu", spi->bs_leftbytes, ffmpi->avsync_timestamp);
		return retcode_audio;
	}

	retry = INFFMPG_QUEUE_PACKETNUM_FILL_STEP * 2;
	do{
		mpxplay_packetlist_t *pktlist_ctx = NULL;
		int retry_audiodec = 3;
		audio_ctx = NULL;

		if(!funcbit_test(ffmpi->flags, INFFMPG_FLAG_DEMUX_THREAD) && !funcbit_test(ffmpi->flags, INFFMPG_FLAG_STILLPICTURE))
			in_ffmpgdec_demux_read_packets(ffmpi);

#ifdef INFFMP_USE_DEMUXER_AUINIT
		if(mpxplay_ffmpgdec_packetqueue_get((void *)ffmpi, &ffmpi->codecctx_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO], &pktlist_ctx, 0) != MPXPLAY_ERROR_OK)
#else
		if(in_ffmpgdec_infile_callback((void *)ffmpi, MPXPLAY_INFILE_CBKCTRL_GET_CODECCTXINIT_FFMPEG, &pktlist_ctx, &streamtype_index, INFFMPG_MUTEXTIME_NOLOCK) != MPXPLAY_ERROR_OK)
#endif
		{
			mpxplay_debugf(MPXPLAY_DEBUG_ADECODE, "in_ffmpgdec_decode_audio 1. pktlist_ctx:%8.8X ctx:%8.8X", (int)pktlist_ctx, (pktlist_ctx)? (int)pktlist_ctx->codec_ctx : 0);
			retcode_audio = AVERROR(ESPIPE);
			break;
		}

		if(!pktlist_ctx || !pktlist_ctx->codec_ctx){
			//mpxplay_debugf(MPXPLAY_DEBUG_ERROR, "in_ffmpgdec_decode_audio 2. pktlist_ctx:%8.8X ctx:%8.8X", (int)pktlist_ctx, (pktlist_ctx)? (int)pktlist_ctx->codec_ctx : 0);
			retcode_audio = AVERROR(ESPIPE);
			break;
		}

		audio_ctx = pktlist_ctx->codec_ctx;

		if((retcode_audio = inffmpgdec_decodeaudio_clearbuf(ffmpi, pktlist_ctx, audio_ctx)) != 0)
			break;

		do{
			mpxp_int64_t avsync_vidtimestamp = 1;
#ifdef INFFMP_USE_VIDEO_SYNC
			if((retcode_audio = inffmpgdec_decodeaudio_silentblock_by_vsync(ffmpi, spi, &avsync_vidtimestamp)) != 0)
			{
				retry = 1;
				break;
			}
#endif
			retcode_audio = inffmpgdec_decodeaudio_receive_next_frame(ffmpi, miis, audio_ctx);
			if(retcode_audio == 0)
			{
				int left_samples = ffmpi->audioframe_samples_per_channel - ffmpi->audioframe_copy_samplepos;
				int sample_blocksize = ((left_samples > 0) && (left_samples <= ffmpi->audioframe_copy_sampleblocksize))? left_samples : ffmpi->audioframe_copy_sampleblocksize;
				int sample_rate = (ffmpi->audio_frame->sample_rate > 0)? ffmpi->audio_frame->sample_rate : audio_ctx->sample_rate;
				int channels = (ffmpi->audio_frame->channels > 0)? ffmpi->audio_frame->channels :  audio_ctx->channels;
				int bytes_per_sample = ffmpi->outsample_size;
				if((sample_blocksize > 0) && (sample_rate > 0) && (channels > 0))
				{
					inffmpgdec_decodeaudio_copy_audio(ffmpi, miis, avsync_vidtimestamp, sample_blocksize, channels, bytes_per_sample);
					inffmpgdec_decodeaudio_correct_currtimestamp(ffmpi, spi, sample_rate);
					inffmpgdec_decodeaudio_audiovisualization_prepare(ffmpi, audio_ctx, sample_rate, sample_blocksize, channels, bytes_per_sample);
					ffmpi->audioframe_copy_samplepos += sample_blocksize;
					ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_AUDIO] += (int64_t)AV_TIME_BASE * (int64_t)sample_blocksize / (int64_t)sample_rate;
				}
				else
				{
					av_frame_unref(ffmpi->audio_frame);
					ffmpi->audioframe_copy_sampleblocksize = 0;
				}
				if(spi->bs_leftbytes && !funcbit_test(spi->flags,MPXPLAY_SPI_FLAG_SILENTBLOCK))
				{
					retry = 1;
					break;
				}
			} else if(retcode_audio == AVERROR(EAGAIN)){
				ffmpi->audioframe_copy_sampleblocksize = 0;
				if((retcode_audio = inffmpgdec_decodeaudio_load_next_packet(ffmpi, miis, &audio_ctx)) != 0)
				{
					retry = 1;
					break;
				}
			} else {
				if(avcodec_is_open(audio_ctx) && audio_ctx->codec)
					avcodec_flush_buffers(audio_ctx);
				mpxplay_debugf(MPXPLAY_DEBUG_ERROR,"inffmp_decode_audio ERR:%d ffmpi:%8.8X ctx:%8.8X avframe:%8.8X", retcode_audio, (int)ffmpi, (int)audio_ctx, (int)ffmpi->audio_frame);
				retcode_audio = AVERROR(ESPIPE);
				retry = 1;
				break;
			}
		}while(--retry_audiodec);
	}while(--retry);

	if(audio_ctx)
	{
		in_ffmpgdec_infile_callback((void *)ffmpi, MPXPLAY_INFILE_CBKCTRL_SET_CODECCTX_CONSOLIDATE, audio_ctx, &streamtype_index, INFFMPG_MUTEXTIME_NOLOCK);
	}

	mpxplay_debugf(MPXPLAY_DEBUG_ADECODE, "inffmp_decode_audio END %8.8X frd:%d ra:%d lb:%d r:%d nba:%d nbv:%d ptsl:%lld", (unsigned int)ffmpi,
		ffmpi->retcode_demux, retcode_audio, spi->bs_leftbytes, retry, ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_AUDIO].nb_packets,
		ffmpi->packet_queues[MPXPLAY_STREAMTYPEINDEX_VIDEO].nb_packets, ffmpi->stream_last_pts[MPXPLAY_STREAMTYPEINDEX_AUDIO]);
	return retcode_audio;
}

#endif //MPXPLAY_LINK_INFILE_FF_MPEG
