#ifndef MOC_VIDEO_QT_H
#define MOC_VIDEO_QT_H
 
#include <QWidget>
#include <QtWidgets>
#ifdef MPXPLAY_LINK_QTMEDIA
#include <QtMultimediaWidgets/QVideoWidget>
#endif
#include "moc_config.h"
#ifdef MPXPLAY_LINK_QTMEDIA
#include <QtMultimedia/QMediaPlayer>
#endif
#ifdef MPXPLAY_LINK_QTAV
#include <QtAV/QtAV>
#include <QtAVWidgets/QtAVWidgets>
//#define MPXPLAY_QTAV_VIDEORENDER 1
#endif
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
//#include "evr/evrcustompresenter.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavfilter/avfiltergraph.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
#ifdef __cplusplus
}
#endif
#endif

QT_BEGIN_NAMESPACE
class MainWindow;
class DispQtVideoEventFilter;
QT_END_NAMESPACE

//#define DISPQT_FFMPEG_TEST_SPEED 1 // test execution duration of different parts (rendering time)
//#define DISPQT_USE_OPENGL_WIDGET 1 // not finished (in the current implementation, there's no difference)

#define DISPQT_VIDEO_OPEN_TIMEOUT  5000
#define DISPQT_VIDEO_MUTEX_TIMEOUT DISPQT_MUTEX_TIMEOUT  // 5000

#define DISPQT_VIDEO_CURSORHIDE_TIMEOUT 3000

#define DISPQT_VIDEOWIDGET_SINGLECLICK_TIMEOUTMS 300
#define DISPQT_VIDEOWIDGET_DOUBLECLICK_TIMEOUTMS 300

#define DISPQT_VIDEOWIDGET_CMENUHIDE_TIMEOUTMS 100

#define DISPQT_FFMPEG_WORKERTHREAD_REFRESH_MS 10

#define DISPQT_VIDEO_FILTERPROCESS_AVFORMAT AV_PIX_FMT_YUV420P

#define DISPQT_VIDEO_AUTOCROP_USE_RGB32 1  // currently the autocrop doesn't work properly in YUV

#if DISPQT_VIDEO_AUTOCROP_USE_RGB32
#define DISPQT_VIDEO_AUTOCROP_AVFORMAT AV_PIX_FMT_RGB32
#else
#define DISPQT_VIDEO_AUTOCROP_AVFORMAT DISPQT_VIDEO_FILTERPROCESS_AVFORMAT
#endif
#define DISPQT_VIDEO_PROCESS_BUFFERALIGNMENT 1   // with 16 : autocrop doesn't work properly (don't use 32, it seems to be buggy in FFmpeg -> segmentation fault)
#define DISPQT_VIDEO_QIMAGE_OUTPUT_AVFORMAT AV_PIX_FMT_RGB32
#define DISPQT_VIDEO_QIMAGE_OUTPUT_QFORMAT QImage::Format_RGB32
//#define DISPQT_VIDEO_QIMAGE_OUTPUT_AVFORMAT AV_PIX_FMT_BGRA      // there's no differene on the video surface
//#define DISPQT_VIDEO_QIMAGE_OUTPUT_QFORMAT QImage::Format_ARGB32 //

#define DISPQT_AVFRAME_FLAG_NOSKIPFRAME (1 << 30)  // AVFrame extra flag: disable (for visualization video frame)
#define DISPQT_AVFRAME_FLAG_AR_WINDOW   (1 << 31)  // AVFrame extra flag: use window aspect ratio (for visualization video frame)

#define DISPQT_VIDEOFILTER_INVALID_VALUE -1000000000  // used to identify the initial non variable_ptr ffmpeg elem config statuses

#define DISPQT_VIDEOFILTER_APPLYRETBIT_NOFILTERAPPLY       0
#define DISPQT_VIDEOFILTER_APPLYRETBIT_INPUTFRAME_PROCESSED (1 << 0)
#define DISPQT_VIDEOFILTER_APPLYRETBIT_OUTPUTFRAME_PRODUCED (1 << 1)

