#include "packets_sync.h"

int framedrop = -1;

int packet_queue_init(PacketQueue* q)
{
    memset(q, 0, sizeof(PacketQueue));
    q->pkt_list = av_fifo_alloc2(1, sizeof(MyAVPacketList), AV_FIFO_FLAG_AUTO_GROW);
    if (!q->pkt_list)
        return AVERROR(ENOMEM);
    q->mutex = new QMutex();
    if (!q->mutex) {
        av_log(nullptr, AV_LOG_FATAL, "new QMutex() error.\n");
        return AVERROR(ENOMEM);
    }
    q->cond = new QWaitCondition();
    if (!q->cond) {
        av_log(nullptr, AV_LOG_FATAL, "new QWaitCondition() error.\n");
        return AVERROR(ENOMEM);
    }
    q->abort_request = 1;
    return 0;
}

void packet_queue_destroy(PacketQueue* q)
{
    packet_queue_flush(q);
    av_fifo_freep2(&q->pkt_list);
    delete q->mutex;
    delete q->cond;
}

void packet_queue_flush(PacketQueue* q)
{
    MyAVPacketList pkt1;

    q->mutex->lock();
    while (av_fifo_read(q->pkt_list, &pkt1, 1) >= 0)
        av_packet_free(&pkt1.pkt);
    q->nb_packets = 0;
    q->size = 0;
    q->duration = 0;
    q->serial++;
    q->mutex->unlock();
}

void packet_queue_start(PacketQueue* q)
{
    q->mutex->lock();
    q->abort_request = 0;
    q->serial++;
    q->mutex->unlock();
}

void packet_queue_abort(PacketQueue* q)
{
    q->mutex->lock();
    q->abort_request = 1;
    q->cond->wakeAll();
    q->mutex->unlock();
}

int packet_queue_get(PacketQueue* q, AVPacket* pkt, int block, int* serial)
{
#if PRINT_PACKETQUEUE_INFO
    // packet_queue_print(q, pkt, "packet_queue_get");
#endif

    MyAVPacketList pkt1;
    int ret = 0;

    q->mutex->lock();

    for (;;) {
        if (q->abort_request) {
            ret = -1;
            break;
        }

        if (av_fifo_read(q->pkt_list, &pkt1, 1) >= 0) {
            q->nb_packets--;
            q->size -= pkt1.pkt->size + sizeof(pkt1);
            q->duration -= pkt1.pkt->duration;
            av_packet_move_ref(pkt, pkt1.pkt);
            if (serial)
                *serial = pkt1.serial;
            av_packet_free(&pkt1.pkt);
            ret = 1;
            break;
        } else if (!block) {
            ret = 0;
            break;
        } else {
            q->cond->wait(q->mutex);
        }
    }
    q->mutex->unlock();
    return ret;
}

void packet_queue_print(const PacketQueue* q, const AVPacket* pkt, const QString& prefix)
{
    qDebug("[%s]Queue:[%p](nb_packets:%d, size:%d, dur:%d, serial:%d), "
           "pkt(pts:%lld,dts:%lld,size:%d,s_index:%d,dur:%lld,pos:%lld).",
           qUtf8Printable(prefix),
           q,
           q->nb_packets,
           q->size,
           q->duration,
           q->serial,
           pkt->pts,
           pkt->dts,
           pkt->size,
           pkt->stream_index,
           pkt->duration,
           pkt->pos);
}

int packet_queue_put(PacketQueue* q, AVPacket* pkt)
{
    AVPacket* pkt1;
    int ret = -1;

    pkt1 = av_packet_alloc();
    if (!pkt1) {
        av_packet_unref(pkt);
        return ret;
    }
    av_packet_move_ref(pkt1, pkt);

    q->mutex->lock();
    ret = packet_queue_put_private(q, pkt1);
    q->mutex->unlock();

    if (ret < 0)
        av_packet_free(&pkt1);

#if PRINT_PACKETQUEUE_INFO
    // packet_queue_print(q, pkt, "packet_queue_put");
#endif
    return ret;
}

int packet_queue_put_nullpacket(PacketQueue* q, AVPacket* pkt, int stream_index)
{
    pkt->stream_index = stream_index;
    return packet_queue_put(q, pkt);
}

int packet_queue_put_private(PacketQueue* q, AVPacket* pkt)
{
    MyAVPacketList pkt1;
    int ret;

    if (q->abort_request)
        return -1;

    pkt1.pkt = pkt;
    pkt1.serial = q->serial;

    ret = av_fifo_write(q->pkt_list, &pkt1, 1);
    if (ret < 0)
        return ret;
    q->nb_packets++;
    q->size += pkt1.pkt->size + sizeof(pkt1);
    q->duration += pkt1.pkt->duration;
    /* XXX: should duplicate packet data in DV case */
    q->cond->wakeAll();
    return 0;
}

int frame_queue_init(FrameQueue* f, PacketQueue* pktq, int max_size, int keep_last)
{
    int i;
    memset(f, 0, sizeof(FrameQueue));
    if (!(f->mutex = new QMutex())) {
        av_log(nullptr, AV_LOG_FATAL, "new QMutex() error!\n");
        return AVERROR(ENOMEM);
    }
    if (!(f->cond = new QWaitCondition())) {
        av_log(nullptr, AV_LOG_FATAL, "new QWaitCondition() error\n");
        return AVERROR(ENOMEM);
    }
    f->pktq = pktq;
    f->max_size = FFMIN(max_size, FRAME_QUEUE_SIZE);
    f->keep_last = !!keep_last;
    for (i = 0; i < f->max_size; i++)
        if (!(f->queue[i].frame = av_frame_alloc()))
            return AVERROR(ENOMEM);
    return 0;
}

void frame_queue_destory(FrameQueue* f)
{
    int i;
    for (i = 0; i < f->max_size; i++) {
        Frame* vp = &f->queue[i];
        frame_queue_unref_item(vp);
        av_frame_free(&vp->frame);
    }

    delete f->mutex;
    delete f->cond;
}

