//**************************************************************************
//*                     This file is part of the                           *
//*                MMC - Mpxplay Multimedia Commander                      *
//*                   The source code of MMC is                            *
//*        (C) copyright 1998-2014 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: video playing by QT

//#define MPXPLAY_USE_DEBUGF 1
#define DISPQT_DEBUG_OUTPUT NULL // stdout
#define DISPQT_DEBUG_CLOSE stdout
#define DISPQT_DEBUG_ERROR stderr
#define DISPQT_DEBUGOUT_EVENT NULL // stdout
#define DISPQT_DEBUGOUT_CURSOR NULL //stdout
#define DISPQT_DEBUGOUT_FFMPEG NULL // stdout

#include <QtGui>
#include <QtWidgets>
#include "moc_video_qt.h"
#include "moc_mainwindow.h"

extern "C" {
 extern struct mainvars mvps;
 extern unsigned long mpxplay_config_videoplayer_type;
 extern int playstartmsec;
 extern keyconfig kb[];
}

#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
extern struct dispqt_videofilter_processdescriptor_s mpxplay_dispqt_videofilter_transform_descriptors_all[];
extern unsigned int mpxplay_dispqt_videofilter_transform_nb_descriptors;
#endif
extern struct dispqt_videofilter_processdescriptor_s mpxplay_dispqt_videofilter_eq_descriptors_all[];
extern unsigned int mpxplay_dispqt_videofilter_eq_nb_descriptors;

DispQtVideoPlayer::DispQtVideoPlayer(MainWindow *main_window, QWidget *parent) : QWidget(parent)
{
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "DispQtVideoPlayer BEGIN vt:%d", mpxplay_config_videoplayer_type);
	this->main_window = main_window;
	this->parent_widget = parent;
	this->is_wait_for_open = false;
	this->is_video_show = false;
	this->is_video_open = false;
	this->is_playing = false;
	this->is_wait_for_close = false;
	this->is_video_closed = false;
	this->ptr_handle_curr = this->ptr_handle_new = this->ptr_handle_close = NULL;
	this->video_widget_context = NULL;
	this->video_event_filter = NULL;
#ifdef MPXPLAY_LINK_QTMEDIA
	this->qt_media_player = NULL;
#endif
#ifdef MPXPLAY_LINK_QTAV
	this->qtav_media_player = NULL;
	this->qtav_video_output = NULL;
#endif

#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
	this->video_frame_filter_transform_process = new DispQtVideoFrameFilter(main_window, this, &mpxplay_dispqt_videofilter_transform_descriptors_all[0], mpxplay_dispqt_videofilter_transform_nb_descriptors);
#endif
	this->video_frame_filter_eq_process = new DispQtVideoFrameFilter(main_window, this, &mpxplay_dispqt_videofilter_eq_descriptors_all[0], mpxplay_dispqt_videofilter_eq_nb_descriptors);

	switch(mpxplay_config_videoplayer_type)
	{
#ifdef MPXPLAY_LINK_QTMEDIA
		case MPXPLAY_VIDEOPLAYERTYPE_QTMEDIA:
			this->video_widget_context = new QVideoWidget(this);
			break;
#endif
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
		case MPXPLAY_VIDEOPLAYERTYPE_FFMPEG:
			this->video_widget_context = new FFMpegVideoWidget(main_window, this);
			mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "FFMpegVideoWidget : %8.8X", (unsigned int) this->video_widget_context);
			break;
#endif // MPXPLAY_LINK_ORIGINAL_FFMPEG
	}

	this->video_mediaplayer_open(false);
	this->video_widget_init();
	this->video_layout = new QHBoxLayout;

#ifdef MPXPLAY_LINK_QTMEDIA
	if((mpxplay_config_videoplayer_type == MPXPLAY_VIDEOPLAYERTYPE_QTMEDIA) && this->qt_media_player)
		this->qt_media_player->setVideoOutput((QVideoWidget *)this->video_widget_context);
#endif

	this->video_event_filter = new DispQtVideoEventFilter(main_window, this, this->video_widget_context);
	qApp->installEventFilter(this->video_event_filter);

	this->video_layout->addWidget(this->video_widget_context);
	this->video_layout->setMargin(1);
	this->video_layout->setSpacing(0);
	this->setLayout(this->video_layout);

	connect(this, SIGNAL(signal_video_open()), this, SLOT(video_open()));
	connect(this, SIGNAL(signal_video_close()), this, SLOT(video_close()));
	connect(this, SIGNAL(signal_video_play(bool)), this, SLOT(video_play(bool)));
	connect(this, SIGNAL(signal_video_pause()), this, SLOT(video_pause()));
	connect(this, SIGNAL(signal_video_switch_play()), this, SLOT(video_switch_play()));
	connect(this, SIGNAL(signal_video_seek_ms(qint64)), this, SLOT(video_seek_ms(qint64)));
	connect(this, SIGNAL(signal_video_set_fullscreen(bool)), this, SLOT(video_set_fullscreen(bool)));
	connect(this, SIGNAL(signal_video_switch_fullscreen()), this, SLOT(video_switch_fullscreen()));
	connect(this, SIGNAL(signal_video_apply_fullscreen(bool)), this, SLOT(video_apply_fullscreen(bool)));
	connect(this, SIGNAL(signal_video_check_windows()), this, SLOT(video_check_windows()));
	connect(this, SIGNAL(signal_video_apply_windows()), this, SLOT(video_apply_windows()));
	connect(this, SIGNAL(signal_video_event_stop()), this, SLOT(video_handle_event_stop()));
	connect(this, SIGNAL(signal_video_set_speed(int)), this, SLOT(video_set_speed(int)));
	connect(this, SIGNAL(signal_audio_set_volume(int)), this, SLOT(audio_set_volume(int)));

