//**************************************************************************
//*                     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 handling

//#define MPXPLAY_USE_DEBUGF 1
#define MPXPLAY_DEBUG_ERROR stderr
#define MPXPLAY_DEBUG_WARNING stderr
#define MPXPLAY_DEBUG_OUTPUT NULL //stdout
#define MPXPLAY_DEBUG_PLAY   NULL // stdout
#define MPXPLAY_DEBUG_OPENCLOSE  NULL // stdout
#define MPXPLAY_DEBUG_OPENCLSDTL NULL //stdout
#define MPXPLAY_DEBUG_HEADER  NULL //stdout
#define MPXPLAY_DEBUG_STDUR NULL // stdout
#define MPXPLAY_DEBUG_DEMUX NULL // stdout
#define MPXPLAY_DEBUG_ADECODE NULL // stdout
#define MPXPLAY_DEBUG_QUEUEGET NULL // stdout
#define MPXPLAY_DEBUG_QUEUEGET_AVFRAME NULL // stdout
#define MPXPLAY_DEBUG_QUEUEPUT NULL // stdout
#define MPXPLAY_DEBUG_TIMESTAMP NULL // stdout
#define MPXPLAY_DEBUG_STRMPRIO NULL // stdout
#define MPXPLAY_DEBUG_STRMLIST NULL // stdout
#define MPXPLAY_DEBUG_PRGCHG NULL // stdout
#define MPXPLAY_DEBUG_STRMCHG NULL // stdout
#define MPXPLAY_DEBUG_SEEK NULL // stdout
#define MPXPLAY_DEBUG_CALLBACK NULL // stdout
#define MPXPLAY_DEBUG_TEMP NULL // stdout

#include "mpxplay.h"

#ifdef MPXPLAY_LINK_INFILE_FF_MPEG

#define __STDC_CONSTANT_MACROS

#include "tagging.h"
#include "mpxinbuf.h"
#include "ffmpeg.h"
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/pixfmt.h>
#ifdef MPXPLAY_GUI_QT
#include <libavfilter/avfilter.h>
#include <disp_qt/disp_qt.h>
#endif

#ifdef MPXPLAY_USE_DEBUGF
 #define INFFMPG_DEBUG_HEADER 1
#endif

#define INFFMP_USE_DEMUXER_AUINIT 1 // FIXME: probably this solution is not thread safe, but required in some cases (flac + mjpeg)
#define INFFMP_SUPPORT_DVB_AUDIO  1 //
#define INFFMP_USE_VIDEO_SYNC     1 // start program with video sync (decode audio to this)
#define INFFMPG_USE_MPXPLAY_IOCTX 1 // use Mpxplay's onw low level file handling (dvb/ftp/http/file)
// these options shall be used together
#define INFFMP_USE_DEMUXER_THREAD 1 // TODO: frp buffer is not thread safe (pop and video freeze at start)
#define INFFMP_USE_DEMUXER_SEEK   1 // TODO: test
#define INFFMP_USE_DEMUXER_BUFILL 1

//#define INFFMP_USE_DEMUXER_MUTEX  1 // if demux thread and seek are separated (DEMUXER_THREAD is enabled and DEMUXER_SEEK is disabled)
//#define INFFMPG_USE_VIDEOPTS_CORRECTON 1 // for testing only (use avframe->pts instead)

#define INFFMPG_IOCTX_SIZE_MIN 4096
#define INFFMPG_IOCTX_SIZE_MAX 32768
#define INFFMPG_INITIAL_PACKETBUF_SIZE 65536 // TODO: this is good up to 13mbit video at stream length detect
#define INFFMPG_MAX_OUTPUT_SAMPLENUM  131072 // TODO: WMA (32k) & APE (80k) needs such large buffer
#define INFFMPG_MUTEX_TIMEOUT 5000
#define INFFMPG_MUTEX_QUEUEGET_TIMEOUT 10
#define INFFMPG_MUTEXTIME_NOLOCK  0x7eeeeeee
#define INFFMPG_QUEUE_PACKETNUM_MIN  3   // for both audio and video packets
#define INFFMPG_QUEUE_PACKETNUM_FILL_MIN  16
#define INFFMPG_QUEUE_PACKETNUM_FILL_STEP  8
#define INFFMPG_QUEUE_PACKETNUM_FILL_MAX  64
#define INFFMPG_QUEUE_PACKETNUM_MAX 1024  // to limit number of packets

#define INFFMPG_STREAM_INDEX_DISABLE -1  // don't change these values
#define INFFMPG_INVALID_STREAM_INDEX -2  //
#define INFFMPG_STREAM_INDEX_LOST    -3  // current stream has lost