void frame_queue_unref_item(Frame* vp)
{
    av_frame_unref(vp->frame); // frame reference number reduce 1
    avsubtitle_free(&vp->sub); // sub
}

void frame_queue_signal(FrameQueue* f)
{
    f->mutex->lock();
    f->cond->wakeAll();
    f->mutex->unlock();
}

Frame* frame_queue_peek(FrameQueue* f)
{
    return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
}

Frame* frame_queue_peek_next(FrameQueue* f)
{
    return &f->queue[(f->rindex + f->rindex_shown + 1) % f->max_size];
}

Frame* frame_queue_peek_last(FrameQueue* f)
{
    return &f->queue[f->rindex];
}

Frame* frame_queue_peek_writable(FrameQueue* f)
{
    /* wait until we have space to put a new frame */
    f->mutex->lock();
    while (f->size >= f->max_size && !f->pktq->abort_request) {
        f->cond->wait(f->mutex);
    }
    f->mutex->unlock();

    if (f->pktq->abort_request)
        return nullptr;

    return &f->queue[f->windex];
}

Frame* frame_queue_peek_readable(FrameQueue* f)
{
    /* wait until we have a readable a new frame */
    f->mutex->lock();
    while (f->size - f->rindex_shown <= 0 && !f->pktq->abort_request) {
        f->cond->wait(f->mutex);
    }
    f->mutex->unlock();

    if (f->pktq->abort_request)
        return nullptr;

    return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
}

void frame_queue_push(FrameQueue* f)
{
    if (++f->windex == f->max_size)
        f->windex = 0;
    f->mutex->lock();
    f->size++;
    f->cond->wakeAll();
    f->mutex->unlock();
}

void frame_queue_next(FrameQueue* f)
{
    if (f->keep_last && !f->rindex_shown) {
        f->rindex_shown = 1;
        return;
    }
    frame_queue_unref_item(&f->queue[f->rindex]);
    if (++f->rindex == f->max_size)
        f->rindex = 0;
    f->mutex->lock();
    f->size--;
    f->cond->wakeAll();
    f->mutex->unlock();
}

/* return the number of undisplayed frames in the queue */
int frame_queue_nb_remaining(FrameQueue* f)
{
    return f->size - f->rindex_shown;
}

/* return last shown position */
int64_t frame_queue_last_pos(FrameQueue* f)
{
    Frame* fp = &f->queue[f->rindex];
    if (f->rindex_shown && fp->serial == f->pktq->serial)
        return fp->pos;
    else
        return -1;
}

int queue_picture(
    VideoState* is, AVFrame* src_frame, double pts, double duration, int64_t pos, int serial)
{
#if PRINT_PACKETQUEUE_INFO
    // int64 lld, double lf
    qDebug("queue picture, w:%d, h:%d, nb:%d, ft:%d(%s), kf:%d, pic_t:%d(%c), "
           "pts:%lf, duration:%lf, pos:%lld, serial:%d",
           src_frame->width,
           src_frame->height,
           src_frame->nb_samples,
           src_frame->format,
           av_get_sample_fmt_name(AVSampleFormat(src_frame->format)),
           src_frame->key_frame,
           src_frame->pict_type,
           av_get_picture_type_char(src_frame->pict_type),
           pts,
           duration,
           pos,
           serial);
#endif

    Frame* vp;

    if (!(vp = frame_queue_peek_writable(&is->pictq)))
        return -1;

    vp->sar = src_frame->sample_aspect_ratio;
    vp->uploaded = 0;

    vp->width = src_frame->width;
    vp->height = src_frame->height;
    vp->format = src_frame->format;

    vp->pts = pts;
    vp->duration = duration;
    vp->pos = pos;
    vp->serial = serial;

    av_frame_move_ref(vp->frame, src_frame);
    frame_queue_push(&is->pictq);
    return 0;
}

int get_video_frame(VideoState* is, AVFrame* frame)
{
    int got_picture = -1;

    if ((got_picture = decoder_decode_frame(&is->viddec, frame, nullptr)) < 0)
        return -1;

    if (got_picture) {
        double dpts = NAN;

        if (frame->pts != AV_NOPTS_VALUE)
            dpts = av_q2d(is->video_st->time_base) * frame->pts;

        frame->sample_aspect_ratio = av_guess_sample_aspect_ratio(is->ic, is->video_st, frame);

        if (framedrop > 0 || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) {
            if (frame->pts != AV_NOPTS_VALUE) {
                double diff = dpts - get_master_clock(is);
                if (!isnan(diff) && fabs(diff) < AV_NOSYNC_THRESHOLD
                    && diff - is->frame_last_filter_delay < 0
                    && is->viddec.pkt_serial == is->vidclk.serial && is->videoq.nb_packets) {
                    is->frame_drops_early++;
                    av_frame_unref(frame);
                    got_picture = 0;
                }
            }
        }
    }

    return got_picture;
}

int decoder_init(Decoder* d,
                 AVCodecContext* avctx,
                 PacketQueue* queue,
                 QWaitCondition* empty_queue_cond)
{
    memset(d, 0, sizeof(Decoder));
    d->pkt = av_packet_alloc();
    if (!d->pkt)
        return AVERROR(ENOMEM);
    d->avctx = avctx;
    d->queue = queue;
    d->empty_queue_cond = empty_queue_cond;
    d->start_pts = AV_NOPTS_VALUE;
    d->pkt_serial = -1;
    return 0;
}

int decoder_start(Decoder* d, void* thread, const char* thread_name)
{
    packet_queue_start(d->queue);
    d->decoder_tid = thread;
    d->decoder_name = av_strdup(thread_name);
    return 0;
}

void decoder_destroy(Decoder* d)
{
    av_packet_free(&d->pkt);
    avcodec_free_context(&d->avctx);
    av_free(d->decoder_name);
}