#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
	connect(this, SIGNAL(signal_video_surface_resize_refresh()), this, SLOT(video_surface_resize_refresh()));
	connect(this, SIGNAL(signal_video_surface_filtered_trans_refresh()), this, SLOT(video_surface_filtered_trans_refresh()));
	connect(this, SIGNAL(signal_video_surface_filtered_eq_refresh()), this, SLOT(video_surface_filtered_eq_refresh()));
	connect(this, SIGNAL(signal_ffmpeg_config_callback(unsigned int,mpxplay_infile_callback_t *, void *)), this, SLOT(video_ffmpeg_config_callback(unsigned int,mpxplay_infile_callback_t *, void *)));
#endif

	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "DispQtVideoPlayer END");
}

DispQtVideoPlayer::~DispQtVideoPlayer()
{
	qint64 pos_ms = this->video_tell_ms();
	if(pos_ms >= 0)
		playstartmsec = (int)pos_ms;
	this->video_set_fullscreen(false);
	this->video_close();
	if(this->video_event_filter){
		qApp->removeEventFilter(this->video_event_filter);
		delete this->video_event_filter;
		this->video_event_filter = NULL;
	}
	this->video_mediaplayer_close();
	if(this->video_widget_context){
		switch(mpxplay_config_videoplayer_type)
		{
#ifdef MPXPLAY_LINK_QTMEDIA
			case MPXPLAY_VIDEOPLAYERTYPE_QTMEDIA:
				delete ((QVideoWidget *)this->video_widget_context);
				break;
#endif
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
			case MPXPLAY_VIDEOPLAYERTYPE_FFMPEG:
				delete ((FFMpegVideoWidget *)this->video_widget_context);
				break;
#endif // MPXPLAY_LINK_ORIGINAL_FFMPEG
		}
	}
	mpxplay_debugf(DISPQT_DEBUG_CLOSE, "close adone:%d ms:%d", mvps.adone, playstartmsec);
}

void DispQtVideoPlayer::video_mediaplayer_open(bool lock_qmedia)
{
	bool lock_success = false;
	if(lock_qmedia)
		lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "video_mediaplayer_open BEGIN");
	try {
		switch(mpxplay_config_videoplayer_type)
		{
#ifdef MPXPLAY_LINK_QTMEDIA
			case MPXPLAY_VIDEOPLAYERTYPE_QTMEDIA:
				if(!this->qt_media_player){
					this->qt_media_player = new QMediaPlayer(this);
					if(this->qt_media_player){
						if(this->video_widget_context)
							this->qt_media_player->setVideoOutput((QVideoWidget *)this->video_widget_context);
						connect(this->qt_media_player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), this, SLOT(status_changed(QMediaPlayer::MediaStatus)));
						connect(this->qt_media_player, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(display_error_message(QMediaPlayer::Error)));
					}
				}
				break;
#endif
#ifdef MPXPLAY_LINK_QTAV
			case MPXPLAY_VIDEOPLAYERTYPE_QTAV:
				if(!this->qtav_media_player){
					mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "video_mediaplayer_open AVPlayer BEGIN");
					this->qtav_media_player = new QtAV::AVPlayer(this);
					this->qtav_media_player->setAsyncLoad(false);
				}
				if(!this->qtav_video_output){
					mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "video_mediaplayer_open VideoOutput BEGIN");
#ifdef MPXPLAY_QTAV_VIDEORENDER
					this->qtav_video_output = QtAV::VideoRenderer::create(QtAV::VideoRendererId_OpenGLWidget);
#else
					this->qtav_video_output = new QtAV::VideoOutput(QtAV::VideoRendererId_Direct2D, this);