#define DISPQT_VIDEOFILTER_ELEMTYPE_BOOLEAN  (1 << 0)
#define DISPQT_VIDEOFILTER_ELEMTYPE_INTEGER  (1 << 1)
#define DISPQT_VIDEOFILTER_ELEMTYPE_FLOAT    (1 << 2)

#define DISPQT_VIDEOFILTER_PROCESSFLAG_DEFAULT   (1 << 0) // using of this filter is enabled if input/output formats differ and no other filter is enabled
#define DISPQT_VIDEOFILTER_PROCESSFLAG_DEPENDANT (1 << 1) // using of this filter is enabled only if other filters are used (pad at eq)
#define DISPQT_VIDEOFILTER_PROCESSFLAG_AUDIOSRC  (1 << 2) // audio source

enum dispqt_videoframefilter_process_types
{
	DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_DECODER,
	DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_VMIXERSLIDER,
	DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_TRANSFORM,
	DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_MAX
};

enum dispqt_video_filter_crop_elems {
	DISPQT_VIDEO_FILTERCROP_ELEM_W = DISPQT_VIDEO_VMIXERTYPE_MAX,
	DISPQT_VIDEO_FILTERCROP_ELEM_H,
	DISPQT_VIDEO_FILTERCROP_ELEM_X,
	DISPQT_VIDEO_FILTERCROP_ELEM_Y,
	DISPQT_VIDEO_FILTERCROP_ELEM_MAX,
	DISPQT_VIDEO_FILTERCROP_ELEM_NUM = (DISPQT_VIDEO_FILTERCROP_ELEM_Y - DISPQT_VIDEO_FILTERCROP_ELEM_W + 1)
};

typedef struct dispqt_videofilter_elemdescriptor_s
{
	const char *ffmpeg_option_name;
	const int conf_value_offset;
	const int conf_value_scale;  // at DISPQT_VIDEOFILTER_ELEMTYPE_BOOLEAN, if *variable_ptr == conf_value_scale, then '1' is set, else '0'
	const int conf_value_center;
	int *variable_ptr;
	int last_configured_variable_value;
	const unsigned int flags;
	const int unique_id;
}dispqt_videofilter_elemdescriptor_s;

typedef struct dispqt_videofilter_processdescriptor_s
{
	const char *ffmpeg_filter_name;
	const unsigned int process_type;
	const unsigned int process_flags;
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
	unsigned int (*process_condition_func)(struct mmc_dispqt_config_s *gcfg, AVFrame *inframe, char *filtername_withconfig, unsigned int fnwcbuf_size);
#else
	unsigned int (*process_condition_func)(struct mmc_dispqt_config_s *gcfg);
#endif
	struct dispqt_videofilter_elemdescriptor_s *elemdesc;
}dispqt_videofilter_processdescriptor_s;

class DispQtVideoSurfaceContextMenu : public QMenu
{
Q_OBJECT
public:
	DispQtVideoSurfaceContextMenu(MainWindow *main_window = 0, DispQtVideoEventFilter *parent = 0, QWidget *filtered = 0);
	~DispQtVideoSurfaceContextMenu();
protected:
	void mouseReleaseEvent(QMouseEvent *mevent);

private Q_SLOTS:
	void play_start_or_pause(void);
	void program_exit(void);
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
	void actfunc_audio_visualization_select(QAction *selected_visualization_action);
	void actfunc_video_ar_select(QAction *selected_ar_action);
	void actfunc_video_crop_select(QAction *selected_crop_action);
	void actfunc_video_deinterlace_select(QAction *selected_deinterlace_action);
	void actfunc_video_rotation_select(QAction *selected_rotation_action);
	void actfunc_video_zoom_select(QAction *selected_zoom_action);
	void actfunc_video_program_select(QAction *selected_ar_action);
	void actfunc_video_stream_select(QAction *selected_ar_action);
#endif

private:
	MainWindow *main_window;
	DispQtVideoEventFilter *parent_object;
	QWidget *filtered_widget;
	struct mpxplay_programinfo_t *program_list;
	struct mpxplay_streaminfo_t *stream_list;

