Skip to content
Snippets Groups Projects
ffplay.c 114 KiB
Newer Older
  • Learn to ignore specific revisions
  • static void do_exit(VideoState *is)
    
        if (is) {
            stream_close(is);
    
        uninit_opts();
    #if CONFIG_AVFILTER
        avfilter_uninit();
    
        av_freep(&vfilters);
    
        avformat_network_deinit();
    
        if (show_status)
            printf("\n");
        SDL_Quit();
    
        av_log(NULL, AV_LOG_QUIET, "%s", "");
    
    static void sigterm_handler(int sig)
    {
        exit(123);
    }
    
    
    static int video_open(VideoState *is, int force_set_video_mode)
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
    {
        int flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL;
    
        VideoPicture *vp = &is->pictq[is->pictq_rindex];
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        if (is_full_screen) flags |= SDL_FULLSCREEN;
        else                flags |= SDL_RESIZABLE;
    
        if (is_full_screen && fs_screen_width) {
            w = fs_screen_width;
            h = fs_screen_height;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        } else if (!is_full_screen && screen_width) {
    
            calculate_display_rect(&rect, 0, 0, INT_MAX, vp->height, vp);
            w = rect.w;
            h = rect.h;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        if (screen && is->width == screen->w && screen->w == w
    
           && is->height== screen->h && screen->h == h && !force_set_video_mode)
    
        screen = SDL_SetVideoMode(w, h, 0, flags);
        if (!screen) {
            fprintf(stderr, "SDL: could not set video mode - exiting\n");
    
        if (!window_title)
            window_title = input_filename;
        SDL_WM_SetCaption(window_title, window_title);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        is->width  = screen->w;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    /* display the current picture, if any */
    static void video_display(VideoState *is)
    {
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        if (!screen)
    
        if (is->audio_st && is->show_mode != SHOW_MODE_VIDEO)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            video_audio_display(is);
        else if (is->video_st)
            video_image_display(is);
    }
    
    
    static int refresh_thread(void *opaque)
    
        VideoState *is= opaque;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        while (!is->abort_request) {
    
            SDL_Event event;
            event.type = FF_REFRESH_EVENT;
            event.user.data1 = opaque;
    
            if (!is->refresh && (!is->paused || is->force_refresh)) {
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                is->refresh = 1;
    
                SDL_PushEvent(&event);
    
            //FIXME ideally we should wait the correct time but SDLs event passing is so slow it would be silly
    
            av_usleep(is->audio_st && is->show_mode != SHOW_MODE_VIDEO ? rdftspeed*1000 : 5000);
    
        }
        return 0;
    
    /* get the current audio clock value */
    static double get_audio_clock(VideoState *is)
    {
    
        if (is->paused) {
            return is->audio_current_pts;
        } else {
            return is->audio_current_pts_drift + av_gettime() / 1000000.0;
    
        }
    }
    
    /* get the current video clock value */
    static double get_video_clock(VideoState *is)
    {
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
        if (is->paused) {
    
            return is->video_current_pts;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        } else {
    
            return is->video_current_pts_drift + av_gettime() / 1000000.0;
    
    }
    
    /* get the current external clock value */
    static double get_external_clock(VideoState *is)
    {
    
        if (is->paused) {
            return is->external_clock;
        } else {
    
            double time = av_gettime() / 1000000.0;
            return is->external_clock_drift + time - (time - is->external_clock_time / 1000000.0) * (1.0 - is->external_clock_speed);
    
    static int get_master_sync_type(VideoState *is) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (is->av_sync_type == AV_SYNC_VIDEO_MASTER) {
            if (is->video_st)
    
                return AV_SYNC_VIDEO_MASTER;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            else
    
                return AV_SYNC_AUDIO_MASTER;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        } else if (is->av_sync_type == AV_SYNC_AUDIO_MASTER) {
            if (is->audio_st)
    
                return AV_SYNC_AUDIO_MASTER;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            else
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        } else {
    
            return AV_SYNC_EXTERNAL_CLOCK;
        }
    }
    
    /* get the current master clock value */
    static double get_master_clock(VideoState *is)
    {
        double val;
    
        switch (get_master_sync_type(is)) {
            case AV_SYNC_VIDEO_MASTER:
                val = get_video_clock(is);
                break;
            case AV_SYNC_AUDIO_MASTER:
                val = get_audio_clock(is);
                break;
            default:
                val = get_external_clock(is);
                break;
    
    static void update_external_clock_pts(VideoState *is, double pts)
    {
       is->external_clock_time = av_gettime();
       is->external_clock = pts;
       is->external_clock_drift = pts - is->external_clock_time / 1000000.0;
    }
    
    static void check_external_clock_sync(VideoState *is, double pts) {
        if (fabs(get_external_clock(is) - pts) > AV_NOSYNC_THRESHOLD) {
            update_external_clock_pts(is, pts);
        }
    }
    
    
    static void update_external_clock_speed(VideoState *is, double speed) {
        update_external_clock_pts(is, get_external_clock(is));
        is->external_clock_speed = speed;
    }
    
    static void check_external_clock_speed(VideoState *is) {
       if (is->video_stream >= 0 && is->videoq.nb_packets <= MIN_FRAMES / 2 ||
           is->audio_stream >= 0 && is->audioq.nb_packets <= MIN_FRAMES / 2) {
           update_external_clock_speed(is, FFMAX(EXTERNAL_CLOCK_SPEED_MIN, is->external_clock_speed - EXTERNAL_CLOCK_SPEED_STEP));
       } else if ((is->video_stream < 0 || is->videoq.nb_packets > MIN_FRAMES * 2) &&
                  (is->audio_stream < 0 || is->audioq.nb_packets > MIN_FRAMES * 2)) {
           update_external_clock_speed(is, FFMIN(EXTERNAL_CLOCK_SPEED_MAX, is->external_clock_speed + EXTERNAL_CLOCK_SPEED_STEP));
       } else {
           double speed = is->external_clock_speed;
           if (speed != 1.0)
               update_external_clock_speed(is, speed + EXTERNAL_CLOCK_SPEED_STEP * (1.0 - speed) / fabs(1.0 - speed));
       }
    }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    /* seek in the stream */
    
    static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int seek_by_bytes)
    
        if (!is->seek_req) {
            is->seek_pos = pos;
    
            is->seek_rel = rel;
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
            is->seek_flags &= ~AVSEEK_FLAG_BYTE;
    
            if (seek_by_bytes)
                is->seek_flags |= AVSEEK_FLAG_BYTE;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    }
    
    /* pause or resume the video */
    
    static void stream_toggle_pause(VideoState *is)
    
        if (is->paused) {
            is->frame_timer += av_gettime() / 1000000.0 + is->video_current_pts_drift - is->video_current_pts;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            if (is->read_pause_return != AVERROR(ENOSYS)) {
    
                is->video_current_pts = is->video_current_pts_drift + av_gettime() / 1000000.0;
    
            is->video_current_pts_drift = is->video_current_pts - av_gettime() / 1000000.0;
    
        update_external_clock_pts(is, get_external_clock(is));
    
    static double compute_target_delay(double delay, VideoState *is)
    
    
        /* 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_video_clock(is) - 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, delay);
            if (fabs(diff) < AV_NOSYNC_THRESHOLD) {
                if (diff <= -sync_threshold)
                    delay = 0;
                else if (diff >= sync_threshold)
                    delay = 2 * delay;
            }
        }
    
        av_dlog(NULL, "video: delay=%0.3f A-V=%f\n",
                delay, -diff);
    
        return delay;
    }
    
    static void pictq_next_picture(VideoState *is) {
        /* update queue size and signal for next picture */
        if (++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE)
            is->pictq_rindex = 0;
    
        SDL_LockMutex(is->pictq_mutex);
        is->pictq_size--;
        SDL_CondSignal(is->pictq_cond);
        SDL_UnlockMutex(is->pictq_mutex);
    
    static void pictq_prev_picture(VideoState *is) {
        VideoPicture *prevvp;
        /* update queue size and signal for the previous picture */
        prevvp = &is->pictq[(is->pictq_rindex + VIDEO_PICTURE_QUEUE_SIZE - 1) % VIDEO_PICTURE_QUEUE_SIZE];
    
        if (prevvp->allocated && prevvp->serial == is->videoq.serial) {
    
            SDL_LockMutex(is->pictq_mutex);
    
            if (is->pictq_size < VIDEO_PICTURE_QUEUE_SIZE - 1) {
    
                if (--is->pictq_rindex == -1)
                    is->pictq_rindex = VIDEO_PICTURE_QUEUE_SIZE - 1;
                is->pictq_size++;
            }
            SDL_CondSignal(is->pictq_cond);
            SDL_UnlockMutex(is->pictq_mutex);
        }
    }
    
    
    static void update_video_pts(VideoState *is, double pts, int64_t pos, int serial) {
    
        double time = av_gettime() / 1000000.0;
        /* update current video pts */
        is->video_current_pts = pts;
        is->video_current_pts_drift = is->video_current_pts - time;
        is->video_current_pos = pos;
        is->frame_last_pts = pts;
    
        if (is->videoq.serial == serial)
            check_external_clock_sync(is, is->video_current_pts);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    /* called to display each frame */
    
    static void video_refresh(void *opaque)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        VideoState *is = opaque;
        VideoPicture *vp;
    
        double time;
    
        if (!is->paused && get_master_sync_type(is) == AV_SYNC_EXTERNAL_CLOCK && is->realtime)
            check_external_clock_speed(is);
    
    
        if (!display_disable && is->show_mode != SHOW_MODE_VIDEO && is->audio_st)
            video_display(is);
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (is->video_st) {
    
            if (is->force_refresh)
                pictq_prev_picture(is);
    
    retry:
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (is->pictq_size == 0) {
    
                SDL_LockMutex(is->pictq_mutex);
                if (is->frame_last_dropped_pts != AV_NOPTS_VALUE && is->frame_last_dropped_pts > is->frame_last_pts) {
    
                    update_video_pts(is, is->frame_last_dropped_pts, is->frame_last_dropped_pos, 0);
    
                    is->frame_last_dropped_pts = AV_NOPTS_VALUE;
                }
                SDL_UnlockMutex(is->pictq_mutex);
    
                // nothing to do, no picture to display in the queue
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else {
    
                double last_duration, duration, delay;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                vp = &is->pictq[is->pictq_rindex];
    
                if (vp->serial != is->videoq.serial) {
    
                /* compute nominal last_duration */
                last_duration = vp->pts - is->frame_last_pts;
    
                if (last_duration > 0 && last_duration < is->max_frame_duration) {
    
                    /* if duration of the last frame was sane, update last_duration in video state */
                    is->frame_last_duration = last_duration;
                }
                delay = compute_target_delay(is->frame_last_duration, is);
    
    
                time= av_gettime()/1000000.0;
    
                if (time < is->frame_timer + delay)
    
                    return;
    
                if (delay > 0)
                    is->frame_timer += delay * FFMAX(1, floor((time-is->frame_timer) / delay));
    
                SDL_LockMutex(is->pictq_mutex);
    
                update_video_pts(is, vp->pts, vp->pos, vp->serial);
    
                SDL_UnlockMutex(is->pictq_mutex);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                if (is->pictq_size > 1) {
                    VideoPicture *nextvp = &is->pictq[(is->pictq_rindex + 1) % VIDEO_PICTURE_QUEUE_SIZE];
    
                    duration = nextvp->pts - vp->pts;
    
                    if(!is->step && (framedrop>0 || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) && time > is->frame_timer + duration){
    
                        is->frame_drops_late++;
    
                        goto retry;
                    }
                }
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                if (is->subtitle_st) {
    
                    if (is->subtitle_stream_changed) {
                        SDL_LockMutex(is->subpq_mutex);
    
                        while (is->subpq_size) {
                            free_subpicture(&is->subpq[is->subpq_rindex]);
    
                            /* update queue size and signal for next picture */
                            if (++is->subpq_rindex == SUBPICTURE_QUEUE_SIZE)
                                is->subpq_rindex = 0;
    
                            is->subpq_size--;
                        }
                        is->subtitle_stream_changed = 0;
    
                        SDL_CondSignal(is->subpq_cond);
                        SDL_UnlockMutex(is->subpq_mutex);
                    } else {
                        if (is->subpq_size > 0) {
                            sp = &is->subpq[is->subpq_rindex];
    
                            if (is->subpq_size > 1)
                                sp2 = &is->subpq[(is->subpq_rindex + 1) % SUBPICTURE_QUEUE_SIZE];
                            else
                                sp2 = NULL;
    
                            if ((is->video_current_pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))
                                    || (sp2 && is->video_current_pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000))))
                            {
                                free_subpicture(sp);
    
                                /* update queue size and signal for next picture */
                                if (++is->subpq_rindex == SUBPICTURE_QUEUE_SIZE)
                                    is->subpq_rindex = 0;
    
                                SDL_LockMutex(is->subpq_mutex);
                                is->subpq_size--;
                                SDL_CondSignal(is->subpq_cond);
                                SDL_UnlockMutex(is->subpq_mutex);
                            }
                        }
                    }
                }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                /* display picture */
    
                if (!display_disable && is->show_mode == SHOW_MODE_VIDEO)
    
                    video_display(is);
    
                pictq_next_picture(is);
    
    
                if (is->step && !is->paused)
                    stream_toggle_pause(is);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (show_status) {
            static int64_t last_time;
            int64_t cur_time;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            cur_time = av_gettime();
    
            if (!last_time || (cur_time - last_time) >= 30000) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                aqsize = 0;
                vqsize = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                if (is->audio_st)
                    aqsize = is->audioq.size;
                if (is->video_st)
                    vqsize = is->videoq.size;
    
                if (is->subtitle_st)
                    sqsize = is->subtitleq.size;
    
                av_diff = 0;
                if (is->audio_st && is->video_st)
                    av_diff = get_audio_clock(is) - get_video_clock(is);
    
                printf("%7.2f A-V:%7.3f fd=%4d aq=%5dKB vq=%5dKB sq=%5dB f=%"PRId64"/%"PRId64"   \r",
    
                       get_master_clock(is),
                       av_diff,
    
                       is->frame_drops_early + is->frame_drops_late,
    
                       aqsize / 1024,
                       vqsize / 1024,
                       sqsize,
                       is->video_st ? is->video_st->codec->pts_correction_num_faulty_dts : 0,
                       is->video_st ? is->video_st->codec->pts_correction_num_faulty_pts : 0);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                fflush(stdout);
                last_time = cur_time;
            }
        }
    }
    
    /* allocate a picture (needs to do that in main thread to avoid
       potential locking problems */
    
    static void alloc_picture(VideoState *is)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        VideoPicture *vp;
    
        vp = &is->pictq[is->pictq_windex];
    
        if (vp->bmp)
            SDL_FreeYUVOverlay(vp->bmp);
    
    
    #if CONFIG_AVFILTER
    
        avfilter_unref_bufferp(&vp->picref);
    
        video_open(is, 0);
    
        vp->bmp = SDL_CreateYUVOverlay(vp->width, vp->height,
    
                                       SDL_YV12_OVERLAY,
    
        if (!vp->bmp || vp->bmp->pitches[0] < vp->width) {
            /* SDL allocates a buffer smaller than requested if the video
             * overlay hardware is unable to support the requested size. */
            fprintf(stderr, "Error: the video system does not support an image\n"
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
                            "size of %dx%d pixels. Try using -lowres or -vf \"scale=w:h\"\n"
    
                            "to reduce the image size.\n", vp->width, vp->height );
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        SDL_LockMutex(is->pictq_mutex);
        vp->allocated = 1;
        SDL_CondSignal(is->pictq_cond);
        SDL_UnlockMutex(is->pictq_mutex);
    }
    
    
    static void duplicate_right_border_pixels(SDL_Overlay *bmp) {
        int i, width, height;
        Uint8 *p, *maxp;
        for (i = 0; i < 3; i++) {
            width  = bmp->w;
            height = bmp->h;
            if (i > 0) {
                width  >>= 1;
                height >>= 1;
            }
            if (bmp->pitches[i] > width) {
                maxp = bmp->pixels[i] + bmp->pitches[i] * height - 1;
                for (p = bmp->pixels[i] + width - 1; p < maxp; p += bmp->pitches[i])
                    *(p+1) = *p;
            }
        }
    }
    
    
    static int queue_picture(VideoState *is, AVFrame *src_frame, double pts1, int64_t pos, int serial)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        VideoPicture *vp;
    
        double frame_delay, pts = pts1;
    
        /* compute the exact PTS for the picture if it is omitted in the stream
         * pts1 is the dts of the pkt / pts of the frame */
        if (pts != 0) {
            /* update video clock with pts, if present */
            is->video_clock = pts;
        } else {
            pts = is->video_clock;
        }
        /* update video clock for next frame */
        frame_delay = av_q2d(is->video_st->codec->time_base);
        /* for MPEG2, the frame can be repeated, so we update the
           clock accordingly */
        frame_delay += src_frame->repeat_pict * (frame_delay * 0.5);
        is->video_clock += frame_delay;
    
    #if defined(DEBUG_SYNC) && 0
        printf("frame_type=%c clock=%0.3f pts=%0.3f\n",
    
               av_get_picture_type_char(src_frame->pict_type), pts, pts1);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* wait until we have space to put a new picture */
        SDL_LockMutex(is->pictq_mutex);
    
        /* keep the last already displayed picture in the queue */
    
        while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE - 2 &&
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
               !is->videoq.abort_request) {
            SDL_CondWait(is->pictq_cond, is->pictq_mutex);
        }
        SDL_UnlockMutex(is->pictq_mutex);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (is->videoq.abort_request)
            return -1;
    
        vp = &is->pictq[is->pictq_windex];
    
    
    #if CONFIG_AVFILTER
        vp->sample_aspect_ratio = ((AVFilterBufferRef *)src_frame->opaque)->video->sample_aspect_ratio;
    #else
        vp->sample_aspect_ratio = av_guess_sample_aspect_ratio(is->ic, is->video_st, src_frame);
    #endif
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* alloc or resize hardware picture buffer */
    
        if (!vp->bmp || vp->reallocate || !vp->allocated ||
    
            vp->width  != src_frame->width ||
            vp->height != src_frame->height) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            SDL_Event event;
    
    
            vp->width = src_frame->width;
            vp->height = src_frame->height;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
            /* the allocation must be done in the main thread to avoid
    
               locking problems. */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            event.type = FF_ALLOC_EVENT;
    
            event.user.data1 = is;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            SDL_PushEvent(&event);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* wait until the picture is allocated */
            SDL_LockMutex(is->pictq_mutex);
            while (!vp->allocated && !is->videoq.abort_request) {
                SDL_CondWait(is->pictq_cond, is->pictq_mutex);
            }
    
            /* if the queue is aborted, we have to pop the pending ALLOC event or wait for the allocation to complete */
            if (is->videoq.abort_request && SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENTMASK(FF_ALLOC_EVENT)) != 1) {
                while (!vp->allocated) {
                    SDL_CondWait(is->pictq_cond, is->pictq_mutex);
                }
            }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            SDL_UnlockMutex(is->pictq_mutex);
    
            if (is->videoq.abort_request)
                return -1;
        }
    
    
        /* if the frame is not skipped, then display it */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (vp->bmp) {
    
            AVPicture pict = { { 0 } };
    
    #if CONFIG_AVFILTER
    
            avfilter_unref_bufferp(&vp->picref);
    
            vp->picref = src_frame->opaque;
    #endif
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* get a pointer on the bitmap */
            SDL_LockYUVOverlay (vp->bmp);
    
            pict.data[0] = vp->bmp->pixels[0];
            pict.data[1] = vp->bmp->pixels[2];
            pict.data[2] = vp->bmp->pixels[1];
    
            pict.linesize[0] = vp->bmp->pitches[0];
            pict.linesize[1] = vp->bmp->pitches[2];
            pict.linesize[2] = vp->bmp->pitches[1];
    
    
    #if CONFIG_AVFILTER
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            // FIXME use direct rendering
    
            av_picture_copy(&pict, (AVPicture *)src_frame,
    
                            src_frame->format, vp->width, vp->height);
    
            av_opt_get_int(sws_opts, "sws_flags", 0, &sws_flags);
    
            is->img_convert_ctx = sws_getCachedContext(is->img_convert_ctx,
    
                vp->width, vp->height, src_frame->format, vp->width, vp->height,
    
                AV_PIX_FMT_YUV420P, sws_flags, NULL, NULL, NULL);
    
            if (is->img_convert_ctx == NULL) {
    
                fprintf(stderr, "Cannot initialize the conversion context\n");
                exit(1);
            }
    
            sws_scale(is->img_convert_ctx, src_frame->data, src_frame->linesize,
    
                      0, vp->height, pict.data, pict.linesize);
    #endif
    
            /* workaround SDL PITCH_WORKAROUND */
            duplicate_right_border_pixels(vp->bmp);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* update the bitmap content */
            SDL_UnlockYUVOverlay(vp->bmp);
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
            /* now we can update the picture count */
            if (++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE)
                is->pictq_windex = 0;
            SDL_LockMutex(is->pictq_mutex);
            is->pictq_size++;
            SDL_UnlockMutex(is->pictq_mutex);
        }
    
    static int get_video_frame(VideoState *is, AVFrame *frame, int64_t *pts, AVPacket *pkt, int *serial)
    
        if (packet_queue_get(&is->videoq, pkt, 1, serial) < 0)
    
        if (pkt->data == flush_pkt.data) {
            avcodec_flush_buffers(is->video_st->codec);
    
            SDL_LockMutex(is->pictq_mutex);
    
            // Make sure there are no long delay timers (ideally we should just flush the queue but that's harder)
    
            while (is->pictq_size && !is->videoq.abort_request) {
                SDL_CondWait(is->pictq_cond, is->pictq_mutex);
    
            is->video_current_pos = -1;
            is->frame_last_pts = AV_NOPTS_VALUE;
    
            is->frame_timer = (double)av_gettime() / 1000000.0;
    
            is->frame_last_dropped_pts = AV_NOPTS_VALUE;
            SDL_UnlockMutex(is->pictq_mutex);
    
    
        if(avcodec_decode_video2(is->video_st->codec, frame, &got_picture, pkt) < 0)
    
            if (decoder_reorder_pts == -1) {
    
                *pts = av_frame_get_best_effort_timestamp(frame);
    
            } else if (decoder_reorder_pts) {
    
                *pts = frame->pkt_dts;
    
            }
    
            if (*pts == AV_NOPTS_VALUE) {
                *pts = 0;
    
            if (framedrop>0 || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) {
    
                SDL_LockMutex(is->pictq_mutex);
                if (is->frame_last_pts != AV_NOPTS_VALUE && *pts) {
                    double clockdiff = get_video_clock(is) - get_master_clock(is);
                    double dpts = av_q2d(is->video_st->time_base) * *pts;
                    double ptsdiff = dpts - is->frame_last_pts;
                    if (fabs(clockdiff) < AV_NOSYNC_THRESHOLD &&
                         ptsdiff > 0 && ptsdiff < AV_NOSYNC_THRESHOLD &&
    
                         clockdiff + ptsdiff - is->frame_last_filter_delay < 0) {
    
                        is->frame_last_dropped_pos = pkt->pos;
                        is->frame_last_dropped_pts = dpts;
    
                        is->frame_drops_early++;
    
                        ret = 0;
                    }
                }
                SDL_UnlockMutex(is->pictq_mutex);
            }
    
            return ret;
    
        return 0;
    }
    
    #if CONFIG_AVFILTER
    
    static int configure_filtergraph(AVFilterGraph *graph, const char *filtergraph,
                                     AVFilterContext *source_ctx, AVFilterContext *sink_ctx)
    {
        int ret;
        AVFilterInOut *outputs = NULL, *inputs = NULL;
    
        if (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       = NULL;
    
            inputs->name        = av_strdup("out");
            inputs->filter_ctx  = sink_ctx;
            inputs->pad_idx     = 0;
            inputs->next        = NULL;
    
            if ((ret = avfilter_graph_parse(graph, filtergraph, &inputs, &outputs, NULL)) < 0)
                goto fail;
        } else {
            if ((ret = avfilter_link(source_ctx, 0, sink_ctx, 0)) < 0)
                goto fail;
        }
    
    
        ret = avfilter_graph_config(graph, NULL);
    
    fail:
        avfilter_inout_free(&outputs);
        avfilter_inout_free(&inputs);
        return ret;
    }
    
    
    static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const char *vfilters, AVFrame *frame)
    
        static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };
    
    Stefano Sabatini's avatar
    Stefano Sabatini committed
        char sws_flags_str[128];
    
        char buffersrc_args[256];
    
        AVBufferSinkParams *buffersink_params = av_buffersink_params_alloc();
    
        AVFilterContext *filt_src = NULL, *filt_out = NULL, *filt_crop;
    
        AVCodecContext *codec = is->video_st->codec;
    
    
        if (!buffersink_params)
            return AVERROR(ENOMEM);
    
    
        av_opt_get_int(sws_opts, "sws_flags", 0, &sws_flags);
    
        snprintf(sws_flags_str, sizeof(sws_flags_str), "flags=%"PRId64, sws_flags);
    
    Stefano Sabatini's avatar
    Stefano Sabatini committed
        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,
    
                 codec->sample_aspect_ratio.num, FFMAX(codec->sample_aspect_ratio.den, 1));
    
    
        if ((ret = avfilter_graph_create_filter(&filt_src,
                                                avfilter_get_by_name("buffer"),
    
                                                "ffplay_buffer", buffersrc_args, NULL,
    
        buffersink_params->pixel_fmts = pix_fmts;
    
        ret = avfilter_graph_create_filter(&filt_out,
    
                                           avfilter_get_by_name("ffbuffersink"),
    
                                           "ffplay_buffersink", NULL, buffersink_params, graph);
    
        /* SDL YUV code is not handling odd width/height for some driver
         * combinations, therefore we crop the picture to an even width/height. */
        if ((ret = avfilter_graph_create_filter(&filt_crop,
                                                avfilter_get_by_name("crop"),
                                                "ffplay_crop", "floor(in_w/2)*2:floor(in_h/2)*2", NULL, graph)) < 0)
    
        if ((ret = avfilter_link(filt_crop, 0, filt_out, 0)) < 0)
    
        if ((ret = configure_filtergraph(graph, vfilters, filt_src, filt_crop)) < 0)
    
        is->in_video_filter  = filt_src;
    
        is->out_video_filter = filt_out;
    
    fail:
        av_freep(&buffersink_params);
    
        return ret;
    }
    
    #endif  /* CONFIG_AVFILTER */
    
    static int video_thread(void *arg)
    {
    
        AVPacket pkt = { 0 };
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        AVFrame *frame = avcodec_alloc_frame();
    
        int64_t pts_int = AV_NOPTS_VALUE, pos = -1;
    
        AVCodecContext *codec = is->video_st->codec;
    
        AVFilterGraph *graph = avfilter_graph_alloc();
    
        AVFilterContext *filt_out = NULL, *filt_in = NULL;
    
        enum AVPixelFormat last_format = -2;
    
        if (codec->codec->capabilities & CODEC_CAP_DR1) {
            is->use_dr1 = 1;
            codec->get_buffer     = codec_get_buffer;
            codec->release_buffer = codec_release_buffer;
            codec->opaque         = &is->buffer_pool;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        for (;;) {
    
    #endif
            while (is->paused && !is->videoq.abort_request)
                SDL_Delay(10);
    
            ret = get_video_frame(is, frame, &pts_int, &pkt, &serial);
    
    #if CONFIG_AVFILTER
    
            if (   last_w != frame->width
                || last_h != frame->height
                || last_format != frame->format) {
    
                av_log(NULL, AV_LOG_INFO, "Frame changed from size:%dx%d to size:%dx%d\n",
    
                       last_w, last_h, frame->width, frame->height);
    
                avfilter_graph_free(&graph);
                graph = avfilter_graph_alloc();
    
                if ((ret = configure_video_filters(graph, is, vfilters, frame)) < 0) {
    
                    SDL_Event event;
                    event.type = FF_QUIT_EVENT;
                    event.user.data1 = is;
                    SDL_PushEvent(&event);
    
                    av_free_packet(&pkt);
    
                last_w = frame->width;
                last_h = frame->height;
                last_format = frame->format;
    
            frame->sample_aspect_ratio = av_guess_sample_aspect_ratio(is->ic, is->video_st, frame);
    
            if (is->use_dr1 && frame->opaque) {
    
                FrameBuffer      *buf = frame->opaque;
                AVFilterBufferRef *fb = avfilter_get_video_buffer_ref_from_arrays(
                                            frame->data, frame->linesize,
                                            AV_PERM_READ | AV_PERM_PRESERVE,
                                            frame->width, frame->height,
                                            frame->format);
    
                avfilter_copy_frame_props(fb, frame);
                fb->buf->priv           = buf;
                fb->buf->free           = filter_release_buffer;
    
                buf->refcount++;
    
                av_buffersrc_add_ref(filt_in, fb, AV_BUFFERSRC_FLAG_NO_COPY);
    
    
            } else
                av_buffersrc_write_frame(filt_in, frame);
    
    
            av_free_packet(&pkt);
    
    
                is->frame_last_returned_time = av_gettime() / 1000000.0;
    
    
                ret = av_buffersink_get_buffer_ref(filt_out, &picref, 0);
    
                is->frame_last_filter_delay = av_gettime() / 1000000.0 - is->frame_last_returned_time;
                if (fabs(is->frame_last_filter_delay) > AV_NOSYNC_THRESHOLD / 10.0)
                    is->frame_last_filter_delay = 0;
    
    
                avfilter_copy_buf_props(frame, picref);
    
                tb      = filt_out->inputs[0]->time_base;
    
                pos     = picref->pos;
                frame->opaque = picref;
    
                if (av_cmp_q(tb, is->video_st->time_base)) {
                    av_unused int64_t pts1 = pts_int;
                    pts_int = av_rescale_q(pts_int, tb, is->video_st->time_base);
                    av_dlog(NULL, "video_thread(): "
                            "tb:%d/%d pts:%"PRId64" -> tb:%d/%d pts:%"PRId64"\n",
                            tb.num, tb.den, pts1,
                            is->video_st->time_base.num, is->video_st->time_base.den, pts_int);
                }
                pts = pts_int * av_q2d(is->video_st->time_base);
    
                ret = queue_picture(is, frame, pts, pos, serial);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            pts = pts_int * av_q2d(is->video_st->time_base);
    
            ret = queue_picture(is, frame, pts, pkt.pos, serial);
    
            if (ret < 0)
                goto the_end;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
     the_end:
    
        avcodec_flush_buffers(is->video_st->codec);
    
    #if CONFIG_AVFILTER
    
        avfilter_graph_free(&graph);
    
        av_free_packet(&pkt);
    
        avcodec_free_frame(&frame);
    
    static int subtitle_thread(void *arg)
    {
        VideoState *is = arg;
        SubPicture *sp;
        AVPacket pkt1, *pkt = &pkt1;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        for (;;) {
    
            while (is->paused && !is->subtitleq.abort_request) {
                SDL_Delay(10);
            }
    
            if (packet_queue_get(&is->subtitleq, pkt, 1, NULL) < 0)
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            if (pkt->data == flush_pkt.data) {
    
                avcodec_flush_buffers(is->subtitle_st->codec);
                continue;
            }
    
            SDL_LockMutex(is->subpq_mutex);
            while (is->subpq_size >= SUBPICTURE_QUEUE_SIZE &&
                   !is->subtitleq.abort_request) {
                SDL_CondWait(is->subpq_cond, is->subpq_mutex);
            }
            SDL_UnlockMutex(is->subpq_mutex);
    
            sp = &is->subpq[is->subpq_windex];
    
           /* NOTE: ipts is the PTS of the _first_ picture beginning in
               this packet, if any */
            pts = 0;
            if (pkt->pts != AV_NOPTS_VALUE)
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                pts = av_q2d(is->subtitle_st->time_base) * pkt->pts;
    
            avcodec_decode_subtitle2(is->subtitle_st->codec, &sp->sub,
                                     &got_subtitle, pkt);
    
            if (got_subtitle && sp->sub.format == 0) {
    
                if (sp->sub.pts != AV_NOPTS_VALUE)
                    pts = sp->sub.pts / (double)AV_TIME_BASE;
    
                for (i = 0; i < sp->sub.num_rects; i++)
                {
    
                    for (j = 0; j < sp->sub.rects[i]->nb_colors; j++)
    
                        RGBA_IN(r, g, b, a, (uint32_t*)sp->sub.rects[i]->pict.data[1] + j);
    
                        y = RGB_TO_Y_CCIR(r, g, b);
                        u = RGB_TO_U_CCIR(r, g, b, 0);
                        v = RGB_TO_V_CCIR(r, g, b, 0);
    
                        YUVA_OUT((uint32_t*)sp->sub.rects[i]->pict.data[1] + j, y, u, v, a);
    
                    }
                }
    
                /* now we can update the picture count */
                if (++is->subpq_windex == SUBPICTURE_QUEUE_SIZE)
                    is->subpq_windex = 0;
                SDL_LockMutex(is->subpq_mutex);
                is->subpq_size++;
                SDL_UnlockMutex(is->subpq_mutex);
            }
            av_free_packet(pkt);
        }