void decoder_abort(Decoder* d, FrameQueue* fq)
{
    packet_queue_abort(d->queue);
    frame_queue_signal(fq);
    // SDL_WaitThread(d->decoder_tid, nullptr);
    // TODO: //
    ThreadBase* thread = (ThreadBase*) (d->decoder_tid);
    if (thread && thread->isRunning()) {
        thread->join();
    }
    d->decoder_tid = nullptr;
    packet_queue_flush(d->queue);
}

int decoder_decode_frame(Decoder* d, AVFrame* frame, AVSubtitle* sub)
{
    int ret = AVERROR(EAGAIN);
    int decoder_reorder_pts = -1;
    for (;;) {
        if (d->queue->serial == d->pkt_serial) {
            do {
                if (d->queue->abort_request)
                    return -1;

                switch (d->avctx->codec_type) {
                case AVMEDIA_TYPE_VIDEO:
                    ret = avcodec_receive_frame(d->avctx, frame);
                    if (ret >= 0) {
                        if (decoder_reorder_pts == -1) {
                            frame->pts = frame->best_effort_timestamp;
                        } else if (!decoder_reorder_pts) {
                            frame->pts = frame->pkt_dts;
                        }
                    }
                    break;
                case AVMEDIA_TYPE_AUDIO:
                    ret = avcodec_receive_frame(d->avctx, frame);
                    if (ret >= 0) {
                        AVRational tb = AVRational{1, frame->sample_rate};
                        if (frame->pts != AV_NOPTS_VALUE)
                            frame->pts = av_rescale_q(frame->pts, d->avctx->pkt_timebase, tb);
                        else if (d->next_pts != AV_NOPTS_VALUE)
                            frame->pts = av_rescale_q(d->next_pts, d->next_pts_tb, tb);
                        if (frame->pts != AV_NOPTS_VALUE) {
                            d->next_pts = frame->pts + frame->nb_samples;
                            d->next_pts_tb = tb;
                        }
                    }
                    break;
                }
                if (ret == AVERROR_EOF) {
                    d->finished = d->pkt_serial;
                    avcodec_flush_buffers(d->avctx);
                    return 0;
                }
                if (ret >= 0)
                    return 1;
            } while (ret != AVERROR(EAGAIN));
        }

        do {
            if (d->queue->nb_packets == 0)
                d->empty_queue_cond->wakeAll();
            if (d->packet_pending) {
                d->packet_pending = 0;
            } else {
                int old_serial = d->pkt_serial;
                if (packet_queue_get(d->queue, d->pkt, 1, &d->pkt_serial) < 0)
                    return -1;
                if (old_serial != d->pkt_serial) {
                    avcodec_flush_buffers(d->avctx);
                    d->finished = 0;
                    d->next_pts = d->start_pts;
                    d->next_pts_tb = d->start_pts_tb;
                }
            }
            if (d->queue->serial == d->pkt_serial)
                break;
            av_packet_unref(d->pkt);
        } while (1);

        if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
            int got_frame = 0;
            ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, d->pkt);
            if (ret < 0) {
                ret = AVERROR(EAGAIN);
            } else {
                if (got_frame && !d->pkt->data) {
                    d->packet_pending = 1;
                }
                ret = got_frame ? 0 : (d->pkt->data ? AVERROR(EAGAIN) : AVERROR_EOF);
            }
            av_packet_unref(d->pkt);
        } else {
            if (d->pkt->buf && !d->pkt->opaque_ref) {
                FrameData* fd;

                d->pkt->opaque_ref = av_buffer_allocz(sizeof(*fd));
                if (!d->pkt->opaque_ref)
                    return AVERROR(ENOMEM);
                fd = (FrameData*) d->pkt->opaque_ref->data;
                fd->pkt_pos = d->pkt->pos;
            }

            if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) {
                av_log(d->avctx,
                       AV_LOG_ERROR,
                       "Receive_frame and send_packet both returned EAGAIN, which is an API "
                       "violation.\n");
                d->packet_pending = 1;
            } else {
                av_packet_unref(d->pkt);
            }
        }
    }
}

void get_file_info(const char* filename, int64_t& duration)
{
    AVFormatContext* pFormatCtx = avformat_alloc_context();
    if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) == 0) {
        if (pFormatCtx->duration < 0)
            avformat_find_stream_info(pFormatCtx, NULL);
        duration = pFormatCtx->duration;
    }
    // etc
    avformat_close_input(&pFormatCtx);
    avformat_free_context(pFormatCtx);
}

void get_duration_time(
    const int64_t duration_us, int64_t& hours, int64_t& mins, int64_t& secs, int64_t& us)
{
    int64_t duration = duration_us + (duration_us <= INT64_MAX - 5000 ? 5000 : 0);
    duration = duration < 0 ? 0 : duration;
    secs = duration / AV_TIME_BASE;
    us = duration % AV_TIME_BASE;
    us = (100 * us) / AV_TIME_BASE;
    mins = secs / 60;
    secs %= 60;
    hours = mins / 60;
    mins %= 60;
}

double get_clock(Clock* c)
{
    if (*c->queue_serial != c->serial)
        return NAN;
    if (c->paused) {
        return c->pts;
    } else {
        double time = av_gettime_relative() / 1000000.0;
        return c->pts_drift + time - (time - c->last_updated) * (1.0 - c->speed);
    }
}

void set_clock_at(Clock* c, double pts, int serial, double time)
{
    c->pts = pts;
    c->last_updated = time;
    c->pts_drift = c->pts - time;
    c->serial = serial;
}

void set_clock(Clock* c, double pts, int serial)
{
    double time = av_gettime_relative() / 1000000.0;
    set_clock_at(c, pts, serial, time);
}

void set_clock_speed(Clock* c, double speed)
{
    set_clock(c, get_clock(c), c->serial);
    c->speed = speed;
}

void init_clock(Clock* c, int* queue_serial)
{
    c->speed = 1.0;
    c->paused = 0;
    c->queue_serial = queue_serial;
    set_clock(c, NAN, -1);
}