	QMenu *videocontmenu_sub_audio;
	QMenu *videocontmenu_subaudio_sub_visualization;
	QMenu *videocontmenu_sub_video;
	QMenu *videocontmenu_subvideo_sub_aspectratio;
	QMenu *videocontmenu_subvideo_sub_crop;
	QMenu *videocontmenu_subvideo_sub_deinterlace;
	QMenu *videocontmenu_subvideo_sub_rotation;
	QMenu *videocontmenu_subvideo_sub_zoom;
	QMenu *videocontmenu_sub_programs;
	QMenu *videocontmenu_sub_streams;

	QAction *menuaction_filename, *action_play_start, *action_play_stop;
	QAction *action_play_skipback, *action_play_skipforward, *action_view_videofs, *action_file_exit;
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
	QAction *actions_audio_visualization_select[DISPQT_AUDIOVISUALIZATIONTYPE_MAX];
	QAction *actions_video_ar_select[DISPQT_VIDEOFULLSCREENARTYPE_MAX];
	QAction *actions_video_crop_select[DISPQT_VIDEOSCRCROPTYPE_MAX];
	QAction *actions_video_deinterlace_select[DISPQT_VIDEO_DEINTERLACETYPE_MAX];
	QAction *actions_video_rotation_select[DISPQT_VIDEO_ROTATIONTYPE_MAX];
	QAction *actions_video_zoom_select[DISPQT_VIDEOFULLSCREENZOOMTYPE_MAX];
	QAction **actions_program_select;
	QAction **actions_stream_select[MPXPLAY_STREAMTYPEINDEX_NUM];
#endif
};

class DispQtVideoEventFilter : public QObject
{
Q_OBJECT
public:
	DispQtVideoEventFilter(MainWindow *main_window = 0, QWidget *parent = 0, QWidget *filtered = 0);
	bool get_mousecursor_forbidden_hide(void) { return this->mouse_cursor_forbidden_hide; }
	void mouse_cursor_show_timed(bool timed);
	void mouse_cursor_forbid_hide(bool forbid);
	void video_surface_move(int move_x, int move_y);
	void video_zoom_step(int globalpos_x, int globalpos_y, int direction); // 1: zoomin, -1:zoomout
	void video_callback_call(int globalpos_x, int globalpos_y, unsigned long command, void *arg1, int *arg2);
	DispQtVideoSurfaceContextMenu *video_surface_contextmenu;

private Q_SLOTS:
	void mouse_cursor_hide(void);
	void cmenu_about_hide(void);
	void mouse_singleclick_timeouthandler(void);

protected:
	bool eventFilter(QObject *watched, QEvent *event);

private:
	bool is_video_fullscreen(void);
	MainWindow *main_window;
	QWidget *parent_widget, *filtered_widget;
	Qt::MouseButton mouse_pressed_buttons;
	int mouse_last_globalpos_x, mouse_last_globalpos_y;
	bool mouse_cursor_forbidden_hide, singleclick_event, singleclick_fullsrc_start, fullsrc_curr, is_cursor_overrided;
	QTimer *timer_singleclick, timer_doubleclick, *timer_mouse_hide, timer_cmenu_hide;
	QMutex mutex_video;
};

class DispQtVideoFrameFilter : public QObject
{
Q_OBJECT
public:
	DispQtVideoFrameFilter(MainWindow *main_window = 0, QObject *parent = 0, dispqt_videofilter_processdescriptor_s *process_desc = 0, unsigned int nb_procdesc = 0);
	~DispQtVideoFrameFilter();

	int video_filter_close(void);
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
	int video_filter_check(AVFrame *frame_in, int frameout_format);
	int video_filter_apply(AVFrame *frame_in, AVFrame **frame_out, int frameout_format);
#endif
	int getVideoFilterValue(int index);

public Q_SLOTS:
	void resetVideoFilterValues(int process_type);
	void setVideoFilterValue(int index, int value);

private:
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
	int video_filter_init(int width, int height, int pixeltype_in, int pixeltype_out, AVFrame *inframe);
	int video_filter_config(int width, int height, int pixeltype_in, int pixeltype_out, AVFrame *inframe);
#endif
	dispqt_videofilter_elemdescriptor_s *searchElemByUniqueId(int id, int *process_type);
	bool setVideoFilterInternalValue(int index, int value);