#endif
					if(!this->qtav_video_output){
						mpxplay_debugf(DISPQT_DEBUG_ERROR, "video_mediaplayer_open VideoOutput FAILED");
					}else{
						mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "video_mediaplayer_open VideoOutput %8s", QtAV::VideoRenderer::name(this->qtav_video_output->id()));
					}
				}
				if(this->qtav_media_player && this->qtav_video_output){
					mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "video_mediaplayer_open setRenderer BEGIN");
					if(!this->qtav_video_output->isAvailable() || !this->qtav_video_output->widget()){
						mpxplay_debugf(DISPQT_DEBUG_ERROR, "video_mediaplayer_open setRenderer FAILED");
					}
					this->qtav_media_player->setRenderer(this->qtav_video_output);
					this->video_widget_context = this->qtav_video_output->widget();
				}
				break;
#endif
		}
	} catch(int e) {
		mpxplay_debugf(DISPQT_DEBUG_ERROR, "EXCEPTION video_mediaplayer_open %d", e);
		this->video_mediaplayer_close();
	}
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "video_mediaplayer_open END %8.8X", (int)this->video_widget_context);
	if(lock_qmedia && lock_success)
		this->mutex_qmedia.unlock();
}

void DispQtVideoPlayer::video_mediaplayer_close(void)
{
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	this->is_playing = false;
#ifdef MPXPLAY_LINK_QTMEDIA
	if(this->qt_media_player) {
		this->video_cursor_select(QMediaPlayer::UnknownMediaStatus);
		if(this->is_video_open)
			this->qt_media_player->stop();
		disconnect(this->qt_media_player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), this, SLOT(status_changed(QMediaPlayer::MediaStatus)));
		disconnect(this->qt_media_player, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(display_error_message(QMediaPlayer::Error)));
		delete this->qt_media_player;
		this->qt_media_player = NULL;
	}
#endif
#ifdef MPXPLAY_LINK_QTAV
	if(this->qtav_media_player){
		if(this->is_video_open)
			this->qtav_media_player->stop();
		delete this->qtav_media_player;
		this->qtav_media_player = NULL;
	}
#endif
	this->is_video_open = false;
	this->is_wait_for_open = false;
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "video_mediaplayer_close END %8.8X", (int)this->video_widget_context);
	if(lock_success)
		this->mutex_qmedia.unlock();
}

void DispQtVideoPlayer::video_request_open(void *handle_ptr, char *utf8_filename)
{
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	this->is_wait_for_open = true;
	this->ptr_handle_new = handle_ptr;
	this->media_name = QString::fromUtf8(utf8_filename);
	if(lock_success)
		this->mutex_qmedia.unlock();
	emit this->signal_video_open();
}

void DispQtVideoPlayer::video_open(void)
{
	bool lock_success = this->mutex_player.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	this->video_stop(this->ptr_handle_curr, false);
	this->video_mediaplayer_open(true);
	if(!this->video_widget_context){
		if(lock_success)
			this->mutex_player.unlock();
		return;
	}
	try {
		switch(mpxplay_config_videoplayer_type)
		{
#ifdef MPXPLAY_LINK_QTMEDIA
			case MPXPLAY_VIDEOPLAYERTYPE_QTMEDIA:
				this->video_widget_context->setUpdatesEnabled(false);
				this->media_url = QUrl::fromLocalFile(this->media_name);
				this->media_content = QMediaContent(this->media_url);
				if(!this->media_content.isNull()) {
					this->qt_media_player->setMedia(this->media_content);
					if(this->qt_media_player->error() == QMediaPlayer::NoError)
						this->ptr_handle_curr = this->ptr_handle_new;
					//else
					//	this->video_mediaplayer_close(); // FIXME: ???
				}
				mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "OPEN err:%d %8.8X \"%s\"", ((this->qt_media_player)? (int)this->qt_media_player->error() : -99), (unsigned long)this->ptr_handle_curr, this->media_name.toUtf8().data());
				break;
#endif
#ifdef MPXPLAY_LINK_QTAV
			case MPXPLAY_VIDEOPLAYERTYPE_QTAV:
				this->video_widget_context->setUpdatesEnabled(false);
				this->qtav_media_player->setFile(this->media_name);
				if(this->qtav_media_player->load())
				{
					this->ptr_handle_curr = this->ptr_handle_new;
					this->is_video_open = true;
					this->video_widget_context->setUpdatesEnabled(true);
				}// else
				//	this->video_mediaplayer_close(); // FIXME: ???
				this->is_wait_for_open = false;
				mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "OPEN %8.8X \"%s\"", (unsigned long)this->ptr_handle_curr, this->media_name.toUtf8().data());
				break;
#endif
		}
	} catch(int e) {
		mpxplay_debugf(DISPQT_DEBUG_ERROR, "EXCEPTION video_open %d", e);
	}
	if(lock_success)
		this->mutex_player.unlock();
}

bool DispQtVideoPlayer::video_wait_for_open(void)
{
	int counter = DISPQT_VIDEO_OPEN_TIMEOUT / MPXPLAY_THREADS_SHORTTASKSLEEP; // ca. 5 secs
	this->main_window->mainwin_statusbar_msg_send((char *)"Waiting for video open...");
	while(this->is_wait_for_open && (--counter) && (pds_look_extgetch()!=KEY_ESC)) {pds_threads_sleep(MPXPLAY_THREADS_SHORTTASKSLEEP);}
	this->main_window->mainwin_statusbar_msg_send((this->is_video_open)? (char *)"OK" : (char *)"Video open failed!");
	return this->is_video_open;
}