void sync_clock_to_slave(Clock* c, Clock* slave)
{
    double clock = get_clock(c);
    double slave_clock = get_clock(slave);
    if (!isnan(slave_clock) && (isnan(clock) || fabs(clock - slave_clock) > AV_NOSYNC_THRESHOLD))
        set_clock(c, slave_clock, slave->serial);
}

int stream_has_enough_packets(AVStream* st, int stream_id, PacketQueue* queue)
{
    return stream_id < 0 || queue->abort_request || (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
           || queue->nb_packets > MIN_FRAMES
                  && (!queue->duration || av_q2d(st->time_base) * queue->duration > 1.0);
}

int is_realtime(AVFormatContext* s)
{
    if (!strcmp(s->iformat->name, "rtp") || !strcmp(s->iformat->name, "rtsp")
        || !strcmp(s->iformat->name, "sdp"))
        return 1;

    if (s->pb && (!strncmp(s->url, "rtp:", 4) || !strncmp(s->url, "udp:", 4)))
        return 1;
    return 0;
}

int get_master_sync_type(VideoState* is)
{
    if (is->av_sync_type == AV_SYNC_VIDEO_MASTER) {
        if (is->video_st)
            return AV_SYNC_VIDEO_MASTER;
        else
            return AV_SYNC_AUDIO_MASTER;
    } else if (is->av_sync_type == AV_SYNC_AUDIO_MASTER) {
        if (is->audio_st)
            return AV_SYNC_AUDIO_MASTER;
        else
            return AV_SYNC_EXTERNAL_CLOCK;
    } else {
        return AV_SYNC_EXTERNAL_CLOCK;
    }
}

/* get the current master clock value */
double get_master_clock(VideoState* is)
{
    double val;

    switch (get_master_sync_type(is)) {
    case AV_SYNC_VIDEO_MASTER:
        val = get_clock(&is->vidclk);
        break;
    case AV_SYNC_AUDIO_MASTER:
        val = get_clock(&is->audclk);
        break;
    default:
        val = get_clock(&is->extclk);
        break;
    }
    return val;
}

void check_external_clock_speed(VideoState* is)
{
    if ((is->video_stream >= 0 && is->videoq.nb_packets <= EXTERNAL_CLOCK_MIN_FRAMES)
        || (is->audio_stream >= 0 && is->audioq.nb_packets <= EXTERNAL_CLOCK_MIN_FRAMES)) {
        set_clock_speed(&is->extclk,
                        FFMAX(EXTERNAL_CLOCK_SPEED_MIN,
                              is->extclk.speed - EXTERNAL_CLOCK_SPEED_STEP));
    } else if ((is->video_stream < 0 || is->videoq.nb_packets > EXTERNAL_CLOCK_MAX_FRAMES)
               && (is->audio_stream < 0 || is->audioq.nb_packets > EXTERNAL_CLOCK_MAX_FRAMES)) {
        set_clock_speed(&is->extclk,
                        FFMIN(EXTERNAL_CLOCK_SPEED_MAX,
                              is->extclk.speed + EXTERNAL_CLOCK_SPEED_STEP));
    } else {
        double speed = is->extclk.speed;
        if (speed != 1.0)
            set_clock_speed(&is->extclk,
                            speed + EXTERNAL_CLOCK_SPEED_STEP * (1.0 - speed) / fabs(1.0 - speed));
    }
}

/* seek in the stream */
void stream_seek(VideoState* is, int64_t pos, int64_t rel, int by_bytes)
{
    if (!is->seek_req) {
        is->seek_pos = pos;
        is->seek_rel = rel;
        is->seek_flags &= ~AVSEEK_FLAG_BYTE;
        if (by_bytes)
            is->seek_flags |= AVSEEK_FLAG_BYTE;
        is->seek_flags |= AVSEEK_FLAG_ANY;
        is->seek_req = 1;
        // SDL_CondSignal(is->continue_read_thread);
        is->continue_read_thread->wakeAll();
    }
}

/* pause or resume the video */
// void stream_toggle_pause(VideoState* is, bool pause)
//{
//	if (is->paused) {
//		is->frame_timer += av_gettime_relative() / 1000000.0 -
//is->vidclk.last_updated; 		if (is->read_pause_return != AVERROR(ENOSYS)) {
//			is->vidclk.paused = 0;
//		}
//		set_clock(&is->vidclk, get_clock(&is->vidclk),
//is->vidclk.serial);
//	}
//	set_clock(&is->extclk, get_clock(&is->extclk), is->extclk.serial);
//	is->paused = is->audclk.paused = is->vidclk.paused = is->extclk.paused =
//pause; // !is->paused; 	is->step = 0;
//}

void toggle_pause(VideoState* is, bool pause)
{
    if (is->paused) {
        is->frame_timer += av_gettime_relative() / 1000000.0 - is->vidclk.last_updated;
        if (is->read_pause_return != AVERROR(ENOSYS)) {
            is->vidclk.paused = 0;
        }
        set_clock(&is->vidclk, get_clock(&is->vidclk), is->vidclk.serial);
    }
    set_clock(&is->extclk, get_clock(&is->extclk), is->extclk.serial);
    is->paused = is->audclk.paused = is->vidclk.paused = is->extclk.paused = pause; // !is->paused;
    is->step = 0;
}

void toggle_mute(VideoState* is, bool mute)
{
    bool muted = !!is->muted;
    if (muted != mute) {
        is->muted = mute;
    }
}

void update_volume(VideoState* is, int sign, double step)
{
    double volume_level = is->audio_volume
                              ? (20 * log(is->audio_volume / (double) SDL_MIX_MAXVOLUME) / log(10))
                              : -1000.0;
    int new_volume = lrint(SDL_MIX_MAXVOLUME * pow(10.0, (volume_level + sign * step) / 20.0));
    is->audio_volume = av_clip(is->audio_volume == new_volume ? (is->audio_volume + sign)
                                                              : new_volume,
                               0,
                               SDL_MIX_MAXVOLUME);
}

void step_to_next_frame(VideoState* is)
{
    /* if the stream is paused unpause it, then step */
    if (is->paused)
        toggle_pause(is, !is->paused);
    is->step = 1;
}

double compute_target_delay(double delay, VideoState* is)
{
    double sync_threshold, diff = 0;

    /* update delay to follow master synchronisation source */
    if (get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER) {
        /* if video is slave, we try to correct big delays by
       duplicating or deleting a frame */
        diff = get_clock(&is->vidclk) - get_master_clock(is);

        /* skip or repeat frame. We take into account the
       delay to compute the threshold. I still don't know
       if it is the best guess */
        sync_threshold = FFMAX(AV_SYNC_THRESHOLD_MIN, FFMIN(AV_SYNC_THRESHOLD_MAX, delay));
        if (!isnan(diff) && fabs(diff) < is->max_frame_duration) {
            if (diff <= -sync_threshold)
                delay = FFMAX(0, delay + diff);
            else if (diff >= sync_threshold && delay > AV_SYNC_FRAMEDUP_THRESHOLD)
                delay = delay + diff;
            else if (diff >= sync_threshold)
                delay = 2 * delay;
        }
    }

    av_log(nullptr, AV_LOG_TRACE, "video: delay=%0.3f A-V=%f\n", delay, -diff);
    return delay;
}

double vp_duration(VideoState* is, Frame* vp, Frame* nextvp)
{
    if (vp->serial == nextvp->serial) {
        double duration = nextvp->pts - vp->pts;
        if (isnan(duration) || duration <= 0 || duration > is->max_frame_duration)
            return vp->duration;
        else
            return duration;
    }

    return 0.0;
}

void update_video_pts(VideoState* is, double pts, int64_t pos, int serial)
{
    /* update current video pts */
    set_clock(&is->vidclk, pts, serial);
    sync_clock_to_slave(&is->extclk, &is->vidclk);
}

#if PRINT_PACKETQUEUE_INFO
void print_state_info(VideoState* is)
{
    if (is) {
        PacketQueue* pPacket = &is->videoq;
        qDebug("[VideoState] V PacketQueue[%p](nb_packets:%d,size:%d,dur:%lld, "
               "abort:%d, serial:%d)",
               pPacket,
               pPacket->nb_packets,
               pPacket->size,
               pPacket->duration,
               pPacket->abort_request,
               pPacket->serial);

        pPacket = &is->audioq;
        qDebug("[VideoState] A PacketQueue[%p](nb_packets:%d,size:%d,dur:%lld, "
               "abort:%d, serial:%d)",
               pPacket,
               pPacket->nb_packets,
               pPacket->size,
               pPacket->duration,
               pPacket->abort_request,
               pPacket->serial);

        pPacket = &is->subtitleq;
        qDebug("[VideoState] S PacketQueue[%p](nb_packets:%d,size:%d,dur:%lld, "
               "abort:%d, serial:%d)",
               pPacket,
               pPacket->nb_packets,
               pPacket->size,
               pPacket->duration,
               pPacket->abort_request,
               pPacket->serial);

        /*qDebug("[VideoState]FrameQueue(v:%p,a:%p,s:%p)",
            &is->pictq, &is->sampq, &is->subpq);
    qDebug("[VideoState]Decoder(v:%p,a:%p,s:%p)",
            &is->viddec, &is->auddec, &is->subdec);
    qDebug("[VideoState]Clock(v:%p,a:%p,s:%p)",
            &is->vidclk, &is->audclk, &is->extclk);*/
    }
}
#endif

#if USE_AVFILTER_AUDIO
void set_audio_playspeed(VideoState* is, double value)
{
    if (value < 0 || value > 4)
        return;

    if (is->audio_speed == value)
        return;

    is->audio_speed = value;

    const size_t len = 32;
    if (!is->afilters)
        is->afilters = (char*) av_malloc(len);

    if (value <= 0.5) {
        snprintf(is->afilters, len, "atempo=0.5,");
        char tmp[128];
        snprintf(tmp, sizeof(tmp), "atempo=%lf", value / 0.5);

        // strncat(is->afilters, tmp, len - strlen(is->afilters) - 1);
        strncat_s(is->afilters, len, tmp, len - strlen(is->afilters) - 1);
    } else if (value <= 2.0) {
        snprintf(is->afilters, len, "atempo=%lf", value);
    } else {
        snprintf(is->afilters, len, "atempo=2.0,");
        char tmp[128];
        snprintf(tmp, sizeof(tmp), "atempo=%lf", value / 2.0);
        // strncat(is->afilters, tmp, len - strlen(is->afilters) - 1);
        strncat_s(is->afilters, len, tmp, len - strlen(is->afilters) - 1);
    }

    qDebug("changing audio filters to :%s", is->afilters);

#if USE_AVFILTER_VIDEO
    set_video_playspeed(is);
#endif

    is->audio_clock_old = is->audio_clock;
    is->req_afilter_reconfigure = 1;
}

void set_video_watermark(VideoState* is, const char* watermark_text)
{
    if (!is || !is->video_st) {
        return;
    }

    // 获取当前播放时间
    double current_time = get_master_clock(is);
    if (isnan(current_time)) current_time = 0.0;
    
    // 获取总时长
    double total_duration = 0.0;
    if (is->ic && is->ic->duration != AV_NOPTS_VALUE) {
        total_duration = is->ic->duration / (double)AV_TIME_BASE;
    }
    
    // 格式化时间显示
    int cur_hours = (int)(current_time / 3600);
    int cur_mins = (int)((current_time - cur_hours * 3600) / 60);
    int cur_secs = (int)(current_time - cur_hours * 3600 - cur_mins * 60);
    
    int total_hours = (int)(total_duration / 3600);
    int total_mins = (int)((total_duration - total_hours * 3600) / 60);
    int total_secs_int = (int)(total_duration - total_hours * 3600 - total_mins * 60);
    
    size_t len = 512;
    if (!is->vfilters)
        is->vfilters = (char*) av_malloc(len);

    snprintf(is->vfilters,
             len,
             "drawtext=fontfile='C\\:/Windows/Fonts/"
             "arial.ttf':text='%02d\\:%02d\\:%02d / %02d\\:%02d\\:%02d':x=10:y=10:fontsize=24:fontcolor=white",
             cur_hours, cur_mins, cur_secs, total_hours, total_mins, total_secs_int);

    is->req_vfilter_reconfigure = 1;

    qDebug("setting video watermark to :%s", is->vfilters);
}

void set_video_playspeed(VideoState* is)
{
    double speed = is->audio_speed;

    // 获取当前播放时间
    double current_time = get_master_clock(is);
    if (isnan(current_time)) current_time = 0.0;
    
    // 获取总时长
    double total_duration = 0.0;
    if (is->ic && is->ic->duration != AV_NOPTS_VALUE) {
        total_duration = is->ic->duration / (double)AV_TIME_BASE;
    }
    
    // 格式化时间显示
    int cur_hours = (int)(current_time / 3600);
    int cur_mins = (int)((current_time - cur_hours * 3600) / 60);
    int cur_secs = (int)(current_time - cur_hours * 3600 - cur_mins * 60);
    
    int total_hours = (int)(total_duration / 3600);
    int total_mins = (int)((total_duration - total_hours * 3600) / 60);
    int total_secs_int = (int)(total_duration - total_hours * 3600 - total_mins * 60);

    size_t len = 512; // 增加缓冲区大小以容纳水印滤镜
    if (!is->vfilters)
        is->vfilters = (char*) av_malloc(len);

    // 组合播放速度和水印滤镜
    snprintf(is->vfilters,
             len,
             "setpts=%.4lf*PTS,drawtext=fontfile='C\\:/Windows/Fonts/"
             "arial.ttf':text='%02d\\:%02d\\:%02d / %02d\\:%02d\\:%02d':x=10:y=10:fontsize=24:fontcolor=white",
             1.0 / speed, cur_hours, cur_mins, cur_secs, total_hours, total_mins, total_secs_int);

    is->req_vfilter_reconfigure = 1;

    qDebug("changing video filters to :%s", is->vfilters);
}

int cmp_audio_fmts(enum AVSampleFormat fmt1,
                   int64_t channel_count1,
                   enum AVSampleFormat fmt2,
                   int64_t channel_count2)
{
    /* If channel count == 1, planar and non-planar formats are the same */
    if (channel_count1 == 1 && channel_count2 == 1)
        return av_get_packed_sample_fmt(fmt1) != av_get_packed_sample_fmt(fmt2);
    else
        return channel_count1 != channel_count2 || fmt1 != fmt2;
}

// int64_t get_valid_channel_layout(int64_t channel_layout, int channels)
//{
//	if (channel_layout && av_get_channel_layout_nb_channels(channel_layout)
//== channels) 		return channel_layout; 	else 		return 0;
//}

int configure_filtergraph(AVFilterGraph* graph,
                          const char* filtergraph,
                          AVFilterContext* source_ctx,
                          AVFilterContext* sink_ctx)
{
    int ret;
    int nb_filters = graph->nb_filters;
    AVFilterInOut *outputs = nullptr, *inputs = nullptr;

    if (filtergraph) {
        // 添加调试信息
        qDebug("configure_filtergraph: filtergraph = %s", filtergraph);

        outputs = avfilter_inout_alloc();
        inputs = avfilter_inout_alloc();
        if (!outputs || !inputs) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }

        outputs->name = av_strdup("in");
        outputs->filter_ctx = source_ctx;
        outputs->pad_idx = 0;
        outputs->next = nullptr;

        inputs->name = av_strdup("out");
        inputs->filter_ctx = sink_ctx;
        inputs->pad_idx = 0;
        inputs->next = nullptr;

        if ((ret = avfilter_graph_parse_ptr(graph, filtergraph, &inputs, &outputs, nullptr)) < 0) {
            qDebug("avfilter_graph_parse_ptr failed with error: %d", ret);
            goto fail;
        }
    } else {
        if ((ret = avfilter_link(source_ctx, 0, sink_ctx, 0)) < 0)
            goto fail;
    }

    /* Reorder the filters to ensure that inputs of the custom filters are merged
   * first */
    for (unsigned int i = 0; i < graph->nb_filters - nb_filters; i++)
        FFSWAP(AVFilterContext*, graph->filters[i], graph->filters[i + nb_filters]);

    ret = avfilter_graph_config(graph, nullptr);
fail:
    avfilter_inout_free(&outputs);
    avfilter_inout_free(&inputs);
    return ret;
}