	MainWindow *main_window;
	QObject *parent_object;
	QMutex mutex_filter;
	dispqt_videofilter_processdescriptor_s *videofilter_process_descriptor;
	unsigned int nb_video_filter_process_descriptors;
	dispqt_videofilter_processdescriptor_s *first_selected_process_descriptor;
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
	int videofilter_initialized_width;
	int videofilter_initialized_height;
	int videofilter_initialized_pixeltype;
	AVFilterContext *videofilter_ffmpeg_buffersrc_ctx;
	AVFilterContext *videofilter_ffmpeg_buffersink_ctx;
	AVFilterGraph *videofilter_ffmpeg_graph;
	char videofilter_ffmpeg_filternames_collection[256];
#endif
};

#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG

#define INFFMPEG_ASS_DECODED_MAX_LINES    8
#define INFFMPEG_ASS_DECODED_FLAG_ITALIC (1 << 0)

#define INFFMPEG_VIDEO_RENDER_MIN_INTERVAL_MS  (DISPQT_FFMPEG_WORKERTHREAD_REFRESH_MS / 2)   // we never display more than 200 fps
#define INFFMPEG_VIDEO_RENDER_MAX_INTERVAL_MS  50  // we try to display min. 20 fps in any case (other frames are dropped)
#define INFFMPEG_VIDEO_INITERROR_LIMIT 100  // ca. 1000ms wait with 10ms sleep

typedef struct ffmpegvideo_subtitle_info_s
{
	int last_render_x, last_render_y, last_size_x, last_size_y;
	unsigned int nb_lines;
	char *line_texts[INFFMPEG_ASS_DECODED_MAX_LINES];
	unsigned int line_flags[INFFMPEG_ASS_DECODED_MAX_LINES];
}ffmpegvideo_subtitle_info_s;

extern void video_ffmpeg_subtitleinfo_clear(ffmpegvideo_subtitle_info_s *sub_lines);
extern void video_ffmpeg_subtitleinfo_copy(ffmpegvideo_subtitle_info_s *dest_lines, ffmpegvideo_subtitle_info_s *sub_lines);

#define DISPQT_FFMPGVIDEO_MAX_QUEUED_VIDEOFRAME  5
#define DISPQT_FFMPGVIDEO_MAX_QUEUED_VISVIDEOFRAME  (DISPQT_FFMPGVIDEO_MAX_QUEUED_VIDEOFRAME * 5)
#define DISPQT_FFMPGVIDEO_MAX_DELAY_VISVIDEOFRAME   (DISPQT_FFMPGVIDEO_MAX_QUEUED_VIDEOFRAME * 4) // max number of video frames difference between audio and video output (else ignore timestamp (skip visual video frame))
#define DISPQT_FFMPGVIDEO_VIDEO_MAX_FRAME_DIFF_US   (15 * AV_TIME_BASE)  // max 15 secs difference between video frames
#define DISPQT_FFMPGVIDEO_AVSYNC_MAX_VALID_DIFF_US  (60 * AV_TIME_BASE) // probably an incorrect pts received, display output picture (ignore timestamp)

#define DISPQT_FFMPGVIDEOCALLBACK_INITFRAMECOUNTER_START 50 // video frames

#define DISPQT_FFMPGVIDEOCALLBACKFLAG_EXITEVENT           (1 << 0)  // stop thread event
#define DISPQT_FFMPGVIDEOCALLBACKFLAG_THEXITDONE          (1 << 1)  // done
#define DISPQT_FFMPGVIDEOCALLBACKFLAG_FIRSTFRAME          (1 << 4)  // first displayed frame sign
#define DISPQT_FFMPGVIDEOCALLBACKFLAG_PRIMARYVIDEO        (1 << 5)  // primary video file
#define DISPQT_FFMPGVIDEOCALLBACKFLAG_DISPLAYONEFRAME     (1 << 6)  // display one video frame (in pause)
#define DISPQT_FFMPGVIDEOCALLBACKFLAG_IGNORETIMESTAMP     (1 << 7)  // ignore timestamp sync (after seeking) to display a frame
#define DISPQT_FFMPGVIDEOCALLBACKFLAG_STILLPICT           (1 << 8)  // still picture (aspect ratio and multiply picture frame workaround)

