Skip to content
Snippets Groups Projects
ffplay.c 121 KiB
Newer Older
  • Learn to ignore specific revisions
  • Fabrice Bellard's avatar
    Fabrice Bellard committed
    /*
     * Copyright (c) 2003 Fabrice Bellard
     *
    
     * This file is part of FFmpeg.
     *
     * FFmpeg is free software; you can redistribute it and/or
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     * modify it under the terms of the GNU Lesser General Public
     * License as published by the Free Software Foundation; either
    
     * version 2.1 of the License, or (at your option) any later version.
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     *
    
     * FFmpeg is distributed in the hope that it will be useful,
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     * Lesser General Public License for more details.
     *
     * You should have received a copy of the GNU Lesser General Public
    
     * License along with FFmpeg; if not, write to the Free Software
    
     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     */
    
    /**
     * @file
     * simple media player based on the FFmpeg libraries
     */
    
    
    #include "config.h"
    
    #include <inttypes.h>
    
    #include <math.h>
    #include <limits.h>
    
    #include <signal.h>
    
    #include "libavutil/avstring.h"
    
    #include "libavutil/colorspace.h"
    
    #include "libavutil/mathematics.h"
    
    #include "libavutil/imgutils.h"
    
    #include "libavutil/dict.h"
    
    #include "libavutil/parseutils.h"
    #include "libavutil/samplefmt.h"
    
    #include "libavutil/time.h"
    
    #include "libavformat/avformat.h"
    #include "libavdevice/avdevice.h"
    #include "libswscale/swscale.h"
    
    #include "libavutil/opt.h"
    
    #include "libavcodec/avfft.h"
    
    #include "libswresample/swresample.h"
    
    #if CONFIG_AVFILTER
    
    # include "libavfilter/avcodec.h"
    
    # include "libavfilter/avfilter.h"
    
    # include "libavfilter/buffersrc.h"
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    #include <SDL.h>
    #include <SDL_thread.h>
    
    
    #include <assert.h>
    
    
    const char program_name[] = "ffplay";
    
    const int program_birth_year = 2003;
    
    #define MAX_QUEUE_SIZE (15 * 1024 * 1024)
    #define MIN_FRAMES 5
    
    /* SDL audio buffer size, in samples. Should be small to have precise
       A/V sync as SDL does not have hardware buffer fullness info. */
    #define SDL_AUDIO_BUFFER_SIZE 1024
    
    /* no AV sync correction is done if below the AV sync threshold */
    
    #define AV_SYNC_THRESHOLD 0.01
    
    /* no AV correction is done if too big error */
    #define AV_NOSYNC_THRESHOLD 10.0
    
    /* maximum audio speed change to get correct sync */
    #define SAMPLE_CORRECTION_PERCENT_MAX 10
    
    
    /* external clock speed adjustment constants for realtime sources based on buffer fullness */
    #define EXTERNAL_CLOCK_SPEED_MIN  0.900
    #define EXTERNAL_CLOCK_SPEED_MAX  1.010
    #define EXTERNAL_CLOCK_SPEED_STEP 0.001
    
    
    /* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */
    #define AUDIO_DIFF_AVG_NB   20
    
    
    /* polls for possible required screen refresh at least this often, should be less than 1/fps */
    #define REFRESH_RATE 0.01
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    /* NOTE: the size must be big enough to compensate the hardware audio buffersize size */
    
    /* TODO: We assume that a decoded and resampled frame fits into this buffer */
    #define SAMPLE_ARRAY_SIZE (8 * 65536)
    
    #define CURSOR_HIDE_DELAY 1000000
    
    
    static int64_t sws_flags = SWS_BICUBIC;
    
    typedef struct MyAVPacketList {
        AVPacket pkt;
        struct MyAVPacketList *next;
        int serial;
    } MyAVPacketList;
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    typedef struct PacketQueue {
    
        MyAVPacketList *first_pkt, *last_pkt;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int nb_packets;
        int size;
        int abort_request;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        SDL_mutex *mutex;
        SDL_cond *cond;
    } PacketQueue;
    
    
    #define VIDEO_PICTURE_QUEUE_SIZE 3
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    typedef struct VideoPicture {
    
        double pts;             // presentation timestamp for this picture
        int64_t pos;            // byte position in file
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        SDL_Overlay *bmp;
        int width, height; /* source height & width */
        int allocated;
    
        AVRational sar;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    } VideoPicture;
    
    
    typedef struct SubPicture {
        double pts; /* presentation time stamp for this picture */
        AVSubtitle sub;
    } SubPicture;
    
    
    typedef struct AudioParams {
        int freq;
        int channels;
    
        enum AVSampleFormat fmt;
    } AudioParams;
    
    
    typedef struct Clock {
        double pts;           /* clock base */
        double pts_drift;     /* clock base minus time at which we updated the clock */
        double last_updated;
        double speed;
        int serial;           /* clock is based on a packet with this serial */
        int paused;
        int *queue_serial;    /* pointer to the current packet queue serial, used for obsolete clock detection */
    } Clock;
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    enum {
        AV_SYNC_AUDIO_MASTER, /* default choice */
        AV_SYNC_VIDEO_MASTER,
    
        AV_SYNC_EXTERNAL_CLOCK, /* synchronize to an external clock */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    };
    
    typedef struct VideoState {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        SDL_Thread *video_tid;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int no_background;
        int abort_request;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int paused;
    
        int queue_attachments_req;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int seek_req;
    
        int seek_flags;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int64_t seek_pos;
    
        int64_t seek_rel;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        AVFormatContext *ic;
    
        Clock audclk;
        Clock vidclk;
        Clock extclk;
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int audio_stream;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int av_sync_type;
    
        int audio_clock_serial;
    
        double audio_diff_cum; /* used for AV difference average computation */
        double audio_diff_avg_coef;
        double audio_diff_threshold;
        int audio_diff_avg_count;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        AVStream *audio_st;
        PacketQueue audioq;
        int audio_hw_buf_size;
    
        uint8_t silence_buf[SDL_AUDIO_BUFFER_SIZE];
    
        uint8_t *audio_buf;
    
        uint8_t *audio_buf1;
    
        unsigned int audio_buf_size; /* in bytes */
    
        unsigned int audio_buf1_size;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int audio_buf_index; /* in bytes */
    
        int audio_write_buf_size;
    
        AVPacket audio_pkt_temp;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        AVPacket audio_pkt;
    
    Marton Balint's avatar
    Marton Balint committed
        int audio_last_serial;
    
        struct AudioParams audio_src;
    
    Marton Balint's avatar
    Marton Balint committed
    #if CONFIG_AVFILTER
        struct AudioParams audio_filter_src;
    #endif
    
        struct AudioParams audio_tgt;
    
        struct SwrContext *swr_ctx;
    
        double audio_current_pts;
        double audio_current_pts_drift;
    
        int frame_drops_early;
        int frame_drops_late;
    
        AVFrame *frame;
    
        enum ShowMode {
    
            SHOW_MODE_NONE = -1, SHOW_MODE_VIDEO = 0, SHOW_MODE_WAVES, SHOW_MODE_RDFT, SHOW_MODE_NB
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int16_t sample_array[SAMPLE_ARRAY_SIZE];
        int sample_array_index;
    
        int last_i_start;
    
        RDFTContext *rdft;
    
        int rdft_bits;
    
    Måns Rullgård's avatar
    Måns Rullgård committed
        FFTSample *rdft_data;
    
        SDL_Thread *subtitle_tid;
        int subtitle_stream;
        int subtitle_stream_changed;
        AVStream *subtitle_st;
        PacketQueue subtitleq;
        SubPicture subpq[SUBPICTURE_QUEUE_SIZE];
        int subpq_size, subpq_rindex, subpq_windex;
        SDL_mutex *subpq_mutex;
        SDL_cond *subpq_cond;
    
        double frame_last_dropped_pts;
    
        double frame_last_returned_time;
        double frame_last_filter_delay;
    
        int64_t frame_last_dropped_pos;
    
        int frame_last_dropped_serial;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int video_stream;
        AVStream *video_st;
        PacketQueue videoq;
    
        double video_current_pts;       // current displayed pts
    
        double video_current_pts_drift; // video_current_pts - time (av_gettime) at which we updated video_current_pts - used to have running video pts
        int64_t video_current_pos;      // current displayed file pos
    
        double max_frame_duration;      // maximum duration of a frame - above this, we consider the jump a timestamp discontinuity
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE];
        int pictq_size, pictq_rindex, pictq_windex;
        SDL_mutex *pictq_mutex;
        SDL_cond *pictq_cond;
    
    #if !CONFIG_AVFILTER
    
        struct SwsContext *img_convert_ctx;
    
        SDL_Rect last_display_rect;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        char filename[1024];
        int width, height, xleft, ytop;
    
    #if CONFIG_AVFILTER
    
        AVFilterContext *in_video_filter;   // the first filter in the video chain
        AVFilterContext *out_video_filter;  // the last filter in the video chain
    
    Marton Balint's avatar
    Marton Balint committed
        AVFilterContext *in_audio_filter;   // the first filter in the audio chain
        AVFilterContext *out_audio_filter;  // the last filter in the audio chain
        AVFilterGraph *agraph;              // audio filter graph
    
        int last_video_stream, last_audio_stream, last_subtitle_stream;
    
    
        SDL_cond *continue_read_thread;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    } VideoState;
    
    /* options specified by the user */
    static AVInputFormat *file_iformat;
    static const char *input_filename;
    
    static const char *window_title;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    static int fs_screen_width;
    static int fs_screen_height;
    
    static int default_width  = 640;
    static int default_height = 480;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
    static int screen_width  = 0;
    
    static int screen_height = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    static int audio_disable;
    static int video_disable;
    
    static int subtitle_disable;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
    static int wanted_stream[AVMEDIA_TYPE_NB] = {
        [AVMEDIA_TYPE_AUDIO]    = -1,
        [AVMEDIA_TYPE_VIDEO]    = -1,
        [AVMEDIA_TYPE_SUBTITLE] = -1,
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
    static int seek_by_bytes = -1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    static int display_disable;
    
    static int av_sync_type = AV_SYNC_AUDIO_MASTER;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    static int64_t start_time = AV_NOPTS_VALUE;
    
    Robert Krüger's avatar
    Robert Krüger committed
    static int64_t duration = AV_NOPTS_VALUE;
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
    static int workaround_bugs = 1;
    
    static int fast = 0;
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
    static int lowres = 0;
    
    static int idct = FF_IDCT_AUTO;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
    static int decoder_reorder_pts = -1;
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
    static int autoexit;
    
    static int exit_on_keydown;
    static int exit_on_mousedown;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
    static int loop = 1;
    
    static int framedrop = -1;
    
    static int infinite_buffer = -1;
    
    static enum ShowMode show_mode = SHOW_MODE_NONE;
    
    static const char *audio_codec_name;
    static const char *subtitle_codec_name;
    static const char *video_codec_name;
    
    static int64_t cursor_last_shown;
    static int cursor_hidden = 0;
    
    #if CONFIG_AVFILTER
    static char *vfilters = NULL;
    
    Marton Balint's avatar
    Marton Balint committed
    static char *afilters = NULL;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    /* current context */
    static int is_full_screen;
    
    static int64_t audio_callback_time;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    #define FF_ALLOC_EVENT   (SDL_USEREVENT)
    
    #define FF_QUIT_EVENT    (SDL_USEREVENT + 2)
    
    Marton Balint's avatar
    Marton Balint committed
    static inline
    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;
    }
    
    static inline
    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;
    }
    
    
    static int packet_queue_put(PacketQueue *q, AVPacket *pkt);
    
    static int packet_queue_put_private(PacketQueue *q, AVPacket *pkt)
    
        if (q->abort_request)
           return -1;
    
        pkt1 = av_malloc(sizeof(MyAVPacketList));
    
        if (!pkt1)
            return -1;
        pkt1->pkt = *pkt;
        pkt1->next = NULL;
    
        if (pkt == &flush_pkt)
            q->serial++;
        pkt1->serial = q->serial;
    
    
        if (!q->last_pkt)
            q->first_pkt = pkt1;
        else
            q->last_pkt->next = pkt1;
        q->last_pkt = pkt1;
        q->nb_packets++;
        q->size += pkt1->pkt.size + sizeof(*pkt1);
        /* XXX: should duplicate packet data in DV case */
        SDL_CondSignal(q->cond);
    
        return 0;
    }
    
    static int packet_queue_put(PacketQueue *q, AVPacket *pkt)
    {
        int ret;
    
        /* duplicate the packet */
        if (pkt != &flush_pkt && av_dup_packet(pkt) < 0)
            return -1;
    
        SDL_LockMutex(q->mutex);
        ret = packet_queue_put_private(q, pkt);
    
        SDL_UnlockMutex(q->mutex);
    
    
        if (pkt != &flush_pkt && ret < 0)
            av_free_packet(pkt);
    
        return ret;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    /* packet queue handling */
    static void packet_queue_init(PacketQueue *q)
    {
        memset(q, 0, sizeof(PacketQueue));
        q->mutex = SDL_CreateMutex();
        q->cond = SDL_CreateCond();
    
        q->abort_request = 1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    static void packet_queue_flush(PacketQueue *q)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
    
        MyAVPacketList *pkt, *pkt1;
    
        SDL_LockMutex(q->mutex);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        for (pkt = q->first_pkt; pkt != NULL; pkt = pkt1) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            pkt1 = pkt->next;
            av_free_packet(&pkt->pkt);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        q->last_pkt = NULL;
        q->first_pkt = NULL;
        q->nb_packets = 0;
        q->size = 0;
    
        SDL_UnlockMutex(q->mutex);
    
    static void packet_queue_destroy(PacketQueue *q)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        packet_queue_flush(q);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        SDL_DestroyMutex(q->mutex);
        SDL_DestroyCond(q->cond);
    }
    
    static void packet_queue_abort(PacketQueue *q)
    {
        SDL_LockMutex(q->mutex);
    
        q->abort_request = 1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        SDL_CondSignal(q->cond);
    
        SDL_UnlockMutex(q->mutex);
    }
    
    
    static void packet_queue_start(PacketQueue *q)
    {
        SDL_LockMutex(q->mutex);
        q->abort_request = 0;
        packet_queue_put_private(q, &flush_pkt);
        SDL_UnlockMutex(q->mutex);
    }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    /* return < 0 if aborted, 0 if no packet and > 0 if packet.  */
    
    static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block, int *serial)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int ret;
    
        SDL_LockMutex(q->mutex);
    
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        for (;;) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (q->abort_request) {
                ret = -1;
                break;
            }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            pkt1 = q->first_pkt;
            if (pkt1) {
                q->first_pkt = pkt1->next;
                if (!q->first_pkt)
                    q->last_pkt = NULL;
                q->nb_packets--;
    
                q->size -= pkt1->pkt.size + sizeof(*pkt1);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                *pkt = pkt1->pkt;
    
                if (serial)
                    *serial = pkt1->serial;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                av_free(pkt1);
                ret = 1;
                break;
            } else if (!block) {
                ret = 0;
                break;
            } else {
                SDL_CondWait(q->cond, q->mutex);
            }
        }
        SDL_UnlockMutex(q->mutex);
        return ret;
    }
    
    
    static inline void fill_rectangle(SDL_Surface *screen,
    
                                      int x, int y, int w, int h, int color, int update)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        SDL_Rect rect;
        rect.x = x;
        rect.y = y;
        rect.w = w;
        rect.h = h;
        SDL_FillRect(screen, &rect, color);
    
        if (update && w > 0 && h > 0)
            SDL_UpdateRect(screen, x, y, w, h);
    
    /* draw only the border of a rectangle */
    static void fill_border(int xleft, int ytop, int width, int height, int x, int y, int w, int h, int color, int update)
    {
        int w1, w2, h1, h2;
    
        /* fill the background */
        w1 = x;
        if (w1 < 0)
            w1 = 0;
        w2 = width - (x + w);
        if (w2 < 0)
            w2 = 0;
        h1 = y;
        if (h1 < 0)
            h1 = 0;
        h2 = height - (y + h);
        if (h2 < 0)
            h2 = 0;
        fill_rectangle(screen,
                       xleft, ytop,
                       w1, height,
                       color, update);
        fill_rectangle(screen,
                       xleft + width - w2, ytop,
                       w2, height,
                       color, update);
        fill_rectangle(screen,
                       xleft + w1, ytop,
                       width - w1 - w2, h1,
                       color, update);
        fill_rectangle(screen,
                       xleft + w1, ytop + height - h2,
                       width - w1 - w2, h2,
                       color, update);
    }
    
    
    #define ALPHA_BLEND(a, oldp, newp, s)\
    ((((oldp << s) * (255 - (a))) + (newp * (a))) / (255 << s))
    
    #define RGBA_IN(r, g, b, a, s)\
    {\
        unsigned int v = ((const uint32_t *)(s))[0];\
        a = (v >> 24) & 0xff;\
        r = (v >> 16) & 0xff;\
        g = (v >> 8) & 0xff;\
        b = v & 0xff;\
    }
    
    #define YUVA_IN(y, u, v, a, s, pal)\
    {\
    
        unsigned int val = ((const uint32_t *)(pal))[*(const uint8_t*)(s)];\
    
        a = (val >> 24) & 0xff;\
        y = (val >> 16) & 0xff;\
        u = (val >> 8) & 0xff;\
        v = val & 0xff;\
    }
    
    #define YUVA_OUT(d, y, u, v, a)\
    {\
        ((uint32_t *)(d))[0] = (a << 24) | (y << 16) | (u << 8) | v;\
    }
    
    
    #define BPP 1
    
    
    static void blend_subrect(AVPicture *dst, const AVSubtitleRect *rect, int imgw, int imgh)
    
    {
        int wrap, wrap3, width2, skip2;
        int y, u, v, a, u1, v1, a1, w, h;
        uint8_t *lum, *cb, *cr;
        const uint8_t *p;
        const uint32_t *pal;
    
        dstw = av_clip(rect->w, 0, imgw);
        dsth = av_clip(rect->h, 0, imgh);
        dstx = av_clip(rect->x, 0, imgw - dstw);
        dsty = av_clip(rect->y, 0, imgh - dsth);
    
        lum = dst->data[0] + dsty * dst->linesize[0];
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        cb  = dst->data[1] + (dsty >> 1) * dst->linesize[1];
        cr  = dst->data[2] + (dsty >> 1) * dst->linesize[2];
    
        width2 = ((dstw + 1) >> 1) + (dstx & ~dstw & 1);
    
        wrap3 = rect->pict.linesize[0];
        p = rect->pict.data[0];
        pal = (const uint32_t *)rect->pict.data[1];  /* Now in YCrCb! */
    
                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], u1, 1);
                cr[0] = ALPHA_BLEND(a1 >> 2, cr[0], v1, 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);
    
            p += wrap3 - dstw * BPP;
            lum += wrap - dstw - dstx;
    
            cb += dst->linesize[1] - width2 - skip2;
            cr += dst->linesize[2] - width2 - skip2;
        }
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        for (h = dsth - (dsty & 1); h >= 2; h -= 2) {
    
                YUVA_IN(y, u, v, a, p, pal);
                u1 = u;
                v1 = v;
                a1 = a;
                lum[0] = ALPHA_BLEND(a, lum[0], y, 0);
                p += wrap3;
                lum += wrap;
                YUVA_IN(y, u, v, a, p, pal);
                u1 += u;
                v1 += v;
                a1 += a;
                lum[0] = ALPHA_BLEND(a, lum[0], y, 0);
                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;
            }
    
    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);
                p += wrap3;
                lum += wrap;
    
                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], u1, 2);
                cr[0] = ALPHA_BLEND(a1 >> 2, cr[0], v1, 2);
    
                cb++;
                cr++;
                p += -wrap3 + 2 * BPP;
                lum += -wrap + 2;
            }
            if (w) {
                YUVA_IN(y, u, v, a, p, pal);
                u1 = u;
                v1 = v;
                a1 = a;
                lum[0] = ALPHA_BLEND(a, lum[0], y, 0);
                p += wrap3;
                lum += wrap;
                YUVA_IN(y, u, v, a, p, pal);
                u1 += u;
                v1 += v;
                a1 += a;
                lum[0] = ALPHA_BLEND(a, lum[0], y, 0);
                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_subpicture(SubPicture *sp)
    {
    
        avsubtitle_free(&sp->sub);
    
    static void calculate_display_rect(SDL_Rect *rect, int scr_xleft, int scr_ytop, int scr_width, int scr_height, VideoPicture *vp)
    {
        float aspect_ratio;
        int width, height, x, y;
    
    
            aspect_ratio = av_q2d(vp->sar);
    
    
        if (aspect_ratio <= 0.0)
            aspect_ratio = 1.0;
        aspect_ratio *= (float)vp->width / (float)vp->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)
    {
        VideoPicture *vp;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        SDL_Rect rect;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        vp = &is->pictq[is->pictq_rindex];
        if (vp->bmp) {
    
            if (is->subtitle_st) {
                if (is->subpq_size > 0) {
    
                    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);
    
    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() - 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(4 * nb_freq * sizeof(*s->rdft_data));
    
    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]);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                // least efficient way to do this, we should of course directly access it but its more than fast enough
                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);
    
            if (!s->paused)
                s->xpos++;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            if (s->xpos >= s->width)
    
                s->xpos= s->xleft;
        }