void DispQtVideoPlayer::video_request_close(void *handle_ptr)
{
	bool lock_success = this->mutex_player.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	this->ptr_handle_close = handle_ptr;
	this->is_wait_for_close = true;
	if(lock_success)
		this->mutex_player.unlock();
	emit this->signal_video_close();
}

void DispQtVideoPlayer::video_close(void)
{
	bool lock_success = this->mutex_player.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	if(!this->ptr_handle_close){
		if(lock_success)
			this->mutex_player.unlock();
		return;
	}
	this->is_video_closed = false;
	this->video_stop(this->ptr_handle_close, true);
	this->ptr_handle_close = NULL;
	this->is_video_closed = !this->is_video_open;
	this->is_wait_for_close = false;
	if(lock_success)
		this->mutex_player.unlock();
}

bool DispQtVideoPlayer::video_wait_for_close(void)
{
	int counter = DISPQT_VIDEO_OPEN_TIMEOUT / MPXPLAY_THREADS_SHORTTASKSLEEP; // ca. 5 secs
	while(this->is_wait_for_close && (--counter) && (pds_look_extgetch()!=KEY_ESC)) {pds_threads_sleep(MPXPLAY_THREADS_SHORTTASKSLEEP);}
	return this->is_video_closed;
}

void DispQtVideoPlayer::video_stop(void *handle_ptr, bool close_media)
{
	bool lock_success = false;
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "CLOSE co:%d h:%8.8X c:%8.8X", (int)close_media, (unsigned long)handle_ptr, (unsigned long)this->ptr_handle_curr);
	if(handle_ptr && handle_ptr != this->ptr_handle_curr)
		return;
	lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	this->is_playing = false;
	this->ptr_handle_curr = NULL;
	if(this->is_video_open) {
		this->is_video_open = false;
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
		this->video_ffmpeg_configcallback_wrapper(MPXPLAY_INFILE_CBKCFG_SRVFFMV_CLEAR_CALLBACKS, NULL, NULL); // TODO: ffmpeg audio files?
#endif
#ifdef MPXPLAY_LINK_QTMEDIA
		if(this->qt_media_player)
			this->qt_media_player->stop();
#endif
#ifdef MPXPLAY_LINK_QTAV
		if(this->qtav_media_player)
			this->qtav_media_player->stop();
#endif
	}
	if(lock_success)
		this->mutex_qmedia.unlock();
	if(close_media)
		this->video_mediaplayer_close();
}

#ifdef MPXPLAY_LINK_QTMEDIA
void DispQtVideoPlayer::status_changed(QMediaPlayer::MediaStatus status)
{
	this->video_cursor_select(status);

    switch (status) {
    	case QMediaPlayer::UnknownMediaStatus:
    	case QMediaPlayer::NoMedia:
    		this->is_video_open = false;
    		break;
    	case QMediaPlayer::LoadingMedia:
    		this->main_window->mainwin_statusbar_msg_send((char *)"Loading...");
    		break;
    	case QMediaPlayer::LoadedMedia:
    	case QMediaPlayer::BufferedMedia:
    		if(this->is_wait_for_open) {
    			this->video_widget_context->setUpdatesEnabled(true);
    			this->is_video_open = true;
    			this->is_wait_for_open = false;
    		}
    		break;
    	case QMediaPlayer::BufferingMedia:
    		this->main_window->mainwin_statusbar_msg_send((char *)"Buffering...");
    		break;
    	case QMediaPlayer::StalledMedia:
    		this->main_window->mainwin_statusbar_msg_send((char *)"Media Stalled");
    		this->is_video_open = false;
    		break;
    	case QMediaPlayer::EndOfMedia:
    		this->is_video_open = false;
    		mvps.adone = ADONE_EOF;  // !!!
    		break;
    	case QMediaPlayer::InvalidMedia:
    		display_error_message(QMediaPlayer::FormatError);
    		break;
    }
    mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "STATUS: %d", (int)status);
}

void DispQtVideoPlayer::video_cursor_select(QMediaPlayer::MediaStatus status)
{
#ifndef QT_NO_CURSOR
    if (status == QMediaPlayer::LoadingMedia ||
        status == QMediaPlayer::BufferingMedia ||
        status == QMediaPlayer::StalledMedia)
        setCursor(QCursor(Qt::BusyCursor));
    else
        unsetCursor();
#endif
}