enum {DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYTYPE_NONE = 0, DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYTYPE_SMALL, DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYTYPE_MIDDLE, DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYTYPE_LARGE, DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYTYPE_SIGNIFICANT};
enum {DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYLIMIT_SMALL = 250000, DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYLIMIT_MIDDLE = 500000, DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYLIMIT_LARGE = 1000000, DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYLIMIT_SIGNIFICANT = 3000000};
enum {DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYCOUNT_MIDDLE = 3, DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYCOUNT_LARGE = 10, DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYCOUNT_SIGNIFICANT = 20};
enum {DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYFRAMEDROP_NONE = 0, DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYFRAMEDROP_LARGE = 1, DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYFRAMEDROP_SIGNIFICANT = 1};

typedef struct ffmpegvideo_callback_list_s{
	unsigned int flags;
	mpxplay_infile_callback_t *infile_callback;
	void *infile_passdata;
	int videoctx_init_error_count;
	int video_init_framecounter;              // aka 'firstframes' flag, process video delay
	int video_delay_type;                     // DISPQT_FFMPGVIDEOCALLBACK_VIDEODELAYTYPE_
	int video_delay_large_counter;            // to control video frame drop (decoder + output) with delay
	int video_delay_framedrop_counter;        // belongs to the decoded output frames
	mpxp_int64_t subtitle_begin_timestamp;
	mpxp_int64_t subtitle_end_timestamp;
	mpxp_int64_t video_dec_last_timestamp;
	mpxp_int64_t video_last_timestamp;
	mpxp_int64_t video_last_render_time;
	mpxplay_packetlist_t *video_pktlist_elem;
	mpxplay_packetlist_t *subtitle_pktlist_elem;
	mpxplay_packetlist_t *videoframe_pktlist_elem;
	AVFrame *vid_frame_decoded;
	ffmpegvideo_subtitle_info_s *subtitle_infos;
	mpxp_thread_id_type videodecoder_thread_id;
	DispQtVideoFrameFilter *video_frame_filter_workerprocess;
	mpxplay_packetqueue_t videoframe_queue;
	struct ffmpegvideo_callback_list_s *next;
#ifdef DISPQT_FFMPEG_TEST_SPEED
	mpxp_int64_t timedevent_prev_sync_time;
#endif
}ffmpegvideo_callback_list_s;

class FFMpegVideoDecoderWorker : public QObject
{
Q_OBJECT
public:
	FFMpegVideoDecoderWorker(MainWindow *main_window = 0, QWidget *parent = 0);
	~FFMpegVideoDecoderWorker();
	bool videothread_ffmpeg_config_callback(unsigned int cfgcmd,mpxplay_infile_callback_t *cbk_func, void *passdata);
	int videothread_ffmpeg_callback_call(int globalpos_x, int globalpos_y, unsigned long command, void *arg1, int *arg2);

Q_SIGNALS:
	void signal_ffmpeg_frame_display(int video_index, unsigned int flags, AVFrame *videoout_frame, ffmpegvideo_subtitle_info_s *subtitle_infos);

public Q_SLOTS:
	void decoderworker_TimedEvent(void);

private:
	int cb_queue_put_entry(mpxplay_infile_callback_t *cbk_func, void *passdata, bool insert_to_begin);
	int cb_queue_del_entry(mpxplay_infile_callback_t *cbk_func, void *passdata);
	void cb_queue_elem_clear(struct ffmpegvideo_callback_list_s *cbelem);
	void cb_queue_clear(void);
	void decoderworker_subtitle_load_next_frame(ffmpegvideo_callback_list_s *cbelem);
	void decoderworker_subtitle_decode(ffmpegvideo_callback_list_s *cbelem, mpxp_int64_t timestamp_curr_ms);
	void decoderworker_subtitle_clear(ffmpegvideo_callback_list_s *cbelem);
	int  decoderworker_video_load_next_frame(ffmpegvideo_callback_list_s *cbelem, bool *resetvideo);
	void decoderworker_video_reset(ffmpegvideo_callback_list_s *cbelem, int video_index);
	void decoderworker_video_frame_deliver_to_render(ffmpegvideo_callback_list_s *cbelem, int video_index, mpxp_int64_t timestamp_curr_ms);