#define INFFMPG_INVALID_PTS -1LL
#define INFFMPG_TIMESTAMP_SHIFT  (AV_TIME_BASE) // this must be larger than audio_delay in in_ffmpgdec_decode_audio
#define INFFMPG_TIMESTAMP_MAX_AVDIFFERENCE (AV_TIME_BASE / 2)  // max delay between video frames and audio/video frames
#define INFFMPG_MAX_FRAME_MISMACTHES 25 // * nb_programs
#define INFFMPG_MAX_STREAMS 255
#define INFFMPG_PTS_CHECK_NUM_MAX 12
#define INFFMPG_STILL_PICTURE_FRAME_MAX 3  // progressive jpeg may contain multiply frames
#define INFFMPG_SEEKREQUEST_INVALID_POS -1

#define INFFMPG_FLAG_AUTODETECT       (1 << 0)  // autodetect fileformat
#define INFFMPG_FLAG_LOADHEADONLY     (1 << 1)  // autodetect fileformat
#define INFFMPG_FLAG_IS_STREAM        (1 << 2)  // stream (MPG,TS)
#define INFFMPG_FLAG_IS_LIVESTREAM    (1 << 3)  // live stream
#define INFFMPG_FLAG_IS_SEEKABLE      (1 << 4)  // not live stream
#define INFFMPG_FLAG_STREAM_PARSING   (1 << 5)  // not live stream
#define INFFMPG_FLAG_INITIAL_SEEK     (1 << 6)  // don't seek to bof and don't clear buffs at first seek
#define INFFMPG_FLAG_SEEK_CLEAR       (1 << 7)  // clear ffctx bufs after seeking
#define INFFMPG_FLAG_DEMUX_CLEAR      (1 << 8)  // clear demux bufs after seeking
#define INFFMPG_FLAG_PLANARAUDIO      (1 << 9)  // audio channels are separated (not interlaced)
#define INFFMPG_FLAG_CALLBACKVIDEOCFG (1 <<10)  // we've initialized a callback function for video surface (called back from Qt)
#define INFFMPG_FLAG_DEMUX_THREAD     (1 <<11)  // at video files
#define INFFMPG_FLAG_STILLPICTURE     (1 <<12)  // jpg, bmp, etc.
#define INFFMPG_FLAG_DEMUX_BUFREWIND  (1 <<13)  // rewind buffer at program change
#define INFFMPG_FLAG_DEMUX_TERMINATE  (1 <<14)  //

#ifndef AVSEEK_FLAG_PAUSE
#define AVSEEK_FLAG_PAUSE    256
#endif

#define INFFMPG_STREAMFLAG_CLEARBUF_DEMUX  (1 << 0) // stream change or seek postprocess, flag for demultiplexer
#define INFFMPG_STREAMFLAG_AVSYNC          (1 << 2) // a/v sync by video
#define INFFMPG_STREAMFLAG_AVRESYNC        (1 << 3) // a/v resync by video if video packet queue is full (play video with silent audio (stop audio decoding))

#define INFFMPG_ERROR_STREAM_NOTPARSED -1000

typedef int (ffmpeg_read_packet_t)(void *, uint8_t *, int);

// one subtitle item
typedef struct ffmpeg_subtitle_item_s{
 char *subtitle_texts;
 int subtitle_begin_ms;
 int subtitle_end_ms;
}ffmpeg_subtitle_item_s;

typedef struct ffmpeg_subtitle_list_s{
 int nb_subtitle_items;
 ffmpeg_subtitle_item_s *subtitle_items;
}ffmpeg_subtitle_list_s;

typedef struct ffmpeg_subtitle_file_s{
 int nb_subtitle_files;
 ffmpeg_subtitle_list_s *subtitle_lists_of_files;
}ffmpeg_subtitle_file_s;