void DispQtVideoPlayer::display_error_message(QMediaPlayer::Error err)
{
	if(this->qt_media_player){
		this->main_window->mainwin_statusbar_msg_send((char *)this->qt_media_player->errorString().toUtf8().data());
		mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "ERROR: %d \"%s\"", (int)err, (char *)this->qt_media_player->errorString().toUtf8().data());
	}
	if(err != QMediaPlayer::NoError) {
		if(this->is_video_open)
			mvps.adone = ADONE_EOF;  // !!! ???
		this->video_mediaplayer_close();
	}
}
#endif

void DispQtVideoPlayer::video_play(bool startflag)
{
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	struct mmc_dispqt_config_s *gcfg = this->main_window->gui_config;
	this->is_playing = false;
	switch(mpxplay_config_videoplayer_type)
	{
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
		case MPXPLAY_VIDEOPLAYERTYPE_FFMPEG:
			this->video_ffmpeg_configcallback_wrapper(MPXPLAY_INFILE_CBKCFG_SRVFFMV_UNPAUSE, NULL, NULL);
			if(this->is_video_open)
				this->is_playing = true;
			break;
#endif
#ifdef MPXPLAY_LINK_QTMEDIA
		case MPXPLAY_VIDEOPLAYERTYPE_QTMEDIA:
			if(this->is_video_open && this->qt_media_player){
				this->qt_media_player->play();
				this->is_playing = true;
			}
			break;
#endif
#ifdef MPXPLAY_LINK_QTAV
		case MPXPLAY_VIDEOPLAYERTYPE_QTAV:
			if(this->is_video_open && this->qtav_media_player){
				this->qtav_media_player->play();
				this->qtav_media_player->pause(false);
				this->is_playing = true;
			}
			break;
#endif
	}
	if(lock_success)
		this->mutex_qmedia.unlock();
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "video_play this->is_playing:%d", this->is_playing);
	unsigned long video_control = gcfg->video_control;
	if(this->is_playing && !(gcfg->transparent_control & DISPQT_CONFIG_TRANSCTRL_DESKTOPTRANS_ENABLED) && ((startflag && (video_control & DISPQT_CONFIG_VIDEOCTRL_PLAYSTART_FULLSCREEN)) || (video_control & DISPQT_CONFIG_VIDEOCTRL_ALWAYS_FULLSCREEN)))
		emit this->signal_video_set_fullscreen(true);
}

void DispQtVideoPlayer::video_pause(void)
{
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	if(this->is_video_open){
		switch(mpxplay_config_videoplayer_type)
		{
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
			case MPXPLAY_VIDEOPLAYERTYPE_FFMPEG:
				this->video_ffmpeg_configcallback_wrapper(MPXPLAY_INFILE_CBKCFG_SRVFFMV_PAUSE, NULL, NULL);
				break;
#endif
#ifdef MPXPLAY_LINK_QTMEDIA
			case MPXPLAY_VIDEOPLAYERTYPE_QTMEDIA:
				if(this->qt_media_player)
					this->qt_media_player->pause();
				break;
#endif
#ifdef MPXPLAY_LINK_QTAV
			case MPXPLAY_VIDEOPLAYERTYPE_QTAV:
				if(this->qtav_media_player)
					this->qtav_media_player->pause(true);
				break;
#endif
		}
	}
	this->is_playing = false;
	if(lock_success)
		this->mutex_qmedia.unlock();
}

void DispQtVideoPlayer::video_seek_ms(qint64 pos_ms)
{
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	if(this->is_video_open){
		switch(mpxplay_config_videoplayer_type)
		{
#ifdef MPXPLAY_LINK_QTMEDIA
			case MPXPLAY_VIDEOPLAYERTYPE_QTMEDIA:
				if(this->qt_media_player)
					this->qt_media_player->setPosition(pos_ms);
				break;
#endif
#ifdef MPXPLAY_LINK_QTAV
			case MPXPLAY_VIDEOPLAYERTYPE_QTAV:
				if(this->qtav_media_player)
					this->qtav_media_player->setPosition(pos_ms);
				break;
#endif
		}
	}
	if(lock_success)
		this->mutex_qmedia.unlock();
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "SEEK: %d", (int)pos_ms);
}

qint64 DispQtVideoPlayer::video_tell_ms(void)
{
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	qint64 pos_ms = -1;
	if(this->is_video_open){
		switch(mpxplay_config_videoplayer_type)
		{
#ifdef MPXPLAY_LINK_QTMEDIA
			case MPXPLAY_VIDEOPLAYERTYPE_QTMEDIA:
				if(this->qt_media_player)
					pos_ms = this->qt_media_player->position();
				break;
#endif
#ifdef MPXPLAY_LINK_QTAV
			case MPXPLAY_VIDEOPLAYERTYPE_QTAV:
				if(this->qtav_media_player)
					pos_ms = this->qtav_media_player->position();
				break;
#endif
		}
	}
	if(lock_success)
		this->mutex_qmedia.unlock();
	//mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "TELL: %d", (int)pos_ms);
	return pos_ms;
}