int configure_audio_filters(VideoState* is, const char* afilters, int force_output_format)
{
    static const enum AVSampleFormat sample_fmts[] = {AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE};
    int sample_rates[2] = {0, -1};
    // int64_t channel_layouts[2] = { 0, -1 };
    // AVChannelLayout channel_layouts[2] = {};
    // int channels[2] = { 0, -1 };
    AVFilterContext *filt_asrc = nullptr, *filt_asink = nullptr;
    // char aresample_swr_opts[512] = "";
    // const AVDictionaryEntry* e = nullptr;
    char asrc_args[256];
    int ret;
    AVBPrint bp;

    avfilter_graph_free(&is->agraph);
    if (!(is->agraph = avfilter_graph_alloc()))
        return AVERROR(ENOMEM);
    is->agraph->nb_threads = 0;

    /*while ((e = av_dict_get(swr_opts, "", e, AV_DICT_IGNORE_SUFFIX)))
          av_strlcatf(aresample_swr_opts, sizeof(aresample_swr_opts), "%s=%s:",
  e->key, e->value); if (strlen(aresample_swr_opts))
          aresample_swr_opts[strlen(aresample_swr_opts) - 1] = '\0';
  av_opt_set(is->agraph, "aresample_swr_opts", aresample_swr_opts, 0);*/

    av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
    av_channel_layout_describe_bprint(&is->audio_filter_src.ch_layout, &bp);

    snprintf(asrc_args,
             sizeof(asrc_args),
             "sample_rate=%d:sample_fmt=%s:time_base=%d/%d:channel_layout=%s",
             is->audio_filter_src.freq,
             av_get_sample_fmt_name(is->audio_filter_src.fmt),
             1,
             is->audio_filter_src.freq,
             bp.str);

    ret = avfilter_graph_create_filter(&filt_asrc,
                                       avfilter_get_by_name("abuffer"),
                                       "ffplay_abuffer",
                                       asrc_args,
                                       nullptr,
                                       is->agraph);
    if (ret < 0)
        goto end;

    ret = avfilter_graph_create_filter(&filt_asink,
                                       avfilter_get_by_name("abuffersink"),
                                       "ffplay_abuffersink",
                                       nullptr,
                                       nullptr,
                                       is->agraph);
    if (ret < 0)
        goto end;

    if ((ret = av_opt_set_int_list(filt_asink,
                                   "sample_fmts",
                                   sample_fmts,
                                   AV_SAMPLE_FMT_NONE,
                                   AV_OPT_SEARCH_CHILDREN))
        < 0)
        goto end;
    if ((ret = av_opt_set_int(filt_asink, "all_channel_counts", 1, AV_OPT_SEARCH_CHILDREN)) < 0)
        goto end;

    if (force_output_format) {
        sample_rates[0] = is->audio_filter_src.freq;
        // channel_layouts[0] = is->audio_filter_src.channel_layout;
        // channels[0] = is->audio_filter_src.channels;
        if ((ret = av_opt_set_int(filt_asink, "all_channel_counts", 0, AV_OPT_SEARCH_CHILDREN)) < 0)
            goto end;

        if ((ret = av_opt_set(filt_asink, "ch_layouts", bp.str, AV_OPT_SEARCH_CHILDREN)) < 0)
            goto end;
        /*if ((ret = av_opt_set_int_list(filt_asink, "channel_layouts",
       channel_layouts, -1, AV_OPT_SEARCH_CHILDREN)) < 0) goto end;*/
        /*if ((ret = av_opt_set_int_list(filt_asink, "channel_counts", channels, -1,
       AV_OPT_SEARCH_CHILDREN)) < 0) goto end;*/
        if ((ret = av_opt_set_int_list(filt_asink,
                                       "sample_rates",
                                       sample_rates,
                                       -1,
                                       AV_OPT_SEARCH_CHILDREN))
            < 0)
            goto end;
    }

    if ((ret = configure_filtergraph(is->agraph, afilters, filt_asrc, filt_asink)) < 0)
        goto end;

    is->in_audio_filter = filt_asrc;
    is->out_audio_filter = filt_asink;

end:
    if (ret < 0)
        avfilter_graph_free(&is->agraph);
    av_bprint_finalize(&bp, NULL);
    return ret;
}