	MainWindow *main_window;
	ffmpegvideo_callback_list_s *cblist_first, *cblist_last;
	int nb_callbacks;
#ifdef DISPQT_FFMPEG_TEST_SPEED
	mpxp_int64_t timedevent_prev_start_time;
#endif
	bool last_is_video_open, is_paused;
	QMutex mutex_decoderthread;
};

#if (DISPQT_USE_OPENGL_WIDGET == 2)
class FFMpegVideoWidget : public QOpenGLWidget
#else
class FFMpegVideoWidget : public QWidget
#endif
{
Q_OBJECT
public:
	FFMpegVideoWidget(MainWindow *main_window = 0, QWidget *parent = 0);
	~FFMpegVideoWidget();
	bool videowidget_ffmpeg_config_callback(unsigned int cfgcmd, mpxplay_infile_callback_t *cbk_func, void *passdata);
	int videowidget_ffmpeg_callback_call(int globalpos_x, int globalpos_y, unsigned long command, void *arg1, int *arg2);

Q_SIGNALS:
	void signal_ffmpeg_swctx_resize(int window_width, int window_height, bool do_repaint);
	void signal_ffmpeg_swctx_move(int move_x, int move_y);
	void signal_ffmpeg_swctx_zoom(int globalpos_x, int globalpos_y, int direction);
	void signal_ffmpeg_filtered_trans_refresh(void);
	void signal_ffmpeg_filtered_eq_refresh(void);

public Q_SLOTS:
	void videowidget_ffmpeg_swctx_resize(int window_width, int window_height, bool do_repaint);
	void videowidget_ffmpeg_swctx_move(int move_x, int move_y);
	void videowidget_ffmpeg_swctx_zoom(int globalpos_x, int globalpos_y, int direction);
	void videowidget_ffmpeg_filtered_trans_refresh(void);
	void videowidget_ffmpeg_filtered_eq_refresh(void);
	void videowidget_ffmpeg_frame_display(int video_index, unsigned int flags, AVFrame *videoout_frame, ffmpegvideo_subtitle_info_s *subtitle_infos);

protected:
	void resizeEvent(QResizeEvent *e);
	void paintEvent(QPaintEvent *event);

private:
	void videowidget_ffmpeg_subtitle_render(QPainter *painter, ffmpegvideo_subtitle_info_s *subtitle_infos);
	void videowidget_ffmpeg_subtitle_close(void);
	void videowidget_ffmpeg_filtered_crop_update(bool check_autocrop);
	void videowidget_ffmpeg_filtered_trans_update(bool mutex_locked);
	void videowidget_ffmpeg_filtered_eq_update(bool mutex_locked);
	void videowidget_ffmpeg_filtered_surface_update(bool mutex_locked);
	int  videowidget_ffmpeg_videoctx_init(void);
	void videowidget_ffmpeg_videoctx_close(void);
	void videowidget_ffmpeg_videoctx_clear(void);
	bool videowidget_ffmpeg_swsctx_crop_auto_detect(AVFrame *input_video_frame);
	bool videowidget_ffmpeg_swsctx_calculate(int window_width, int window_height);
	int  videowidget_ffmpeg_swsctx_init(AVFrame *input_video_frame, int output_width, int output_height, enum AVPixelFormat output_pixelformat);
	int  videowidget_ffmpeg_swsctx_get_sws_outformat(AVFrame *input_video_frame);
	bool videowidget_ffmpeg_swsctx_conv_frame(AVFrame *input_frame);
	bool videowidget_ffmpeg_swsctx_check_swsframe_for_outsurface(void);
	void videowidget_ffmpeg_swsctx_close(void);
	void videowidget_ffmpeg_swsctx_clear(void);

	MainWindow *main_window;
	QWidget *parent_widget;
	AVFrame *ffmpeg_decoded_frame;     // came from the decoder (decoded, sw deinterlaced frame), in DISPQT_VIDEO_FILTERPROCESS_AVFORMAT, or in native decoder output
	AVFrame *ffmpeg_sws_frame;         // for autocrop and for rgb32 (qwidget) output if picture filtering is disabled
	AVFrame *ffmpeg_transformed_frame; // picture transformed (pad, rotation, etc.) frame
	AVFrame *ffmpeg_filtered_frame;    // picture filtered (brightness, contrast, etc.) frame
	AVFrame *ffmpeg_output_frame;      // points to ffmpeg_sws_frame or ffmpeg_filtered_frame after preprocess
	bool ffmpeg_swsframe_hasdata;      // ffmpeg_sws_frame has valid data
	bool videoout_surface_recalc;      // re-calculate surface values (videowidget_ffmpeg_swsctx_calculate)
	bool videoout_surface_clearpic;    // clear output surface before or instead of rendering (eg: at autocrop, or at new file)
	struct SwsContext *sws_ctx;
	unsigned long last_video_render_type, last_video_crop_type, ffmpeg_swsframe_cropreset;
	int last_video_zoom_min_val, last_video_zoom_ti_val;
	int vid_src_ori_width, vid_src_ori_height;   // came out from decoder (for progressive jpeg largest frame decision)
	int sws_src_width, sws_src_height;           // after transformation (rotate, pad)
	int sws_srccrop_x, sws_srccrop_y, sws_srccrop_width, sws_srccrop_height;
	int sws_crop_x, sws_crop_y, sws_crop_w, sws_crop_h;
	int video_zoom_globalpos_x, video_zoom_globalpos_y;
	int sws_dst_width, sws_dst_height, sws_dst_pos_x, sws_dst_pos_y;
	int sws_autoxcrop_framecounter;
	enum AVPixelFormat vid_src_ori_pix_fmt;      // format of decoded frame
	enum AVPixelFormat sws_src_pix_fmt;          // source format of sws_scale (came from the ffmpeg_decoded_frame)
	enum AVPixelFormat sws_out_pix_fmt;          // output format of sws_scale (RGB32 for autocrop, or videoout_surface_pix_fmt)
	enum AVPixelFormat videoout_surface_pix_fmt; // the final output format (RGB32 for qwidget)
	AVRational video_aspect_ratio;
	QMutex mutex_video;
	FFMpegVideoDecoderWorker *video_decoder_worker;
	ffmpegvideo_subtitle_info_s *sub_title_infos;
#if (DISPQT_USE_OPENGL_WIDGET == 1)
	QOpenGLContext *video_opengl_context;
#endif
//	Scheduler *video_sample_scheduler_evr;
//	EVRCustomPresenter *video_renderer_evr;
	QThread video_decoder_thread;
	QTimer video_decoder_timer;
	QFont font_subtitle;
};
#endif // MPXPLAY_LINK_ORIGINAL_FFMPEG