qint64 DispQtVideoPlayer::video_duration_ms(void)
{
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	qint64 dur_ms = -1;
	if(this->is_video_open){
		switch(mpxplay_config_videoplayer_type)
		{
#ifdef MPXPLAY_LINK_QTMEDIA
			case MPXPLAY_VIDEOPLAYERTYPE_QTMEDIA:
				if(this->qt_media_player)
					dur_ms = this->qt_media_player->duration();
				break;
#endif
#ifdef MPXPLAY_LINK_QTAV
			case MPXPLAY_VIDEOPLAYERTYPE_QTAV:
				if(this->qtav_media_player)
					dur_ms = this->qtav_media_player->duration();
				break;
#endif
		}
	}
	if(lock_success)
		this->mutex_qmedia.unlock();
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "DUR: %d", (int)dur_ms);
	return dur_ms;
}

void DispQtVideoPlayer::video_set_speed(int speed) // 1000 = 100%
{
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	if(this->is_video_open){
		if(speed < 100)
			speed = 100;
		else if(speed > 10000)
			speed = 10000;
		qreal rate = (qreal)speed / 1000.0;
		switch(mpxplay_config_videoplayer_type)
		{
#ifdef MPXPLAY_LINK_QTMEDIA
			case MPXPLAY_VIDEOPLAYERTYPE_QTMEDIA:
				if(this->qt_media_player)
					this->qt_media_player->setPlaybackRate(rate);
				break;
#endif
#ifdef MPXPLAY_LINK_QTAV
			case MPXPLAY_VIDEOPLAYERTYPE_QTAV:
				if(this->qtav_media_player)
					this->qtav_media_player->setSpeed(rate);
				break;
#endif
		}
		mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "SPD: %d %1.3f", speed, rate);
	}
	if(lock_success)
		this->mutex_qmedia.unlock();
}

void DispQtVideoPlayer::audio_set_volume(int vol) // 100 = 100%
{
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	if(this->is_video_open) {
		if(vol < 0)
			vol = 0;
		switch(mpxplay_config_videoplayer_type)
		{
#ifdef MPXPLAY_LINK_QTMEDIA
			case MPXPLAY_VIDEOPLAYERTYPE_QTMEDIA:
				if(vol > 100)
					vol = 100;
				if(this->qt_media_player)
					this->qt_media_player->setVolume(vol);
				break;
#endif
#ifdef MPXPLAY_LINK_QTAV
			case MPXPLAY_VIDEOPLAYERTYPE_QTAV:
				if(this->qtav_media_player && this->qtav_media_player->audio())
					this->qtav_media_player->audio()->setVolume((qreal)vol / 100.0);
				break;
#endif
		}
		mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "VOL: %d", vol);
	}
	if(lock_success)
		this->mutex_qmedia.unlock();
}

#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
void DispQtVideoPlayer::video_surface_resize_refresh(void)
{
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	if(mpxplay_config_videoplayer_type == MPXPLAY_VIDEOPLAYERTYPE_FFMPEG)
		emit ((FFMpegVideoWidget *)this->video_widget_context)->signal_ffmpeg_swctx_resize(-1,-1,true); // FIXME: hack
	if(lock_success)
		this->mutex_qmedia.unlock();
}

void DispQtVideoPlayer::video_surface_filtered_eq_refresh(void)
{
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	if(mpxplay_config_videoplayer_type == MPXPLAY_VIDEOPLAYERTYPE_FFMPEG)
		emit ((FFMpegVideoWidget *)this->video_widget_context)->signal_ffmpeg_filtered_eq_refresh();
	if(lock_success)
		this->mutex_qmedia.unlock();
}

void DispQtVideoPlayer::video_surface_filtered_trans_refresh(void)
{
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	if(mpxplay_config_videoplayer_type == MPXPLAY_VIDEOPLAYERTYPE_FFMPEG)
		emit ((FFMpegVideoWidget *)this->video_widget_context)->signal_ffmpeg_filtered_trans_refresh();
	if(lock_success)
		this->mutex_qmedia.unlock();
}
#endif

void DispQtVideoPlayer::video_set_fullscreen(bool set_fs)
{
	if(!this->video_widget_context)
		return;
	bool is_fs = this->video_widget_context->isFullScreen();
	if(set_fs != is_fs)
		this->video_switch_fullscreen();
}

void DispQtVideoPlayer::video_switch_fullscreen(void)
{
	if(!this->video_widget_context)
		return;
	if(this->video_widget_context->isFullScreen())
		emit this->signal_video_apply_fullscreen(false);
	else
		emit this->main_window->signal_video_fullscreen(true);
}

int DispQtVideoPlayer::video_get_value(unsigned int ctrl)
{
	int value = 0;
	switch(ctrl)
	{
		case VideoCtrlValue_FullScreen:
			if(this->video_widget_context)
				value = this->video_widget_context->isFullScreen();
			break;
		case VideoCtrlValue_Height:
			if(this->video_widget_context)
				value = this->video_widget_context->height();
			break;
		case VideoCtrlValue_Speed:
			break;
	}
	return value;
}