typedef struct ffmpg_demuxer_data_s{
 unsigned int flags, outsample_size, streamtype_limit;
 unsigned int stream_flags[MPXPLAY_STREAMTYPEINDEX_NUM];
 int retcode_demux;
 int errorcount_demux;
 int program_number_current;
 int select_new_program_number;
 int select_new_stream_number[MPXPLAY_STREAMTYPEINDEX_NUM];
 int selected_stream_number[MPXPLAY_STREAMTYPEINDEX_NUM];
 int selected_ff_stream_index[MPXPLAY_STREAMTYPEINDEX_NUM];
 unsigned int nb_streams[MPXPLAY_STREAMTYPEINDEX_NUM];
 unsigned int nb_frame_mismatches[MPXPLAY_STREAMTYPEINDEX_NUM];
 struct mpxplay_filehand_buffered_func_s *fbfs;
 void *fbds;
 struct mpxplay_infile_info_s *miis;
 AVIOContext *ioctx;               // for own low level file handling (disable ffmpeg's protocols)
 unsigned char *iobuffer;          // buffer for own ffmpeg ioctx
 AVFormatContext *fctx;            // main ffmpeg file handler context
 AVStream *selected_avstream[MPXPLAY_STREAMTYPEINDEX_NUM];
 AVCodecContext *codec_ctx[MPXPLAY_STREAMTYPEINDEX_NUM];
 AVCodec *avcodecs[MPXPLAY_STREAMTYPEINDEX_NUM];
 int64_t stream_last_pts[MPXPLAY_STREAMTYPEINDEX_NUM];
 AVPacket *pkt;
 AVFrame *audio_frame;
#ifdef MPXPLAY_GUI_CONSOLE
 int audioframe_codec_id;             // saved AV_CODEC_ID_
#endif
 int audioframe_channel_layout;       // saved channel layout
 int audioframe_samples_per_channel;  // samples per channel in audio_frame
 int audioframe_copy_sampleblocksize; // copy blocksize in samples (if we spilt a big frame for better a/v sync)
 int audioframe_copy_samplepos;       // sample pos of copy in audio_frame
#ifdef INFFMP_USE_DEMUXER_MUTEX
 void *mutexhnd_demuxer;            // mutex handler for demuxing (seek vs read thread)
#endif
 void *mutexhnd_ffmpeg;             // mutex handler for decoding
 mpxp_int64_t avsync_timestamp, avsync_clocktime;  // timestamp of audio, in AV_TIME_BASE
 mpxp_int64_t avsync_videotimestamp;// timestamp of displayed first video frame
 mpxp_int64_t seek_request_pos;     // in AV_TIME_BASE or byte position of the seek
 unsigned int seek_request_flags;   // flags for av_seek_frame, from FFMPG_infile_fseek_av to in_ffmpgdec_demux_read_packets
 int seek_request_retcode;          // result of the seek handled in an other thread
 int audioctx_init_error_count;     // a delay if audio context is not initialized (and because of this the packet is not used)
 mpxplay_packetqueue_t packet_queues[MPXPLAY_STREAMTYPEINDEX_NUM];    // audio/video data (frame) packets
 mpxplay_packetqueue_t codecctx_queues[MPXPLAY_STREAMTYPEINDEX_NUM];  // audio/video codec contexts
}ffmpg_demuxer_data_s;

// in_fmpeg.c
extern mpxp_bool_t infmpeg_is_fileformat_real_stream(struct ffmpg_demuxer_data_s *ffmpi);
extern int in_fmpeg_audio_codecid_to_waveid(struct ffmpg_demuxer_data_s *ffmpi,struct mpxplay_infile_info_s *miis, AVFrame *audio_frame);
extern void infmpeg_infile_seek_process(struct ffmpg_demuxer_data_s *ffmpi);
extern void inffmpg_tags_update(struct mpxplay_infile_info_s *miis, void *fbds, AVDictionary *metadata, unsigned int cmd);

// ffmpgdec.c
extern mpxp_bool_t in_ffmpgdec_content_has_programs(struct ffmpg_demuxer_data_s *ffmpi);
extern int64_t in_ffmpgdec_get_avpacket_ts_timestamp(struct ffmpg_demuxer_data_s *ffmpi, unsigned int streamtype_index, int64_t pkt_ts);
extern void in_ffmpgdec_packet_queues_clear(struct ffmpg_demuxer_data_s *ffmpi, mpxplay_packetqueue_t *packet_queue);
extern void in_ffmpgdec_packet_queues_init(mpxplay_packetqueue_t *packet_queue);
extern void in_ffmpgdec_packet_queues_close(struct ffmpg_demuxer_data_s *ffmpi, mpxplay_packetqueue_t *packet_queue);
extern mpxp_bool_t in_ffmpgdec_streams_initialize_all(struct ffmpg_demuxer_data_s *ffmpi, mpxp_bool_t initial);
extern int  in_ffmpgdec_infile_callback(void *demuxer_data, unsigned int control, void *arg1, int *arg2, int timeout_ms);
extern void in_ffmpgdec_stream_select_all_default(struct ffmpg_demuxer_data_s *ffmpi);
extern void inffmp_demux_reset_buffers(struct ffmpg_demuxer_data_s *ffmpi, unsigned int streamtype_index, struct mpxplay_infile_info_s *miis);
extern void in_ffmpgdec_demux_read_packets(struct ffmpg_demuxer_data_s *ffmpi);

extern int in_ffmpgdec_decode_audio(struct ffmpg_demuxer_data_s *ffmpi, struct mpxplay_infile_info_s *miis);

#endif //MPXPLAY_LINK_INFILE_FF_MPEG