int configure_video_filters(AVFilterGraph* graph,
                            VideoState* is,
                            const char* vfilters,
                            AVFrame* frame)
{
    enum AVPixelFormat pix_fmts[1]; // FF_ARRAY_ELEMS(sdl_texture_format_map)
    // char sws_flags_str[512] = "";
    char buffersrc_args[256];
    int ret;
    AVFilterContext *filt_src = nullptr, *filt_out = nullptr, *last_filter = nullptr;
    AVCodecParameters* codecpar = is->video_st->codecpar;
    AVRational fr = av_guess_frame_rate(is->ic, is->video_st, nullptr);
    // const AVDictionaryEntry* e = nullptr;
    int nb_pix_fmts = 0;

    /*
  int i, j;
  for (i = 0; i < renderer_info.num_texture_formats; i++) {
          for (j = 0; j < FF_ARRAY_ELEMS(sdl_texture_format_map) - 1; j++) {
                  if (renderer_info.texture_formats[i] ==
  sdl_texture_format_map[j].texture_fmt) { pix_fmts[nb_pix_fmts++] =
  sdl_texture_format_map[j].format; break;
                  }
          }
  }*/
    pix_fmts[nb_pix_fmts] = AV_PIX_FMT_NONE;

    /*while ((e = av_dict_get(sws_dict, "", e, AV_DICT_IGNORE_SUFFIX))) {
          if (!strcmp(e->key, "sws_flags")) {
                  av_strlcatf(sws_flags_str, sizeof(sws_flags_str), "%s=%s:",
  "flags", e->value);
          }
          else
                  av_strlcatf(sws_flags_str, sizeof(sws_flags_str), "%s=%s:",
  e->key, e->value);
  }
  if (strlen(sws_flags_str))
          sws_flags_str[strlen(sws_flags_str) - 1] = '\0';

  graph->scale_sws_opts = av_strdup(sws_flags_str);*/

    snprintf(buffersrc_args,
             sizeof(buffersrc_args),
             "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
             frame->width,
             frame->height,
             frame->format,
             is->video_st->time_base.num,
             is->video_st->time_base.den,
             codecpar->sample_aspect_ratio.num,
             FFMAX(codecpar->sample_aspect_ratio.den, 1));
    if (fr.num && fr.den)
        av_strlcatf(buffersrc_args, sizeof(buffersrc_args), ":frame_rate=%d/%d", fr.num, fr.den);

    if ((ret = avfilter_graph_create_filter(&filt_src,
                                            avfilter_get_by_name("buffer"),
                                            "ffplay_buffer",
                                            buffersrc_args,
                                            nullptr,
                                            graph))
        < 0)
        goto fail;

    ret = avfilter_graph_create_filter(&filt_out,
                                       avfilter_get_by_name("buffersink"),
                                       "ffplay_buffersink",
                                       nullptr,
                                       nullptr,
                                       graph);
    if (ret < 0)
        goto fail;

    if ((ret = av_opt_set_int_list(filt_out,
                                   "pix_fmts",
                                   pix_fmts,
                                   AV_PIX_FMT_NONE,
                                   AV_OPT_SEARCH_CHILDREN))
        < 0)
        goto fail;

    last_filter = filt_out;

#if 0
	/* Note: this macro adds a filter before the lastly added filter, so the
	 * processing order of the filters is in reverse */
#define INSERT_FILT(name, arg) \
    do { \
        AVFilterContext* filt_ctx; \
\
        ret = avfilter_graph_create_filter(&filt_ctx, \
                                           avfilter_get_by_name(name), \
                                           "ffplay_" name, \
                                           arg, \
                                           nullptr, \
                                           graph); \
        if (ret < 0) \
            goto fail; \
\
        ret = avfilter_link(filt_ctx, 0, last_filter, 0); \
        if (ret < 0) \
            goto fail; \
\
        last_filter = filt_ctx; \
    } while (0)

	if (autorotate) {
		int32_t* displaymatrix = (int32_t*)av_stream_get_side_data(is->video_st, AV_PKT_DATA_DISPLAYMATRIX, nullptr);
		double theta = get_rotation(displaymatrix);

		if (fabs(theta - 90) < 1.0) {
			INSERT_FILT("transpose", "clock");
		}
		else if (fabs(theta - 180) < 1.0) {
			INSERT_FILT("hflip", nullptr);
			INSERT_FILT("vflip", nullptr);
		}
		else if (fabs(theta - 270) < 1.0) {
			INSERT_FILT("transpose", "cclock");
		}
		else if (fabs(theta) > 1.0) {
			char rotate_buf[64];
			snprintf(rotate_buf, sizeof(rotate_buf), "%f*PI/180", theta);
			INSERT_FILT("rotate", rotate_buf);
		}
	}
#endif

    if ((ret = configure_filtergraph(graph, vfilters, filt_src, last_filter)) < 0)
        goto fail;

    is->in_video_filter = filt_src;
    is->out_video_filter = filt_out;

fail:
    return ret;
}