void DispQtVideoPlayer::video_set_value(unsigned int ctrl, int value)
{
	Qt::WindowFlags flags;
	switch(ctrl)
	{
		case VideoCtrlValue_FullScreen:
			if(!this->video_widget_context)
				break;
			switch(mpxplay_config_videoplayer_type){
#ifdef MPXPLAY_LINK_QTAV
				case MPXPLAY_VIDEOPLAYERTYPE_QTAV:
#endif
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
				case MPXPLAY_VIDEOPLAYERTYPE_FFMPEG:
#endif
#if defined(MPXPLAY_LINK_QTAV) || defined(MPXPLAY_LINK_ORIGINAL_FFMPEG)
					//int max_x = QApplication::desktop()->screenGeometry().width();
					//int max_y = QApplication::desktop()->screenGeometry().height();
					this->setUpdatesEnabled(false);  // maybe it's not needed, just for safe
					this->video_layout->setEnabled(false);
					this->video_widget_context->setUpdatesEnabled(false);
					if(value){
						flags = this->video_widget_context->windowFlags();
						this->video_wdg_nonfullscreen_flags = flags;
						funcbit_enable(flags, Qt::Window);
						funcbit_disable(flags, Qt::SubWindow);
						this->video_widget_context->setParent(NULL);
					}else{
						flags = this->video_wdg_nonfullscreen_flags;
					}
					this->video_widget_context->setWindowFlags(flags);
					this->video_widget_context->setWindowState((value) ? (this->video_widget_context->windowState() | Qt::WindowFullScreen) : (this->video_widget_context->windowState() & ~Qt::WindowFullScreen));
					if(value){
						//this->qtav_video_output->resizeRenderer(max_x, max_y);
						//this->video_widget_context->resize(max_x, max_y);
						//this->qtav_video_output->setOutAspectRatioMode(QtAV::VideoRenderer::VideoAspectRatio);
						this->video_widget_context->setUpdatesEnabled(true);
						this->video_widget_context->setVisible(true);
					}else{
						this->video_widget_context->setParent(this);
						this->video_widget_context->setUpdatesEnabled(true);
						this->video_widget_context->setVisible(true);
						this->video_layout->setEnabled(true);
					}
					this->setUpdatesEnabled(true);  // maybe it's not needed, just for safe
					this->setVisible(true);         //
					mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "video_set_value %8.8X %8.8X m:%8.8X", flags, this->video_widget_context->windowFlags(), this->main_window->windowFlags());
					break;
#endif
#if defined(MPXPLAY_LINK_QTMEDIA)
				case MPXPLAY_VIDEOPLAYERTYPE_QTMEDIA:
	#if (QT_VERSION >= 0x050400)  // note: it's true for MMC static Qt build only (official Qt 5.3.2 also doesn't work properly)
					this->setUpdatesEnabled(false);  // maybe it's not needed, just for safe
					this->video_layout->setEnabled(false);
					this->video_widget_context->setUpdatesEnabled(false);
					if(value){
						this->video_widget_context->setParent(NULL);
					}
					((QVideoWidget *)this->video_widget_context)->setFullScreen(value);
					if(value){
						this->video_widget_context->setUpdatesEnabled(true);
						this->video_widget_context->setVisible(true);
					}else{
						this->video_widget_context->setParent(this);
						this->video_widget_context->setUpdatesEnabled(true);
						this->video_widget_context->setVisible(true);
						this->video_layout->setEnabled(true);
					}
					this->setUpdatesEnabled(true);  // maybe it's not needed, just for safe
					this->setVisible(true);         //
	#else
					((QVideoWidget *)this->video_widget_context)->setFullScreen(value);
	#endif
					break;
#endif
			}
			break;
		case VideoCtrlValue_Speed:
			break;
	}
}

void DispQtVideoPlayer::video_apply_fullscreen(bool full)
{
	struct mmc_dispqt_config_s *gcfg = this->main_window->gui_config;
	if(!(gcfg->mainwin_control & DISPQT_CONFIG_MAINWINCTRL_SHOW_VIDEO) && this->video_widget_context){
		this->video_widget_control((full)? DISPQT_CONFIG_MAINWINCTRL_SHOW_VIDEO : gcfg->mainwin_control);
	}
	this->video_set_value(VideoCtrlValue_FullScreen, (int)full);
	this->video_widget_context->setMouseTracking(full);
	emit this->signal_video_apply_windows();
}

void DispQtVideoPlayer::video_check_windows(void)
{
    if(this->video_get_value(VideoCtrlValue_FullScreen) && this->is_video_open)
        this->main_window->mainwin_video_check_show(QCursor::pos().x(), QCursor::pos().y()); // TODO: audio only ffmpeg files? (works but why)
    else if(this->video_event_filter)
    	this->video_event_filter->mouse_cursor_show_timed(false);
}

