Skip to content
Snippets Groups Projects
ffplay.c 131 KiB
Newer Older
  • Learn to ignore specific revisions
  •             cb[0] = ALPHA_BLEND(a1 >> 2, cb[0], u1, 1);
                cr[0] = ALPHA_BLEND(a1 >> 2, cr[0], v1, 1);
                cb++;
                cr++;
                p += -wrap3 + BPP;
                lum += -wrap + 1;
            }
    
            p += wrap3 + (wrap3 - dstw * BPP);
            lum += wrap + (wrap - dstw - dstx);
    
            cb += dst->linesize[1] - width2 - skip2;
            cr += dst->linesize[2] - width2 - skip2;
        }
        /* handle odd height */
        if (h) {
    
                YUVA_IN(y, u, v, a, p, pal);
                lum[0] = ALPHA_BLEND(a, lum[0], y, 0);
                cb[0] = ALPHA_BLEND(a >> 2, cb[0], u, 0);
                cr[0] = ALPHA_BLEND(a >> 2, cr[0], v, 0);
                cb++;
                cr++;
                lum++;
                p += BPP;
            }
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            for (w = dstw - (dstx & 1); w >= 2; w -= 2) {
    
                YUVA_IN(y, u, v, a, p, pal);
                u1 = u;
                v1 = v;
                a1 = a;
                lum[0] = ALPHA_BLEND(a, lum[0], y, 0);
    
                YUVA_IN(y, u, v, a, p + BPP, pal);
                u1 += u;
                v1 += v;
                a1 += a;
                lum[1] = ALPHA_BLEND(a, lum[1], y, 0);
                cb[0] = ALPHA_BLEND(a1 >> 2, cb[0], u, 1);
                cr[0] = ALPHA_BLEND(a1 >> 2, cr[0], v, 1);
                cb++;
                cr++;
                p += 2 * BPP;
                lum += 2;
            }
            if (w) {
                YUVA_IN(y, u, v, a, p, pal);
                lum[0] = ALPHA_BLEND(a, lum[0], y, 0);
                cb[0] = ALPHA_BLEND(a >> 2, cb[0], u, 0);
                cr[0] = ALPHA_BLEND(a >> 2, cr[0], v, 0);
            }
        }
    }
    
    
    static void free_picture(Frame *vp)
    
    {
         if (vp->bmp) {
             SDL_FreeYUVOverlay(vp->bmp);
             vp->bmp = NULL;
         }
    }
    
    
    static void calculate_display_rect(SDL_Rect *rect,
                                       int scr_xleft, int scr_ytop, int scr_width, int scr_height,
                                       int pic_width, int pic_height, AVRational pic_sar)
    
    {
        float aspect_ratio;
        int width, height, x, y;
    
    
    
        if (aspect_ratio <= 0.0)
            aspect_ratio = 1.0;
    
        aspect_ratio *= (float)pic_width / (float)pic_height;
    
    
        /* XXX: we suppose the screen has a 1.0 pixel ratio */
        height = scr_height;
        width = ((int)rint(height * aspect_ratio)) & ~1;
        if (width > scr_width) {
            width = scr_width;
            height = ((int)rint(width / aspect_ratio)) & ~1;
        }
        x = (scr_width - width) / 2;
        y = (scr_height - height) / 2;
        rect->x = scr_xleft + x;
        rect->y = scr_ytop  + y;
        rect->w = FFMAX(width,  1);
        rect->h = FFMAX(height, 1);
    }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    static void video_image_display(VideoState *is)
    {
    
        Frame *vp;
        Frame *sp;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        SDL_Rect rect;
    
        vp = frame_queue_peek(&is->pictq);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (vp->bmp) {
    
                if (frame_queue_nb_remaining(&is->subpq) > 0) {
                    sp = frame_queue_peek(&is->subpq);
    
                    if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) {
    
                        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];
    
                        for (i = 0; i < sp->sub.num_rects; i++)
    
                            blend_subrect(&pict, sp->sub.rects[i],
    
            calculate_display_rect(&rect, is->xleft, is->ytop, is->width, is->height, vp->width, vp->height, vp->sar);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            SDL_DisplayYUVOverlay(vp->bmp, &rect);
    
    
            if (rect.x != is->last_display_rect.x || rect.y != is->last_display_rect.y || rect.w != is->last_display_rect.w || rect.h != is->last_display_rect.h || is->force_refresh) {
                int bgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0x00);
                fill_border(is->xleft, is->ytop, is->width, is->height, rect.x, rect.y, rect.w, rect.h, bgcolor, 1);
                is->last_display_rect = rect;
            }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    }
    
    static inline int compute_mod(int a, int b)
    {
    
        return a < 0 ? a%b + b : a%b;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    }
    
    static void video_audio_display(VideoState *s)
    {
        int i, i_start, x, y1, y, ys, delay, n, nb_display_channels;
        int ch, channels, h, h2, bgcolor, fgcolor;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        for (rdft_bits = 1; (1 << rdft_bits) < 2 * s->height; rdft_bits++)
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        nb_freq = 1 << (rdft_bits - 1);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* compute display index : center on currently output samples */
    
        channels = s->audio_tgt.channels;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        nb_display_channels = channels;
    
        if (!s->paused) {
    
            int data_used= s->show_mode == SHOW_MODE_WAVES ? s->width : (2*nb_freq);
    
            n = 2 * channels;
    
            delay = s->audio_write_buf_size;
    
            /* to be more precise, we take into account the time spent since
               the last buffer computation */
            if (audio_callback_time) {
    
                time_diff = av_gettime_relative() - audio_callback_time;
    
                delay -= (time_diff * s->audio_tgt.freq) / 1000000;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            delay += 2 * data_used;
    
            if (delay < data_used)
                delay = data_used;
    
    
            i_start= x = compute_mod(s->sample_array_index - delay * channels, SAMPLE_ARRAY_SIZE);
    
            if (s->show_mode == SHOW_MODE_WAVES) {
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                h = INT_MIN;
                for (i = 0; i < 1000; i += channels) {
                    int idx = (SAMPLE_ARRAY_SIZE + x - i) % SAMPLE_ARRAY_SIZE;
                    int a = s->sample_array[idx];
                    int b = s->sample_array[(idx + 4 * channels) % SAMPLE_ARRAY_SIZE];
                    int c = s->sample_array[(idx + 5 * channels) % SAMPLE_ARRAY_SIZE];
                    int d = s->sample_array[(idx + 9 * channels) % SAMPLE_ARRAY_SIZE];
                    int score = a - d;
                    if (h < score && (b ^ c) < 0) {
                        h = score;
                        i_start = idx;
    
            s->last_i_start = i_start;
        } else {
            i_start = s->last_i_start;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
        bgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0x00);
    
        if (s->show_mode == SHOW_MODE_WAVES) {
    
            fill_rectangle(screen,
                           s->xleft, s->ytop, s->width, s->height,
    
    
            fgcolor = SDL_MapRGB(screen->format, 0xff, 0xff, 0xff);
    
            /* total height for one channel */
            h = s->height / nb_display_channels;
            /* graph height / 2 */
            h2 = (h * 9) / 20;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            for (ch = 0; ch < nb_display_channels; ch++) {
    
                i = i_start + ch;
                y1 = s->ytop + ch * h + (h / 2); /* position of center line */
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                for (x = 0; x < s->width; x++) {
    
                    y = (s->sample_array[i] * h2) >> 15;
                    if (y < 0) {
                        y = -y;
                        ys = y1 - y;
                    } else {
                        ys = y1;
                    }
                    fill_rectangle(screen,
                                   s->xleft + x, ys, 1, y,
    
                    i += channels;
                    if (i >= SAMPLE_ARRAY_SIZE)
                        i -= SAMPLE_ARRAY_SIZE;
    
            fgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0xff);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            for (ch = 1; ch < nb_display_channels; ch++) {
    
                y = s->ytop + ch * h;
                fill_rectangle(screen,
                               s->xleft, y, s->width, 1,
    
            }
            SDL_UpdateRect(screen, s->xleft, s->ytop, s->width, s->height);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        } else {
    
            nb_display_channels= FFMIN(nb_display_channels, 2);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            if (rdft_bits != s->rdft_bits) {
    
                av_rdft_end(s->rdft);
    
    Måns Rullgård's avatar
    Måns Rullgård committed
                av_free(s->rdft_data);
    
                s->rdft = av_rdft_init(rdft_bits, DFT_R2C);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                s->rdft_bits = rdft_bits;
    
                s->rdft_data = av_malloc_array(nb_freq, 4 *sizeof(*s->rdft_data));
    
            if (!s->rdft || !s->rdft_data){
                av_log(NULL, AV_LOG_ERROR, "Failed to allocate buffers for RDFT, switching to waves display\n");
                s->show_mode = SHOW_MODE_WAVES;
            } else {
    
    Måns Rullgård's avatar
    Måns Rullgård committed
                FFTSample *data[2];
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                for (ch = 0; ch < nb_display_channels; ch++) {
                    data[ch] = s->rdft_data + 2 * nb_freq * ch;
    
                    i = i_start + ch;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                    for (x = 0; x < 2 * nb_freq; x++) {
                        double w = (x-nb_freq) * (1.0 / nb_freq);
                        data[ch][x] = s->sample_array[i] * (1.0 - w * w);
    
                        i += channels;
                        if (i >= SAMPLE_ARRAY_SIZE)
                            i -= SAMPLE_ARRAY_SIZE;
                    }
    
                    av_rdft_calc(s->rdft, data[ch]);
    
    Diego Biurrun's avatar
    Diego Biurrun committed
                /* Least efficient way to do this, we should of course
                 * directly access it but it is more than fast enough. */
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                for (y = 0; y < s->height; y++) {
                    double w = 1 / sqrt(nb_freq);
                    int a = sqrt(w * sqrt(data[0][2 * y + 0] * data[0][2 * y + 0] + data[0][2 * y + 1] * data[0][2 * y + 1]));
                    int b = (nb_display_channels == 2 ) ? sqrt(w * sqrt(data[1][2 * y + 0] * data[1][2 * y + 0]
                           + data[1][2 * y + 1] * data[1][2 * y + 1])) : a;
                    a = FFMIN(a, 255);
                    b = FFMIN(b, 255);
                    fgcolor = SDL_MapRGB(screen->format, a, b, (a + b) / 2);
    
    
                    fill_rectangle(screen,
                                s->xpos, s->height-y, 1, 1,
    
                }
            }
            SDL_UpdateRect(screen, s->xpos, s->ytop, 1, s->height);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            if (s->xpos >= s->width)
    
                s->xpos= s->xleft;
        }
    
    static void stream_close(VideoState *is)
    {
        /* XXX: use a special url_shutdown call to abort parse cleanly */
        is->abort_request = 1;
        SDL_WaitThread(is->read_tid, NULL);
    
        packet_queue_destroy(&is->videoq);
        packet_queue_destroy(&is->audioq);
        packet_queue_destroy(&is->subtitleq);
    
    
        /* free all pictures */
    
        frame_queue_destory(&is->pictq);
    
        frame_queue_destory(&is->sampq);
    
        frame_queue_destory(&is->subpq);
    
        SDL_DestroyCond(is->continue_read_thread);
    
    #if !CONFIG_AVFILTER
    
        sws_freeContext(is->img_convert_ctx);
    
    static void do_exit(VideoState *is)
    
        if (is) {
            stream_close(is);
    
        uninit_opts();
    #if CONFIG_AVFILTER
    
        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 void set_default_window_size(int width, int height, AVRational sar)
    
        calculate_display_rect(&rect, 0, 0, INT_MAX, height, width, height, sar);
    
        default_width  = rect.w;
        default_height = rect.h;
    }
    
    
    static int video_open(VideoState *is, int force_set_video_mode, Frame *vp)
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
    {
        int flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        if (is_full_screen) flags |= SDL_FULLSCREEN;
        else                flags |= SDL_RESIZABLE;
    
            set_default_window_size(vp->width, vp->height, vp->sar);
    
        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) {
    
            w = default_width;
            h = default_height;
    
        w = FFMIN(16383, w);
    
    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) {
    
            av_log(NULL, AV_LOG_FATAL, "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)
    
            video_open(is, 0, NULL);
    
        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 double get_clock(Clock *c)
    
        if (*c->queue_serial != c->serial)
    
        if (c->paused) {
            return c->pts;
    
            double time = av_gettime_relative() / 1000000.0;
    
            return c->pts_drift + time - (time - c->last_updated) * (1.0 - c->speed);
    
    static 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;
    
    static void set_clock(Clock *c, double pts, int serial)
    
        double time = av_gettime_relative() / 1000000.0;
    
        set_clock_at(c, pts, serial, time);
    }
    
    static void set_clock_speed(Clock *c, double speed)
    {
        set_clock(c, get_clock(c), c->serial);
        c->speed = speed;
    }
    
    static 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);
    }
    
    static 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);
    
    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_clock(&is->vidclk);
    
                break;
            case AV_SYNC_AUDIO_MASTER:
    
                val = get_clock(&is->audclk);
    
                val = get_clock(&is->extclk);
    
    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) {
    
           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 > MIN_FRAMES * 2) &&
                  (is->audio_stream < 0 || is->audioq.nb_packets > MIN_FRAMES * 2)) {
    
           set_clock_speed(&is->extclk, FFMIN(EXTERNAL_CLOCK_SPEED_MAX, is->extclk.speed + EXTERNAL_CLOCK_SPEED_STEP));
    
           double speed = is->extclk.speed;
    
               set_clock_speed(&is->extclk, 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;
    
            SDL_CondSignal(is->continue_read_thread);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    }
    
    /* pause or resume the video */
    
    static void stream_toggle_pause(VideoState *is)
    
            is->frame_timer += av_gettime_relative() / 1000000.0 - is->vidclk.last_updated;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            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 = !is->paused;
    
    static void toggle_pause(VideoState *is)
    {
        stream_toggle_pause(is);
        is->step = 0;
    }
    
    static void step_to_next_frame(VideoState *is)
    {
        /* if the stream is paused unpause it, then step */
        if (is->paused)
            stream_toggle_pause(is);
        is->step = 1;
    }
    
    
    static 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) {
    
                    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(NULL, AV_LOG_TRACE, "video: delay=%0.3f A-V=%f\n",
    
    static 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;
        } else {
            return 0.0;
        }
    }
    
    
    static 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);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    /* called to display each frame */
    
    static void video_refresh(void *opaque, double *remaining_time)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        VideoState *is = opaque;
    
        double time;
    
        Frame *sp, *sp2;
    
        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) {
    
            time = av_gettime_relative() / 1000000.0;
    
            if (is->force_refresh || is->last_vis_time + rdftspeed < time) {
                video_display(is);
                is->last_vis_time = time;
            }
            *remaining_time = FFMIN(*remaining_time, is->last_vis_time + rdftspeed - time);
        }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (is->video_st) {
    
                redisplay = frame_queue_prev(&is->pictq);
    
    retry:
    
            if (frame_queue_nb_remaining(&is->pictq) == 0) {
    
                // nothing to do, no picture to display in the queue
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else {
    
                double last_duration, duration, delay;
    
                Frame *vp, *lastvp;
    
                lastvp = frame_queue_peek_last(&is->pictq);
                vp = frame_queue_peek(&is->pictq);
    
                if (vp->serial != is->videoq.serial) {
    
                    frame_queue_next(&is->pictq);
    
                if (lastvp->serial != vp->serial && !redisplay)
    
                    is->frame_timer = av_gettime_relative() / 1000000.0;
    
                /* compute nominal last_duration */
    
                last_duration = vp_duration(is, lastvp, vp);
    
                    delay = compute_target_delay(last_duration, is);
    
                time= av_gettime_relative()/1000000.0;
    
                if (time < is->frame_timer + delay && !redisplay) {
    
                    *remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);
    
                    return;
    
                is->frame_timer += delay;
                if (delay > 0 && time - is->frame_timer > AV_SYNC_THRESHOLD_MAX)
                    is->frame_timer = time;
    
                SDL_LockMutex(is->pictq.mutex);
    
                if (!redisplay && !isnan(vp->pts))
    
                    update_video_pts(is, vp->pts, vp->pos, vp->serial);
    
                SDL_UnlockMutex(is->pictq.mutex);
    
                if (frame_queue_nb_remaining(&is->pictq) > 1) {
                    Frame *nextvp = frame_queue_peek_next(&is->pictq);
    
                    duration = vp_duration(is, vp, nextvp);
    
                    if(!is->step && (redisplay || framedrop>0 || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) && time > is->frame_timer + duration){
                        if (!redisplay)
                            is->frame_drops_late++;
    
                        frame_queue_next(&is->pictq);
    
                        goto retry;
                    }
                }
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                if (is->subtitle_st) {
    
                        while (frame_queue_nb_remaining(&is->subpq) > 0) {
                            sp = frame_queue_peek(&is->subpq);
    
                            if (frame_queue_nb_remaining(&is->subpq) > 1)
                                sp2 = frame_queue_peek_next(&is->subpq);
    
                            if (sp->serial != is->subtitleq.serial
                                    || (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))
    
                                    || (sp2 && is->vidclk.pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000))))
    
                                frame_queue_next(&is->subpq);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                /* display picture */
    
                if (!display_disable && is->show_mode == SHOW_MODE_VIDEO)
    
                    video_display(is);
    
                frame_queue_next(&is->pictq);
    
    
                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;
    
            cur_time = av_gettime_relative();
    
            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_clock(&is->audclk) - get_clock(&is->vidclk);
    
                else if (is->video_st)
                    av_diff = get_master_clock(is) - get_clock(&is->vidclk);
                else if (is->audio_st)
                    av_diff = get_master_clock(is) - get_clock(&is->audclk);
    
                av_log(NULL, AV_LOG_INFO,
                       "%7.2f %s:%7.3f fd=%4d aq=%5dKB vq=%5dKB sq=%5dB f=%"PRId64"/%"PRId64"   \r",
    
                       get_master_clock(is),
    
                       (is->audio_st && is->video_st) ? "A-V" : (is->video_st ? "M-V" : (is->audio_st ? "M-A" : "   ")),
    
                       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)
    
        vp = &is->pictq.queue[is->pictq.windex];
    
        free_picture(vp);
    
        video_open(is, 0, vp);
    
        vp->bmp = SDL_CreateYUVOverlay(vp->width, vp->height,
    
                                       SDL_YV12_OVERLAY,
    
        bufferdiff = vp->bmp ? FFMAX(vp->bmp->pixels[0], vp->bmp->pixels[1]) - FFMIN(vp->bmp->pixels[0], vp->bmp->pixels[1]) : 0;
    
        if (!vp->bmp || vp->bmp->pitches[0] < vp->width || bufferdiff < (int64_t)vp->height * vp->bmp->pitches[0]) {
    
            /* SDL allocates a buffer smaller than requested if the video
             * overlay hardware is unable to support the requested size. */
    
            av_log(NULL, AV_LOG_FATAL,
                   "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 );
    
        SDL_LockMutex(is->pictq.mutex);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        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 pts, double duration, int64_t pos, int serial)
    
        printf("frame_type=%c pts=%0.3f\n",
               av_get_picture_type_char(src_frame->pict_type), pts);
    
        if (!(vp = frame_queue_peek_writable(&is->pictq)))
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            return -1;
    
    
        vp->sar = src_frame->sample_aspect_ratio;
    
    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);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            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 && !is->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;
        }
    
    
        /* if the frame is not skipped, then display it */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (vp->bmp) {
    
            AVPicture pict = { { 0 } };
    
    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);
    
                av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
    
            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 */
    
            frame_queue_push(&is->pictq);
    
    static int get_video_frame(VideoState *is, AVFrame *frame)
    
        if ((got_picture = decoder_decode_frame(&is->viddec, frame, NULL)) < 0)
    
        if (got_picture) {
    
            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->frame_drops_early++;
    
                        av_frame_unref(frame);
    
    static int configure_filtergraph(AVFilterGraph *graph, const char *filtergraph,
                                     AVFilterContext *source_ctx, AVFilterContext *sink_ctx)
    {
    
        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_ptr(graph, filtergraph, &inputs, &outputs, NULL)) < 0)
    
                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 (i = 0; i < graph->nb_filters - nb_filters; i++)
            FFSWAP(AVFilterContext*, graph->filters[i], graph->filters[i + nb_filters]);
    
    
        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];
    
        AVFilterContext *filt_src = NULL, *filt_out = NULL, *last_filter = NULL;
    
        AVCodecContext *codec = is->video_st->codec;
    
        AVRational fr = av_guess_frame_rate(is->ic, is->video_st, NULL);
    
        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 (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, NULL,
    
        ret = avfilter_graph_create_filter(&filt_out,
    
                                           avfilter_get_by_name("buffersink"),
    
                                           "ffplay_buffersink", NULL, NULL, graph);
    
        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;
    
    /* 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;                                              \
                                                                                \