#if 0
int audio_open(void* opaque, int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate, struct AudioParams* audio_hw_params)
{
	//SDL_AudioSpec wanted_spec, spec;
	//const char* env;
	static const int next_nb_channels[] = { 0, 0, 1, 6, 2, 6, 4, 6 };
	static const int next_sample_rates[] = { 0, 44100, 48000, 96000, 192000 };
	//int next_sample_rate_idx = FF_ARRAY_ELEMS(next_sample_rates) - 1;

	/*env = SDL_getenv("SDL_AUDIO_CHANNELS");
	if (env) {
		wanted_nb_channels = atoi(env);
		wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
	}*/

	if (!wanted_channel_layout || wanted_nb_channels != av_get_channel_layout_nb_channels(wanted_channel_layout)) {
		wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
		wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
	}

	wanted_nb_channels = av_get_channel_layout_nb_channels(wanted_channel_layout);
	/*
	wanted_spec.channels = wanted_nb_channels;
	wanted_spec.freq = wanted_sample_rate;
	if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0) {
		av_log(nullptr, AV_LOG_ERROR, "Invalid sample rate or channel count!\n");
		return -1;
	}
	while (next_sample_rate_idx && next_sample_rates[next_sample_rate_idx] >= wanted_spec.freq)
		next_sample_rate_idx--;
	wanted_spec.format = AUDIO_S16SYS;
	wanted_spec.silence = 0;
	wanted_spec.samples = FFMAX(SDL_AUDIO_MIN_BUFFER_SIZE, 2 << av_log2(wanted_spec.freq / SDL_AUDIO_MAX_CALLBACKS_PER_SEC));
	wanted_spec.callback = sdl_audio_callback;
	wanted_spec.userdata = opaque;
	while (!(audio_dev = SDL_OpenAudioDevice(nullptr, 0, &wanted_spec, &spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE))) {
		av_log(nullptr, AV_LOG_WARNING, "SDL_OpenAudio (%d channels, %d Hz): %s\n",
			wanted_spec.channels, wanted_spec.freq, SDL_GetError());
		wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)];
		if (!wanted_spec.channels) {
			wanted_spec.freq = next_sample_rates[next_sample_rate_idx--];
			wanted_spec.channels = wanted_nb_channels;
			if (!wanted_spec.freq) {
				av_log(nullptr, AV_LOG_ERROR,
					"No more combinations to try, audio open failed\n");
				return -1;
			}
		}
		wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels);
	}
	if (spec.format != AUDIO_S16SYS) {
		av_log(nullptr, AV_LOG_ERROR,
			"SDL advised audio format %d is not supported!\n", spec.format);
		return -1;
	}
	if (spec.channels != wanted_spec.channels) {
		wanted_channel_layout = av_get_default_channel_layout(spec.channels);
		if (!wanted_channel_layout) {
			av_log(nullptr, AV_LOG_ERROR,
				"SDL advised channel count %d is not supported!\n", spec.channels);
			return -1;
		}
	}*/

	audio_hw_params->fmt = AV_SAMPLE_FMT_S16;
	audio_hw_params->freq = wanted_sample_rate; // spec.freq;
	audio_hw_params->channel_layout.nb_channels = wanted_channel_layout;
	audio_hw_params->channels = wanted_nb_channels; // spec.channels;

	audio_hw_params->frame_size = av_samples_get_buffer_size(nullptr, audio_hw_params->channels, 1, audio_hw_params->fmt, 1);
	audio_hw_params->bytes_per_sec = av_samples_get_buffer_size(nullptr, audio_hw_params->channels, audio_hw_params->freq, audio_hw_params->fmt, 1);
	if (audio_hw_params->bytes_per_sec <= 0 || audio_hw_params->frame_size <= 0) {
		av_log(nullptr, AV_LOG_ERROR, "av_samples_get_buffer_size failed\n");
		return -1;
	}
	return 0;// spec.size;
}
#endif

#endif