void DispQtVideoPlayer::video_apply_windows(void)
{
	this->video_check_windows();
	if(!this->video_get_value(VideoCtrlValue_FullScreen)){
	    this->main_window->mainwin_video_check_show(-1, -1); // FIXME: better idea? (to hide unnecessary window(s) at non-fullscreen)
		emit this->main_window->signal_video_fullscreen(false);
	}
}

void DispQtVideoPlayer::video_handle_event_stop(void)
{
	if(this->main_window->gui_config->video_control & DISPQT_CONFIG_VIDEOCTRL_EXIT_FULLSCREEN_AT_STOP)
		this->video_set_fullscreen(false);
	else
		emit this->main_window->signal_video_mainwin_wakeup(true, false);
}

void DispQtVideoPlayer::video_mouse_cursor_show(bool timed)
{
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	if(this->video_event_filter)
		this->video_event_filter->mouse_cursor_show_timed(timed);
	if(lock_success)
		this->mutex_qmedia.unlock();
}

void DispQtVideoPlayer::video_mouse_cursor_forbid_hide(bool forbid)
{
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	if(this->video_event_filter)
		this->video_event_filter->mouse_cursor_forbid_hide(forbid);
	if(lock_success)
		this->mutex_qmedia.unlock();
}

void DispQtVideoPlayer::video_widget_init(void)
{
	this->video_widget_context->setFocusPolicy(Qt::ClickFocus);

	this->video_widget_context->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored);
	this->video_widget_context->resize(150, 100);

	QPalette p = palette();
	p.setColor(QPalette::Window, Qt::black);
	this->video_widget_context->setPalette(p);

	this->video_widget_context->setAttribute(Qt::WA_OpaquePaintEvent);
}

void DispQtVideoPlayer::video_widget_control(unsigned long mainwin_control)
{
	if(mainwin_control & DISPQT_CONFIG_MAINWINCTRL_SHOW_VIDEO){
		this->video_widget_context->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored);
		this->video_widget_context->resize(150, 100);
		this->video_widget_context->show();
	} else
		this->video_widget_context->hide();
}

#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
int DispQtVideoPlayer::video_ffmpeg_configcallback_create(unsigned int cfgcmd, mpxplay_infile_callback_t *cbk_func, void *passdata)
{
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
	this->is_wait_for_open = true;
	if(lock_success)
		this->mutex_qmedia.unlock();
	mpxplay_debugf(DISPQT_DEBUGOUT_FFMPEG, "video_ffmpeg_configcallback_create EMIT %8.8X %8.8X %8.8X", cfgcmd, cbk_func, passdata);

	emit this->signal_ffmpeg_config_callback(cfgcmd, cbk_func, passdata);

	mpxp_int64_t end_time = pds_gettimem() + DISPQT_VIDEO_OPEN_TIMEOUT;
	while(this->is_wait_for_open && (pds_gettimem() < end_time)) {pds_threads_sleep(MPXPLAY_THREADS_SHORTTASKSLEEP);}

	mpxplay_debugf(DISPQT_DEBUGOUT_FFMPEG, "video_ffmpeg_configcallback_create END %8.8X %8.8X %8.8X", cfgcmd, cbk_func, passdata);

	return ((!this->is_wait_for_open)? 0 : -1);
}

void DispQtVideoPlayer::video_ffmpeg_configcallback_wrapper(unsigned int cfgcmd, mpxplay_infile_callback_t *cbk_func, void *passdata)
{
	if(mpxplay_config_videoplayer_type == MPXPLAY_VIDEOPLAYERTYPE_FFMPEG){
		QWidget *video_widget = this->video_get_widgetptr();
		if(video_widget)
			this->is_video_open = ((FFMpegVideoWidget *)video_widget)->videowidget_ffmpeg_config_callback(cfgcmd, cbk_func, passdata);
	}
}

void DispQtVideoPlayer::video_ffmpeg_config_callback(unsigned int cfgcmd, mpxplay_infile_callback_t *cbk_func, void *passdata)
{
	mpxplay_debugf(DISPQT_DEBUGOUT_FFMPEG, "video_ffmpeg_config_callback BEGIN %8.8X %8.8X %8.8X", cfgcmd, (unsigned long)cbk_func, (unsigned long)passdata);
	bool lock_success = this->mutex_qmedia.tryLock(DISPQT_VIDEO_MUTEX_TIMEOUT);
//	mpxplay_debugf(DISPQT_DEBUGOUT_FFMPEG, "video_ffmpeg_config_callback MUTEX mutex_qmedia passed");
	this->video_ffmpeg_configcallback_wrapper(cfgcmd, cbk_func, passdata);
//	mpxplay_debugf(DISPQT_DEBUGOUT_FFMPEG, "video_ffmpeg_config_callback END open:%d", (int)this->is_video_open);
	this->is_wait_for_open = false;
	if(lock_success)
		this->mutex_qmedia.unlock();
}

#endif