class DispQtVideoPlayer : public QWidget
{
Q_OBJECT
public:
enum {VideoCtrlValue_FullScreen = 0, VideoCtrlValue_Height, VideoCtrlValue_Speed};
	DispQtVideoPlayer(MainWindow *main_window = 0, QWidget *parent = 0);
	~DispQtVideoPlayer();
	void video_mediaplayer_open(bool lock_qmedia);
	void video_mediaplayer_close(void);
	void set_focus(void) { if(this->video_widget_context) this->video_widget_context->setFocus(Qt::MouseFocusReason); }
	void video_request_open(void *handle_ptr, char *utf8_filename);
	bool video_wait_for_open(void);
	void video_request_close(void *handle_ptr);
	bool video_wait_for_close(void);
	bool video_is_fullscreen(void) { return ((this->video_widget_context)? this->video_widget_context->isFullScreen() : false); }
	qint64 video_tell_ms(void);
	qint64 video_duration_ms(void);
	QWidget *video_get_widgetptr(void) { return this->video_widget_context; }
	int  video_get_value(unsigned int ctrl);
	void video_mouse_cursor_show(bool timed);
	void video_mouse_cursor_forbid_hide(bool forbid);
	void video_widget_init(void);
	void video_widget_control(unsigned long mainwin_control);
	//QString video_media_name(void) { return this->media_name; }
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
	int video_ffmpeg_configcallback_create(unsigned int cfgcmd, mpxplay_infile_callback_t *cbk_func, void *passdata);
	DispQtVideoFrameFilter *video_frame_filter_transform_process;
#endif
	DispQtVideoFrameFilter *video_frame_filter_eq_process;

Q_SIGNALS:
	void signal_video_open(void);
	void signal_video_close(void);
	void signal_video_play(bool startflag);
	void signal_video_pause(void);
	void signal_video_switch_play(void);
	void signal_video_seek_ms(qint64 pos_ms);
	void signal_video_set_fullscreen(bool fs);
	void signal_video_switch_fullscreen(void);
	void signal_video_apply_fullscreen(bool fs); // 2nd step of switch_fullscreen
	void signal_video_check_windows(void);
	void signal_video_apply_windows(void);       // 3rd step of switch_fullscreen
	void signal_video_event_stop(void);
	void signal_video_set_speed(int speed1000);
	void signal_audio_set_volume(int vol);
	void signal_video_set_value(unsigned int ctrl, int val);
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
	void signal_video_surface_resize_refresh(void);
	void signal_video_surface_filtered_trans_refresh(void);
	void signal_video_surface_filtered_eq_refresh(void);
	void signal_ffmpeg_config_callback(unsigned int cfgcmd, mpxplay_infile_callback_t *cbk_func, void *passdata);
#endif

private Q_SLOTS:
	void video_open(void);
	void video_close(void);
	void video_play(bool startflag);
	void video_pause(void);
	void video_switch_play(void) { if(this->is_playing) this->video_pause(); else this->video_play(false); }
	void video_stop(void *handle_ptr, bool close_media);
	void video_seek_ms(qint64 pos_ms);
	void video_set_fullscreen(bool fs);
	void video_switch_fullscreen(void);
	void video_apply_fullscreen(bool fs);
	void video_check_windows(void);
	void video_apply_windows(void);
	void video_handle_event_stop(void);
	void video_set_speed(int speed1000);
	void audio_set_volume(int vol);
	void video_set_value(unsigned int ctrl, int val);
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
	void video_surface_resize_refresh(void);
	void video_surface_filtered_trans_refresh(void);
	void video_surface_filtered_eq_refresh(void);
	void video_ffmpeg_configcallback_wrapper(unsigned int cfgcmd, mpxplay_infile_callback_t *cbk_func, void *passdata);
	void video_ffmpeg_config_callback(unsigned int cfgcmd, mpxplay_infile_callback_t *cbk_func, void *passdata);
#endif

#ifdef MPXPLAY_LINK_QTMEDIA
	void status_changed(QMediaPlayer::MediaStatus status);
	void display_error_message(QMediaPlayer::Error);
#endif

private:
#ifdef MPXPLAY_LINK_QTMEDIA
	void video_cursor_select(QMediaPlayer::MediaStatus status);
#endif

	MainWindow *main_window;
	QWidget *parent_widget;
	QHBoxLayout *video_layout;
	QString media_name;
#ifdef MPXPLAY_LINK_QTAV
	QtAV::AVPlayer *qtav_media_player;
#ifdef MPXPLAY_QTAV_VIDEORENDER
	QtAV::VideoRenderer *qtav_video_output;
#else
	QtAV::VideoOutput *qtav_video_output;
#endif
#endif
#ifdef MPXPLAY_LINK_QTMEDIA
	QMediaPlayer *qt_media_player;
	QUrl media_url;
	QMediaContent media_content;
#endif
	DispQtVideoEventFilter *video_event_filter;
	QWidget *video_widget_context;
	Qt::WindowFlags video_wdg_nonfullscreen_flags;
	bool is_video_show, is_video_open, is_wait_for_open, is_playing;
	bool is_wait_for_close, is_video_closed;
	void *ptr_handle_curr, *ptr_handle_new, *ptr_handle_close; // for close identification
	QMutex mutex_player, mutex_qmedia;
};

#endif
