X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=ffplay.c;fp=avplay.c;h=759f46f3b5f9f0b4eeb77127c99d89ea0715cc83;hb=231fd4411ff35762cb82145c9bebc2dd1db191e0;hp=4b7576cc1fcd3bf37440f6dfec0583c999303999;hpb=38f0c0781a6e099f11c0acec07f9b8be742190c4;p=ffmpeg diff --git a/avplay.c b/ffplay.c similarity index 55% rename from avplay.c rename to ffplay.c index 4b7576cc1fc..759f46f3b5f 100644 --- a/avplay.c +++ b/ffplay.c @@ -1,28 +1,33 @@ /* - * avplay : Simple Media Player based on the Libav libraries * Copyright (c) 2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * 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. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file + * simple media player based on the FFmpeg libraries + */ + #include "config.h" #include #include #include +#include #include "libavutil/avstring.h" #include "libavutil/colorspace.h" #include "libavutil/mathematics.h" @@ -31,36 +36,33 @@ #include "libavutil/dict.h" #include "libavutil/parseutils.h" #include "libavutil/samplefmt.h" +#include "libavutil/avassert.h" #include "libavutil/time.h" #include "libavformat/avformat.h" #include "libavdevice/avdevice.h" #include "libswscale/swscale.h" -#include "libavresample/avresample.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/buffersink.h" # include "libavfilter/buffersrc.h" #endif -#include "cmdutils.h" - #include #include -#ifdef __MINGW32__ -#undef main /* We don't want SDL to override our main() */ -#endif +#include "cmdutils.h" #include -const char program_name[] = "avplay"; +const char program_name[] = "ffplay"; const int program_birth_year = 2003; #define MAX_QUEUE_SIZE (15 * 1024 * 1024) -#define MIN_AUDIOQ_SIZE (20 * 16 * 1024) #define MIN_FRAMES 5 /* SDL audio buffer size, in samples. Should be small to have precise @@ -72,40 +74,55 @@ const int program_birth_year = 2003; /* no AV correction is done if too big error */ #define AV_NOSYNC_THRESHOLD 10.0 -#define FRAME_SKIP_FACTOR 0.05 - /* 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 + /* NOTE: the size must be big enough to compensate the hardware audio buffersize size */ -#define SAMPLE_ARRAY_SIZE (2 * 65536) +/* 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; + typedef struct PacketQueue { - AVPacketList *first_pkt, *last_pkt; + MyAVPacketList *first_pkt, *last_pkt; int nb_packets; int size; int abort_request; + int serial; SDL_mutex *mutex; SDL_cond *cond; } PacketQueue; -#define VIDEO_PICTURE_QUEUE_SIZE 2 +#define VIDEO_PICTURE_QUEUE_SIZE 4 #define SUBPICTURE_QUEUE_SIZE 4 typedef struct VideoPicture { double pts; // presentation timestamp for this picture - double target_clock; // av_gettime() time at which this should be displayed ideally int64_t pos; // byte position in file SDL_Overlay *bmp; int width, height; /* source height & width */ int allocated; int reallocate; - enum AVPixelFormat pix_fmt; + int serial; AVRational sar; } VideoPicture; @@ -115,6 +132,13 @@ typedef struct SubPicture { AVSubtitle sub; } SubPicture; +typedef struct AudioParams { + int freq; + int channels; + int64_t channel_layout; + enum AVSampleFormat fmt; +} AudioParams; + enum { AV_SYNC_AUDIO_MASTER, /* default choice */ AV_SYNC_VIDEO_MASTER, @@ -122,28 +146,33 @@ enum { }; typedef struct VideoState { - SDL_Thread *parse_tid; + SDL_Thread *read_tid; SDL_Thread *video_tid; - SDL_Thread *refresh_tid; AVInputFormat *iformat; int no_background; int abort_request; + int force_refresh; int paused; int last_paused; + int queue_attachments_req; int seek_req; int seek_flags; int64_t seek_pos; int64_t seek_rel; int read_pause_return; AVFormatContext *ic; + int realtime; int audio_stream; int av_sync_type; - double external_clock; /* external clock base */ - int64_t external_clock_time; + double external_clock; ///< external clock base + double external_clock_drift; ///< external clock base - time (av_gettime) at which we updated external_clock + int64_t external_clock_time; ///< last reference time + double external_clock_speed; ///< speed of the external clock double audio_clock; + int audio_clock_serial; double audio_diff_cum; /* used for AV difference average computation */ double audio_diff_avg_coef; double audio_diff_threshold; @@ -155,20 +184,29 @@ typedef struct VideoState { uint8_t *audio_buf; uint8_t *audio_buf1; unsigned int audio_buf_size; /* in bytes */ + unsigned int audio_buf1_size; int audio_buf_index; /* in bytes */ + int audio_write_buf_size; + int audio_buf_frames_pending; AVPacket audio_pkt_temp; AVPacket audio_pkt; - enum AVSampleFormat sdl_sample_fmt; - uint64_t sdl_channel_layout; - int sdl_channels; - int sdl_sample_rate; - enum AVSampleFormat resample_sample_fmt; - uint64_t resample_channel_layout; - int resample_sample_rate; - AVAudioResampleContext *avr; + int audio_pkt_temp_serial; + int audio_last_serial; + struct AudioParams audio_src; +#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; - int show_audio; /* if true, display audio samples */ + enum ShowMode { + SHOW_MODE_NONE = -1, SHOW_MODE_VIDEO = 0, SHOW_MODE_WAVES, SHOW_MODE_RDFT, SHOW_MODE_NB + } show_mode; int16_t sample_array[SAMPLE_ARRAY_SIZE]; int sample_array_index; int last_i_start; @@ -176,6 +214,7 @@ typedef struct VideoState { int rdft_bits; FFTSample *rdft_data; int xpos; + double last_vis_time; SDL_Thread *subtitle_tid; int subtitle_stream; @@ -189,14 +228,20 @@ typedef struct VideoState { double frame_timer; double frame_last_pts; - double frame_last_delay; - double video_clock; // pts of last decoded frame / predicted pts of next decoded frame + double frame_last_duration; + 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; int video_stream; AVStream *video_st; PacketQueue videoq; - double video_current_pts; // current displayed pts (different from video_clock if frame fifos are used) + 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 + int video_clock_serial; VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE]; int pictq_size, pictq_rindex, pictq_windex; SDL_mutex *pictq_mutex; @@ -204,21 +249,23 @@ typedef struct VideoState { #if !CONFIG_AVFILTER struct SwsContext *img_convert_ctx; #endif + SDL_Rect last_display_rect; - // QETimer *video_timer; char filename[1024]; int width, height, xleft, ytop; - - PtsCorrectionContext pts_ctx; + int step; #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 + 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 #endif - float skip_frames; - float skip_frames_index; - int refresh; + int last_video_stream, last_audio_stream, last_subtitle_stream; + + SDL_cond *continue_read_thread; } VideoState; /* options specified by the user */ @@ -227,10 +274,13 @@ static const char *input_filename; static const char *window_title; static int fs_screen_width; static int fs_screen_height; +static int default_width = 640; +static int default_height = 480; static int screen_width = 0; static int screen_height = 0; static int audio_disable; static int video_disable; +static int subtitle_disable; static int wanted_stream[AVMEDIA_TYPE_NB] = { [AVMEDIA_TYPE_AUDIO] = -1, [AVMEDIA_TYPE_VIDEO] = -1, @@ -242,56 +292,122 @@ static int show_status = 1; static int av_sync_type = AV_SYNC_AUDIO_MASTER; static int64_t start_time = AV_NOPTS_VALUE; static int64_t duration = AV_NOPTS_VALUE; -static int debug_mv = 0; -static int step = 0; static int workaround_bugs = 1; static int fast = 0; static int genpts = 0; +static int lowres = 0; static int idct = FF_IDCT_AUTO; -static enum AVDiscard skip_frame = AVDISCARD_DEFAULT; -static enum AVDiscard skip_idct = AVDISCARD_DEFAULT; -static enum AVDiscard skip_loop_filter = AVDISCARD_DEFAULT; static int error_concealment = 3; static int decoder_reorder_pts = -1; static int autoexit; static int exit_on_keydown; static int exit_on_mousedown; static int loop = 1; -static int framedrop = 1; -static int infinite_buffer = 0; - -static int rdftspeed = 20; +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; +double rdftspeed = 0.02; +static int64_t cursor_last_shown; +static int cursor_hidden = 0; #if CONFIG_AVFILTER static char *vfilters = NULL; +static char *afilters = NULL; #endif /* current context */ static int is_full_screen; -static VideoState *cur_stream; static int64_t audio_callback_time; static AVPacket flush_pkt; #define FF_ALLOC_EVENT (SDL_USEREVENT) -#define FF_REFRESH_EVENT (SDL_USEREVENT + 1) #define FF_QUIT_EVENT (SDL_USEREVENT + 2) static SDL_Surface *screen; +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) +{ + MyAVPacketList *pkt1; + + 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; +} + /* packet queue handling */ static void packet_queue_init(PacketQueue *q) { memset(q, 0, sizeof(PacketQueue)); q->mutex = SDL_CreateMutex(); q->cond = SDL_CreateCond(); - packet_queue_put(q, &flush_pkt); + q->abort_request = 1; } static void packet_queue_flush(PacketQueue *q) { - AVPacketList *pkt, *pkt1; + MyAVPacketList *pkt, *pkt1; SDL_LockMutex(q->mutex); for (pkt = q->first_pkt; pkt != NULL; pkt = pkt1) { @@ -306,60 +422,36 @@ static void packet_queue_flush(PacketQueue *q) SDL_UnlockMutex(q->mutex); } -static void packet_queue_end(PacketQueue *q) +static void packet_queue_destroy(PacketQueue *q) { packet_queue_flush(q); SDL_DestroyMutex(q->mutex); SDL_DestroyCond(q->cond); } -static int packet_queue_put(PacketQueue *q, AVPacket *pkt) +static void packet_queue_abort(PacketQueue *q) { - AVPacketList *pkt1; - - /* duplicate the packet */ - if (pkt != &flush_pkt && av_dup_packet(pkt) < 0) - return -1; - - pkt1 = av_malloc(sizeof(AVPacketList)); - if (!pkt1) - return -1; - pkt1->pkt = *pkt; - pkt1->next = NULL; - - SDL_LockMutex(q->mutex); - if (!q->last_pkt) + q->abort_request = 1; - 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); SDL_UnlockMutex(q->mutex); - return 0; } -static void packet_queue_abort(PacketQueue *q) +static void packet_queue_start(PacketQueue *q) { SDL_LockMutex(q->mutex); - - q->abort_request = 1; - - SDL_CondSignal(q->cond); - + q->abort_request = 0; + packet_queue_put_private(q, &flush_pkt); SDL_UnlockMutex(q->mutex); } /* return < 0 if aborted, 0 if no packet and > 0 if packet. */ -static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) +static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block, int *serial) { - AVPacketList *pkt1; + MyAVPacketList *pkt1; int ret; SDL_LockMutex(q->mutex); @@ -378,6 +470,8 @@ static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) q->nb_packets--; q->size -= pkt1->pkt.size + sizeof(*pkt1); *pkt = pkt1->pkt; + if (serial) + *serial = pkt1->serial; av_free(pkt1); ret = 1; break; @@ -393,7 +487,7 @@ static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) } static inline void fill_rectangle(SDL_Surface *screen, - int x, int y, int w, int h, int color) + int x, int y, int w, int h, int color, int update) { SDL_Rect rect; rect.x = x; @@ -401,6 +495,44 @@ static inline void fill_rectangle(SDL_Surface *screen, 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)\ @@ -637,45 +769,50 @@ 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; + + if (vp->sar.num == 0) + aspect_ratio = 0; + else + 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); +} + static void video_image_display(VideoState *is) { VideoPicture *vp; SubPicture *sp; AVPicture pict; - float aspect_ratio; - int width, height, x, y; SDL_Rect rect; int i; vp = &is->pictq[is->pictq_rindex]; if (vp->bmp) { -#if CONFIG_AVFILTER - if (!vp->sar.num) - aspect_ratio = 0; - else - aspect_ratio = av_q2d(vp->sar); -#else - - /* XXX: use variable in the frame */ - if (is->video_st->sample_aspect_ratio.num) - aspect_ratio = av_q2d(is->video_st->sample_aspect_ratio); - else if (is->video_st->codec->sample_aspect_ratio.num) - aspect_ratio = av_q2d(is->video_st->codec->sample_aspect_ratio); - else - aspect_ratio = 0; -#endif - if (aspect_ratio <= 0.0) - aspect_ratio = 1.0; - aspect_ratio *= (float)vp->width / (float)vp->height; - - if (is->subtitle_st) - { - if (is->subpq_size > 0) - { + if (is->subtitle_st) { + if (is->subpq_size > 0) { sp = &is->subpq[is->subpq_rindex]; - if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) - { + if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) { SDL_LockYUVOverlay (vp->bmp); pict.data[0] = vp->bmp->pixels[0]; @@ -695,46 +832,28 @@ static void video_image_display(VideoState *is) } } + calculate_display_rect(&rect, is->xleft, is->ytop, is->width, is->height, vp); - /* XXX: we suppose the screen has a 1.0 pixel ratio */ - height = is->height; - width = ((int)rint(height * aspect_ratio)) & ~1; - if (width > is->width) { - width = is->width; - height = ((int)rint(width / aspect_ratio)) & ~1; - } - x = (is->width - width) / 2; - y = (is->height - height) / 2; - is->no_background = 0; - rect.x = is->xleft + x; - rect.y = is->ytop + y; - rect.w = width; - rect.h = height; SDL_DisplayYUVOverlay(vp->bmp, &rect); - } -} -/* get the current audio output buffer size, in samples. With SDL, we - cannot have a precise information */ -static int audio_write_get_buf_size(VideoState *is) -{ - return is->audio_buf_size - is->audio_buf_index; + 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; + } + } } static inline int compute_mod(int a, int b) { - a = a % b; - if (a >= 0) - return a; - else - return a + b; + return a < 0 ? a%b + b : a%b; } 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; - int16_t time_diff; + int64_t time_diff; int rdft_bits, nb_freq; for (rdft_bits = 1; (1 << rdft_bits) < 2 * s->height; rdft_bits++) @@ -742,19 +861,19 @@ static void video_audio_display(VideoState *s) nb_freq = 1 << (rdft_bits - 1); /* compute display index : center on currently output samples */ - channels = s->sdl_channels; + channels = s->audio_tgt.channels; nb_display_channels = channels; if (!s->paused) { - int data_used = s->show_audio == 1 ? s->width : (2 * nb_freq); + int data_used= s->show_mode == SHOW_MODE_WAVES ? s->width : (2*nb_freq); n = 2 * channels; - delay = audio_write_get_buf_size(s); + delay = s->audio_write_buf_size; delay /= n; /* 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->sdl_sample_rate) / 1000000; + delay -= (time_diff * s->audio_tgt.freq) / 1000000; } delay += 2 * data_used; @@ -762,7 +881,7 @@ static void video_audio_display(VideoState *s) delay = data_used; i_start= x = compute_mod(s->sample_array_index - delay * channels, SAMPLE_ARRAY_SIZE); - if (s->show_audio == 1) { + if (s->show_mode == SHOW_MODE_WAVES) { h = INT_MIN; for (i = 0; i < 1000; i += channels) { int idx = (SAMPLE_ARRAY_SIZE + x - i) % SAMPLE_ARRAY_SIZE; @@ -784,10 +903,10 @@ static void video_audio_display(VideoState *s) } bgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0x00); - if (s->show_audio == 1) { + if (s->show_mode == SHOW_MODE_WAVES) { fill_rectangle(screen, s->xleft, s->ytop, s->width, s->height, - bgcolor); + bgcolor, 0); fgcolor = SDL_MapRGB(screen->format, 0xff, 0xff, 0xff); @@ -808,7 +927,7 @@ static void video_audio_display(VideoState *s) } fill_rectangle(screen, s->xleft + x, ys, 1, y, - fgcolor); + fgcolor, 0); i += channels; if (i >= SAMPLE_ARRAY_SIZE) i -= SAMPLE_ARRAY_SIZE; @@ -821,7 +940,7 @@ static void video_audio_display(VideoState *s) y = s->ytop + ch * h; fill_rectangle(screen, s->xleft, y, s->width, 1, - fgcolor); + fgcolor, 0); } SDL_UpdateRect(screen, s->xleft, s->ytop, s->width, s->height); } else { @@ -859,56 +978,103 @@ static void video_audio_display(VideoState *s) fill_rectangle(screen, s->xpos, s->height-y, 1, 1, - fgcolor); + fgcolor, 0); } } SDL_UpdateRect(screen, s->xpos, s->ytop, 1, s->height); - s->xpos++; + if (!s->paused) + s->xpos++; if (s->xpos >= s->width) s->xpos= s->xleft; } } -static int video_open(VideoState *is) +static void stream_close(VideoState *is) +{ + VideoPicture *vp; + int i; + /* 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 */ + for (i = 0; i < VIDEO_PICTURE_QUEUE_SIZE; i++) { + vp = &is->pictq[i]; + if (vp->bmp) { + SDL_FreeYUVOverlay(vp->bmp); + vp->bmp = NULL; + } + } + SDL_DestroyMutex(is->pictq_mutex); + SDL_DestroyCond(is->pictq_cond); + SDL_DestroyMutex(is->subpq_mutex); + SDL_DestroyCond(is->subpq_cond); + SDL_DestroyCond(is->continue_read_thread); +#if !CONFIG_AVFILTER + sws_freeContext(is->img_convert_ctx); +#endif + av_free(is); +} + +static void do_exit(VideoState *is) +{ + if (is) { + stream_close(is); + } + av_lockmgr_register(NULL); + uninit_opts(); +#if CONFIG_AVFILTER + avfilter_uninit(); + av_freep(&vfilters); +#endif + avformat_network_deinit(); + if (show_status) + printf("\n"); + SDL_Quit(); + av_log(NULL, AV_LOG_QUIET, "%s", ""); + exit(0); +} + +static void sigterm_handler(int sig) +{ + exit(123); +} + +static int video_open(VideoState *is, int force_set_video_mode, VideoPicture *vp) { int flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL; int w,h; + SDL_Rect rect; if (is_full_screen) flags |= SDL_FULLSCREEN; else flags |= SDL_RESIZABLE; + if (vp && vp->width) { + calculate_display_rect(&rect, 0, 0, INT_MAX, vp->height, vp); + default_width = rect.w; + default_height = rect.h; + } + if (is_full_screen && fs_screen_width) { w = fs_screen_width; h = fs_screen_height; } else if (!is_full_screen && screen_width) { w = screen_width; h = screen_height; -#if CONFIG_AVFILTER - } else if (is->out_video_filter && is->out_video_filter->inputs[0]) { - w = is->out_video_filter->inputs[0]->w; - h = is->out_video_filter->inputs[0]->h; -#else - } else if (is->video_st && is->video_st->codec->width) { - w = is->video_st->codec->width; - h = is->video_st->codec->height; -#endif } else { - w = 640; - h = 480; + w = default_width; + h = default_height; } if (screen && is->width == screen->w && screen->w == w - && is->height== screen->h && screen->h == h) + && is->height== screen->h && screen->h == h && !force_set_video_mode) return 0; - -#if defined(__APPLE__) && !SDL_VERSION_ATLEAST(1, 2, 14) - /* setting bits_per_pixel = 0 or 32 causes blank video on OS X and older SDL */ - screen = SDL_SetVideoMode(w, h, 24, flags); -#else screen = SDL_SetVideoMode(w, h, 0, flags); -#endif if (!screen) { fprintf(stderr, "SDL: could not set video mode - exiting\n"); - return -1; + do_exit(is); } if (!window_title) window_title = input_filename; @@ -924,49 +1090,30 @@ static int video_open(VideoState *is) static void video_display(VideoState *is) { if (!screen) - video_open(cur_stream); - if (is->audio_st && is->show_audio) + video_open(is, 0, NULL); + if (is->audio_st && is->show_mode != SHOW_MODE_VIDEO) video_audio_display(is); else if (is->video_st) video_image_display(is); } -static int refresh_thread(void *opaque) -{ - VideoState *is= opaque; - while (!is->abort_request) { - SDL_Event event; - event.type = FF_REFRESH_EVENT; - event.user.data1 = opaque; - if (!is->refresh) { - is->refresh = 1; - SDL_PushEvent(&event); - } - av_usleep(is->audio_st && is->show_audio ? rdftspeed * 1000 : 5000); // FIXME ideally we should wait the correct time but SDLs event passing is so slow it would be silly - } - return 0; -} - /* get the current audio clock value */ static double get_audio_clock(VideoState *is) { - double pts; - int hw_buf_size, bytes_per_sec; - pts = is->audio_clock; - hw_buf_size = audio_write_get_buf_size(is); - bytes_per_sec = 0; - if (is->audio_st) { - bytes_per_sec = is->sdl_sample_rate * is->sdl_channels * - av_get_bytes_per_sample(is->sdl_sample_fmt); + if (is->audio_clock_serial != is->audioq.serial) + return NAN; + if (is->paused) { + return is->audio_current_pts; + } else { + return is->audio_current_pts_drift + av_gettime() / 1000000.0; } - if (bytes_per_sec) - pts -= (double)hw_buf_size / bytes_per_sec; - return pts; } /* get the current video clock value */ static double get_video_clock(VideoState *is) { + if (is->video_clock_serial != is->videoq.serial) + return NAN; if (is->paused) { return is->video_current_pts; } else { @@ -977,32 +1124,82 @@ static double get_video_clock(VideoState *is) /* get the current external clock value */ static double get_external_clock(VideoState *is) { - int64_t ti; - ti = av_gettime(); - return is->external_clock + ((ti - is->external_clock_time) * 1e-6); + 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); + } } -/* get the current master clock value */ -static double get_master_clock(VideoState *is) -{ - double val; - +static int get_master_sync_type(VideoState *is) { if (is->av_sync_type == AV_SYNC_VIDEO_MASTER) { if (is->video_st) - val = get_video_clock(is); + return AV_SYNC_VIDEO_MASTER; else - val = get_audio_clock(is); + return AV_SYNC_AUDIO_MASTER; } else if (is->av_sync_type == AV_SYNC_AUDIO_MASTER) { if (is->audio_st) - val = get_audio_clock(is); + return AV_SYNC_AUDIO_MASTER; else - val = get_video_clock(is); + return AV_SYNC_EXTERNAL_CLOCK; } else { - val = get_external_clock(is); + 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; } return val; } +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) { + double ext_clock = get_external_clock(is); + if (isnan(ext_clock) || fabs(ext_clock - 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)); + } +} + /* seek in the stream */ static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int seek_by_bytes) { @@ -1013,11 +1210,12 @@ static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int seek_by_by if (seek_by_bytes) is->seek_flags |= AVSEEK_FLAG_BYTE; is->seek_req = 1; + SDL_CondSignal(is->continue_read_thread); } } /* pause or resume the video */ -static void stream_pause(VideoState *is) +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; @@ -1026,26 +1224,30 @@ static void stream_pause(VideoState *is) } is->video_current_pts_drift = is->video_current_pts - av_gettime() / 1000000.0; } + update_external_clock_pts(is, get_external_clock(is)); is->paused = !is->paused; } -static double compute_target_time(double frame_current_pts, VideoState *is) +static void toggle_pause(VideoState *is) { - double delay, sync_threshold, diff; + stream_toggle_pause(is); + is->step = 0; +} - /* compute nominal delay */ - delay = frame_current_pts - is->frame_last_pts; - if (delay <= 0 || delay >= 10.0) { - /* if incorrect delay, use previous one */ - delay = is->frame_last_delay; - } else { - is->frame_last_delay = delay; - } - is->frame_last_pts = frame_current_pts; +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; /* update delay to follow master synchronisation source */ - if (((is->av_sync_type == AV_SYNC_AUDIO_MASTER && is->audio_st) || - is->av_sync_type == AV_SYNC_EXTERNAL_CLOCK)) { + 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); @@ -1054,63 +1256,139 @@ static double compute_target_time(double frame_current_pts, VideoState *is) 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 (!isnan(diff) && fabs(diff) < AV_NOSYNC_THRESHOLD) { if (diff <= -sync_threshold) delay = 0; else if (diff >= sync_threshold) delay = 2 * delay; } } - is->frame_timer += delay; - av_dlog(NULL, "video: delay=%0.3f pts=%0.3f A-V=%f\n", - delay, frame_current_pts, -diff); + av_dlog(NULL, "video: delay=%0.3f A-V=%f\n", + delay, -diff); - return is->frame_timer; + 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 int pictq_prev_picture(VideoState *is) { + VideoPicture *prevvp; + int ret = 0; + /* 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++; + ret = 1; + } + SDL_CondSignal(is->pictq_cond); + SDL_UnlockMutex(is->pictq_mutex); + } + return ret; +} + +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; + is->video_clock_serial = serial; + if (is->videoq.serial == serial) + check_external_clock_sync(is, is->video_current_pts); } /* called to display each frame */ -static void video_refresh_timer(void *opaque) +static void video_refresh(void *opaque, double *remaining_time) { VideoState *is = opaque; VideoPicture *vp; + double time; SubPicture *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() / 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); + } + if (is->video_st) { + int redisplay = 0; + if (is->force_refresh) + redisplay = pictq_prev_picture(is); retry: if (is->pictq_size == 0) { - // nothing to do, no picture to display in the que + 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, is->frame_last_dropped_serial); + is->frame_last_dropped_pts = AV_NOPTS_VALUE; + } + SDL_UnlockMutex(is->pictq_mutex); + // nothing to do, no picture to display in the queue } else { - double time = av_gettime() / 1000000.0; - double next_target; + double last_duration, duration, delay; /* dequeue the picture */ vp = &is->pictq[is->pictq_rindex]; - if (time < vp->target_clock) + if (vp->serial != is->videoq.serial) { + pictq_next_picture(is); + redisplay = 0; + goto retry; + } + + if (is->paused) + goto display; + + /* 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) { + *remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time); return; - /* update current video pts */ - is->video_current_pts = vp->pts; - is->video_current_pts_drift = is->video_current_pts - time; - is->video_current_pos = vp->pos; + } + + 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); + if (is->pictq_size > 1) { VideoPicture *nextvp = &is->pictq[(is->pictq_rindex + 1) % VIDEO_PICTURE_QUEUE_SIZE]; - assert(nextvp->target_clock >= vp->target_clock); - next_target= nextvp->target_clock; - } else { - next_target = vp->target_clock + is->video_clock - vp->pts; // FIXME pass durations cleanly - } - if (framedrop && time > next_target) { - is->skip_frames *= 1.0 + FRAME_SKIP_FACTOR; - if (is->pictq_size > 1 || time > next_target + 0.5) { - /* 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); + duration = nextvp->pts - vp->pts; + 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++; + pictq_next_picture(is); + redisplay = 0; goto retry; } } @@ -1159,29 +1437,18 @@ retry: } } +display: /* display picture */ - if (!display_disable) + if (!display_disable && is->show_mode == SHOW_MODE_VIDEO) video_display(is); - /* update queue size and signal for next picture */ - if (++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) - is->pictq_rindex = 0; + pictq_next_picture(is); - SDL_LockMutex(is->pictq_mutex); - is->pictq_size--; - SDL_CondSignal(is->pictq_cond); - SDL_UnlockMutex(is->pictq_mutex); + if (is->step && !is->paused) + stream_toggle_pause(is); } - } else if (is->audio_st) { - /* draw the next audio frame */ - - /* if only audio stream, then display the audio bars (better - than nothing, just to test the implementation */ - - /* display picture */ - if (!display_disable) - video_display(is); } + is->force_refresh = 0; if (show_status) { static int64_t last_time; int64_t cur_time; @@ -1202,66 +1469,25 @@ retry: 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 s:%3.1f aq=%5dKB vq=%5dKB sq=%5dB f=%"PRId64"/%"PRId64" \r", - get_master_clock(is), av_diff, FFMAX(is->skip_frames - 1, 0), aqsize / 1024, - vqsize / 1024, sqsize, is->pts_ctx.num_faulty_dts, is->pts_ctx.num_faulty_pts); + 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); fflush(stdout); last_time = cur_time; } } } -static void stream_close(VideoState *is) -{ - VideoPicture *vp; - int i; - /* XXX: use a special url_shutdown call to abort parse cleanly */ - is->abort_request = 1; - SDL_WaitThread(is->parse_tid, NULL); - SDL_WaitThread(is->refresh_tid, NULL); - - /* free all pictures */ - for (i = 0; i < VIDEO_PICTURE_QUEUE_SIZE; i++) { - vp = &is->pictq[i]; - if (vp->bmp) { - SDL_FreeYUVOverlay(vp->bmp); - vp->bmp = NULL; - } - } - SDL_DestroyMutex(is->pictq_mutex); - SDL_DestroyCond(is->pictq_cond); - SDL_DestroyMutex(is->subpq_mutex); - SDL_DestroyCond(is->subpq_cond); -#if !CONFIG_AVFILTER - if (is->img_convert_ctx) - sws_freeContext(is->img_convert_ctx); -#endif - av_free(is); -} - -static void do_exit(void) -{ - if (cur_stream) { - stream_close(cur_stream); - cur_stream = NULL; - } - uninit_opts(); -#if CONFIG_AVFILTER - avfilter_uninit(); -#endif - avformat_network_deinit(); - if (show_status) - printf("\n"); - SDL_Quit(); - av_log(NULL, AV_LOG_QUIET, ""); - exit(0); -} - /* allocate a picture (needs to do that in main thread to avoid potential locking problems */ -static void alloc_picture(void *opaque) +static void alloc_picture(VideoState *is) { - VideoState *is = opaque; VideoPicture *vp; vp = &is->pictq[is->pictq_windex]; @@ -1269,15 +1495,7 @@ static void alloc_picture(void *opaque) if (vp->bmp) SDL_FreeYUVOverlay(vp->bmp); -#if CONFIG_AVFILTER - vp->width = is->out_video_filter->inputs[0]->w; - vp->height = is->out_video_filter->inputs[0]->h; - vp->pix_fmt = is->out_video_filter->inputs[0]->format; -#else - vp->width = is->video_st->codec->width; - vp->height = is->video_st->codec->height; - vp->pix_fmt = is->video_st->codec->pix_fmt; -#endif + video_open(is, 0, vp); vp->bmp = SDL_CreateYUVOverlay(vp->width, vp->height, SDL_YV12_OVERLAY, @@ -1286,9 +1504,9 @@ static void alloc_picture(void *opaque) /* 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" - "size of %dx%d pixels. Try using -vf \"scale=w:h\"\n" + "size of %dx%d pixels. Try using -lowres or -vf \"scale=w:h\"\n" "to reduce the image size.\n", vp->width, vp->height ); - do_exit(); + do_exit(is); } SDL_LockMutex(is->pictq_mutex); @@ -1297,23 +1515,38 @@ static void alloc_picture(void *opaque) SDL_UnlockMutex(is->pictq_mutex); } -/* The 'pts' parameter is the dts of the packet / pts of the frame and - * guessed if not known. */ -static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, int64_t pos) +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, int64_t pos, int serial) { VideoPicture *vp; -#if CONFIG_AVFILTER - AVPicture pict_src; -#else - int dst_pix_fmt = AV_PIX_FMT_YUV420P; + +#if defined(DEBUG_SYNC) && 0 + printf("frame_type=%c pts=%0.3f\n", + av_get_picture_type_char(src_frame->pict_type), pts); #endif + /* wait until we have space to put a new picture */ SDL_LockMutex(is->pictq_mutex); - if (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->refresh) - is->skip_frames = FFMAX(1.0 - FRAME_SKIP_FACTOR, is->skip_frames * (1.0 - FRAME_SKIP_FACTOR)); - - while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && + /* keep the last already displayed picture in the queue */ + while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE - 2 && !is->videoq.abort_request) { SDL_CondWait(is->pictq_cond, is->pictq_mutex); } @@ -1324,22 +1557,25 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, int64_t vp = &is->pictq[is->pictq_windex]; - /* alloc or resize hardware picture buffer */ - if (!vp->bmp || vp->reallocate || #if CONFIG_AVFILTER - vp->width != is->out_video_filter->inputs[0]->w || - vp->height != is->out_video_filter->inputs[0]->h) { + vp->sar = src_frame->sample_aspect_ratio; #else - vp->width != is->video_st->codec->width || - vp->height != is->video_st->codec->height) { + vp->sar = av_guess_sample_aspect_ratio(is->ic, is->video_st, src_frame); #endif + + /* alloc or resize hardware picture buffer */ + if (!vp->bmp || vp->reallocate || !vp->allocated || + vp->width != src_frame->width || + vp->height != src_frame->height) { SDL_Event event; vp->allocated = 0; vp->reallocate = 0; + vp->width = src_frame->width; + vp->height = src_frame->height; /* the allocation must be done in the main thread to avoid - locking problems */ + locking problems. */ event.type = FF_ALLOC_EVENT; event.user.data1 = is; SDL_PushEvent(&event); @@ -1349,6 +1585,12 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, int64_t 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); + } + } SDL_UnlockMutex(is->pictq_mutex); if (is->videoq.abort_request) @@ -1371,22 +1613,14 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, int64_t pict.linesize[2] = vp->bmp->pitches[1]; #if CONFIG_AVFILTER - pict_src.data[0] = src_frame->data[0]; - pict_src.data[1] = src_frame->data[1]; - pict_src.data[2] = src_frame->data[2]; - - pict_src.linesize[0] = src_frame->linesize[0]; - pict_src.linesize[1] = src_frame->linesize[1]; - pict_src.linesize[2] = src_frame->linesize[2]; - // FIXME use direct rendering - av_picture_copy(&pict, &pict_src, - vp->pix_fmt, vp->width, vp->height); + av_picture_copy(&pict, (AVPicture *)src_frame, + src_frame->format, vp->width, vp->height); #else 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, vp->pix_fmt, vp->width, vp->height, - dst_pix_fmt, sws_flags, NULL, NULL, NULL); + 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); @@ -1394,175 +1628,256 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, int64_t 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); /* update the bitmap content */ SDL_UnlockYUVOverlay(vp->bmp); vp->pts = pts; vp->pos = pos; + vp->serial = serial; /* now we can update the picture count */ if (++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) is->pictq_windex = 0; SDL_LockMutex(is->pictq_mutex); - vp->target_clock = compute_target_time(vp->pts, is); - is->pictq_size++; SDL_UnlockMutex(is->pictq_mutex); } return 0; } -/* Compute the exact PTS for the picture if it is omitted in the stream. - * The 'pts1' parameter is the dts of the packet / pts of the frame. */ -static int output_picture2(VideoState *is, AVFrame *src_frame, double pts1, int64_t pos) -{ - double frame_delay, pts; - int ret; - - pts = pts1; - - 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; - - ret = queue_picture(is, src_frame, pts, pos); - av_frame_unref(src_frame); - return ret; -} - -static int get_video_frame(VideoState *is, AVFrame *frame, int64_t *pts, AVPacket *pkt) +static int get_video_frame(VideoState *is, AVFrame *frame, AVPacket *pkt, int *serial) { - int got_picture, i; + int got_picture; - if (packet_queue_get(&is->videoq, pkt, 1) < 0) + if (packet_queue_get(&is->videoq, pkt, 1, serial) < 0) return -1; 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 que but thats harder) - for (i = 0; i < VIDEO_PICTURE_QUEUE_SIZE; i++) { - is->pictq[i].target_clock= 0; - } + // 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; - SDL_UnlockMutex(is->pictq_mutex); - - init_pts_correction(&is->pts_ctx); is->frame_last_pts = AV_NOPTS_VALUE; - is->frame_last_delay = 0; + is->frame_last_duration = 0; is->frame_timer = (double)av_gettime() / 1000000.0; - is->skip_frames = 1; - is->skip_frames_index = 0; + is->frame_last_dropped_pts = AV_NOPTS_VALUE; + SDL_UnlockMutex(is->pictq_mutex); return 0; } - avcodec_decode_video2(is->video_st->codec, frame, &got_picture, pkt); + if(avcodec_decode_video2(is->video_st->codec, frame, &got_picture, pkt) < 0) + return 0; if (got_picture) { + int ret = 1; + if (decoder_reorder_pts == -1) { - *pts = guess_correct_pts(&is->pts_ctx, frame->pkt_pts, frame->pkt_dts); + frame->pts = av_frame_get_best_effort_timestamp(frame); } else if (decoder_reorder_pts) { - *pts = frame->pkt_pts; + frame->pts = frame->pkt_pts; } else { - *pts = frame->pkt_dts; + frame->pts = frame->pkt_dts; } - if (*pts == AV_NOPTS_VALUE) { - *pts = 0; - } - if (is->video_st->sample_aspect_ratio.num) { - frame->sample_aspect_ratio = is->video_st->sample_aspect_ratio; + if (frame->pts == AV_NOPTS_VALUE) { + frame->pts = 0; } - is->skip_frames_index += 1; - if (is->skip_frames_index >= is->skip_frames) { - is->skip_frames_index -= FFMAX(is->skip_frames, 1.0); - return 1; + 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 && frame->pts) { + double clockdiff = get_video_clock(is) - get_master_clock(is); + double dpts = av_q2d(is->video_st->time_base) * frame->pts; + double ptsdiff = dpts - is->frame_last_pts; + if (!isnan(clockdiff) && 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_last_dropped_serial = *serial; + is->frame_drops_early++; + av_frame_unref(frame); + ret = 0; + } + } + SDL_UnlockMutex(is->pictq_mutex); } - av_frame_unref(frame); + + return ret; } return 0; } #if CONFIG_AVFILTER -static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const char *vfilters) +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 }; char sws_flags_str[128]; char buffersrc_args[256]; int ret; - AVFilterContext *filt_src = NULL, *filt_out = NULL, *filt_format; + AVBufferSinkParams *buffersink_params = av_buffersink_params_alloc(); + AVFilterContext *filt_src = NULL, *filt_out = NULL, *filt_crop; AVCodecContext *codec = is->video_st->codec; + AVRational fr = av_guess_frame_rate(is->ic, is->video_st, NULL); + + 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); graph->scale_sws_opts = av_strdup(sws_flags_str); - snprintf(buffersrc_args, sizeof(buffersrc_args), "%d:%d:%d:%d:%d:%d:%d", - codec->width, codec->height, codec->pix_fmt, + 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, codec->sample_aspect_ratio.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"), - "src", buffersrc_args, NULL, + "ffplay_buffer", buffersrc_args, NULL, graph)) < 0) - return ret; - if ((ret = avfilter_graph_create_filter(&filt_out, - avfilter_get_by_name("buffersink"), - "out", NULL, NULL, graph)) < 0) - return ret; - - if ((ret = avfilter_graph_create_filter(&filt_format, - avfilter_get_by_name("format"), - "format", "yuv420p", NULL, graph)) < 0) - return ret; - if ((ret = avfilter_link(filt_format, 0, filt_out, 0)) < 0) - return ret; - - - if (vfilters) { - AVFilterInOut *outputs = avfilter_inout_alloc(); - AVFilterInOut *inputs = avfilter_inout_alloc(); - - outputs->name = av_strdup("in"); - outputs->filter_ctx = filt_src; - outputs->pad_idx = 0; - outputs->next = NULL; + goto fail; - inputs->name = av_strdup("out"); - inputs->filter_ctx = filt_format; - inputs->pad_idx = 0; - inputs->next = NULL; + buffersink_params->pixel_fmts = pix_fmts; + ret = avfilter_graph_create_filter(&filt_out, + avfilter_get_by_name("buffersink"), + "ffplay_buffersink", NULL, buffersink_params, graph); + if (ret < 0) + goto fail; - if ((ret = avfilter_graph_parse(graph, vfilters, inputs, outputs, NULL)) < 0) - return ret; - } else { - if ((ret = avfilter_link(filt_src, 0, filt_format, 0)) < 0) - return ret; - } + /* 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) + goto fail; + if ((ret = avfilter_link(filt_crop, 0, filt_out, 0)) < 0) + goto fail; - if ((ret = avfilter_graph_config(graph, NULL)) < 0) - return ret; + if ((ret = configure_filtergraph(graph, vfilters, filt_src, filt_crop)) < 0) + goto fail; is->in_video_filter = filt_src; is->out_video_filter = filt_out; +fail: + av_freep(&buffersink_params); return ret; } +static int configure_audio_filters(VideoState *is, const char *afilters, int force_output_format) +{ + static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }; + int sample_rates[2] = { 0, -1 }; + int64_t channel_layouts[2] = { 0, -1 }; + int channels[2] = { 0, -1 }; + AVFilterContext *filt_asrc = NULL, *filt_asink = NULL; + char asrc_args[256]; + AVABufferSinkParams *asink_params = NULL; + int ret; + + avfilter_graph_free(&is->agraph); + if (!(is->agraph = avfilter_graph_alloc())) + return AVERROR(ENOMEM); + + ret = snprintf(asrc_args, sizeof(asrc_args), + "sample_rate=%d:sample_fmt=%s:channels=%d:time_base=%d/%d", + is->audio_filter_src.freq, av_get_sample_fmt_name(is->audio_filter_src.fmt), + is->audio_filter_src.channels, + 1, is->audio_filter_src.freq); + if (is->audio_filter_src.channel_layout) + snprintf(asrc_args + ret, sizeof(asrc_args) - ret, + ":channel_layout=0x%"PRIx64, is->audio_filter_src.channel_layout); + + ret = avfilter_graph_create_filter(&filt_asrc, + avfilter_get_by_name("abuffer"), "ffplay_abuffer", + asrc_args, NULL, is->agraph); + if (ret < 0) + goto end; + + if (!(asink_params = av_abuffersink_params_alloc())) { + ret = AVERROR(ENOMEM); + goto end; + } + asink_params->sample_fmts = sample_fmts; + + asink_params->all_channel_counts = 1; + if (force_output_format) { + channel_layouts[0] = is->audio_tgt.channel_layout; + asink_params->channel_layouts = channel_layouts; + asink_params->all_channel_counts = 0; + channels[0] = is->audio_tgt.channels; + asink_params->channel_counts = channels; + asink_params->all_channel_counts = 0; + sample_rates[0] = is->audio_tgt.freq; + asink_params->sample_rates = sample_rates; + } + + ret = avfilter_graph_create_filter(&filt_asink, + avfilter_get_by_name("abuffersink"), "ffplay_abuffersink", + NULL, asink_params, is->agraph); + if (ret < 0) + goto end; + + if ((ret = configure_filtergraph(is->agraph, afilters, filt_asrc, filt_asink)) < 0) + goto end; + + is->in_audio_filter = filt_asrc; + is->out_audio_filter = filt_asink; + +end: + av_freep(&asink_params); + if (ret < 0) + avfilter_graph_free(&is->agraph); + return ret; +} #endif /* CONFIG_AVFILTER */ static int video_thread(void *arg) @@ -1570,94 +1885,98 @@ static int video_thread(void *arg) AVPacket pkt = { 0 }; VideoState *is = arg; AVFrame *frame = av_frame_alloc(); - int64_t pts_int; double pts; int ret; + int serial = 0; #if CONFIG_AVFILTER AVFilterGraph *graph = avfilter_graph_alloc(); AVFilterContext *filt_out = NULL, *filt_in = NULL; - int last_w = is->video_st->codec->width; - int last_h = is->video_st->codec->height; - - if ((ret = configure_video_filters(graph, is, vfilters)) < 0) - goto the_end; - filt_in = is->in_video_filter; - filt_out = is->out_video_filter; + int last_w = 0; + int last_h = 0; + enum AVPixelFormat last_format = -2; + int last_serial = -1; #endif for (;;) { -#if CONFIG_AVFILTER - AVRational tb; -#endif while (is->paused && !is->videoq.abort_request) SDL_Delay(10); + avcodec_get_frame_defaults(frame); av_free_packet(&pkt); - ret = get_video_frame(is, frame, &pts_int, &pkt); + ret = get_video_frame(is, frame, &pkt, &serial); if (ret < 0) goto the_end; - if (!ret) continue; #if CONFIG_AVFILTER - if ( last_w != is->video_st->codec->width - || last_h != is->video_st->codec->height) { - av_dlog(NULL, "Changing size %dx%d -> %dx%d\n", last_w, last_h, - is->video_st->codec->width, is->video_st->codec->height); + if ( last_w != frame->width + || last_h != frame->height + || last_format != frame->format + || last_serial != serial) { + av_log(NULL, AV_LOG_DEBUG, + "Video frame changed from size:%dx%d format:%s serial:%d to size:%dx%d format:%s serial:%d\n", + last_w, last_h, + (const char *)av_x_if_null(av_get_pix_fmt_name(last_format), "none"), last_serial, + frame->width, frame->height, + (const char *)av_x_if_null(av_get_pix_fmt_name(frame->format), "none"), serial); avfilter_graph_free(&graph); graph = avfilter_graph_alloc(); - if ((ret = configure_video_filters(graph, is, vfilters)) < 0) + 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); goto the_end; + } filt_in = is->in_video_filter; filt_out = is->out_video_filter; - last_w = is->video_st->codec->width; - last_h = is->video_st->codec->height; + last_w = frame->width; + last_h = frame->height; + last_format = frame->format; + last_serial = serial; } - frame->pts = pts_int; + frame->sample_aspect_ratio = av_guess_sample_aspect_ratio(is->ic, is->video_st, frame); ret = av_buffersrc_add_frame(filt_in, frame); if (ret < 0) goto the_end; + av_frame_unref(frame); + avcodec_get_frame_defaults(frame); + av_free_packet(&pkt); while (ret >= 0) { - ret = av_buffersink_get_frame(filt_out, frame); + is->frame_last_returned_time = av_gettime() / 1000000.0; + + ret = av_buffersink_get_frame_flags(filt_out, frame, 0); if (ret < 0) { ret = 0; break; } - pts_int = frame->pts; - tb = filt_out->inputs[0]->time_base; - 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 = output_picture2(is, frame, pts, 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; + + pts = frame->pts * av_q2d(filt_out->inputs[0]->time_base); + ret = queue_picture(is, frame, pts, av_frame_get_pkt_pos(frame), serial); + av_frame_unref(frame); } #else - pts = pts_int * av_q2d(is->video_st->time_base); - ret = output_picture2(is, frame, pts, pkt.pos); + pts = frame->pts * av_q2d(is->video_st->time_base); + ret = queue_picture(is, frame, pts, pkt.pos, serial); + av_frame_unref(frame); #endif if (ret < 0) goto the_end; - - - if (step) - if (cur_stream) - stream_pause(cur_stream); } the_end: + avcodec_flush_buffers(is->video_st->codec); #if CONFIG_AVFILTER - av_freep(&vfilters); avfilter_graph_free(&graph); #endif av_free_packet(&pkt); @@ -1679,7 +1998,7 @@ static int subtitle_thread(void *arg) while (is->paused && !is->subtitleq.abort_request) { SDL_Delay(10); } - if (packet_queue_get(&is->subtitleq, pkt, 1) < 0) + if (packet_queue_get(&is->subtitleq, pkt, 1, NULL) < 0) break; if (pkt->data == flush_pkt.data) { @@ -1706,8 +2025,9 @@ static int subtitle_thread(void *arg) 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; sp->pts = pts; for (i = 0; i < sp->sub.num_rects; i++) @@ -1753,27 +2073,20 @@ static void update_sample_display(VideoState *is, short *samples, int samples_si } } -/* return the new audio buffer size (samples can be added or deleted - to get better sync if video or external master clock) */ -static int synchronize_audio(VideoState *is, short *samples, - int samples_size1, double pts) +/* return the wanted number of samples to get better sync if sync_type is video + * or external master clock */ +static int synchronize_audio(VideoState *is, int nb_samples) { - int n, samples_size; - double ref_clock; - - n = is->sdl_channels * av_get_bytes_per_sample(is->sdl_sample_fmt); - samples_size = samples_size1; + int wanted_nb_samples = nb_samples; /* if not master, then we try to remove or add samples to correct the clock */ - if (((is->av_sync_type == AV_SYNC_VIDEO_MASTER && is->video_st) || - is->av_sync_type == AV_SYNC_EXTERNAL_CLOCK)) { + if (get_master_sync_type(is) != AV_SYNC_AUDIO_MASTER) { double diff, avg_diff; - int wanted_size, min_size, max_size, nb_samples; + int min_nb_samples, max_nb_samples; - ref_clock = get_master_clock(is); - diff = get_audio_clock(is) - ref_clock; + diff = get_audio_clock(is) - get_master_clock(is); - if (diff < AV_NOSYNC_THRESHOLD) { + if (!isnan(diff) && fabs(diff) < AV_NOSYNC_THRESHOLD) { is->audio_diff_cum = diff + is->audio_diff_avg_coef * is->audio_diff_cum; if (is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) { /* not enough measures to have a correct estimate */ @@ -1783,39 +2096,14 @@ static int synchronize_audio(VideoState *is, short *samples, avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); if (fabs(avg_diff) >= is->audio_diff_threshold) { - wanted_size = samples_size + ((int)(diff * is->sdl_sample_rate) * n); - nb_samples = samples_size / n; - - min_size = ((nb_samples * (100 - SAMPLE_CORRECTION_PERCENT_MAX)) / 100) * n; - max_size = ((nb_samples * (100 + SAMPLE_CORRECTION_PERCENT_MAX)) / 100) * n; - if (wanted_size < min_size) - wanted_size = min_size; - else if (wanted_size > max_size) - wanted_size = max_size; - - /* add or remove samples to correction the synchro */ - if (wanted_size < samples_size) { - /* remove samples */ - samples_size = wanted_size; - } else if (wanted_size > samples_size) { - uint8_t *samples_end, *q; - int nb; - - /* add samples */ - nb = (samples_size - wanted_size); - samples_end = (uint8_t *)samples + samples_size - n; - q = samples_end + n; - while (nb > 0) { - memcpy(q, samples_end, n); - q += n; - nb -= n; - } - samples_size = wanted_size; - } + wanted_nb_samples = nb_samples + (int)(diff * is->audio_src.freq); + min_nb_samples = ((nb_samples * (100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100)); + max_nb_samples = ((nb_samples * (100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100)); + wanted_nb_samples = FFMIN(FFMAX(wanted_nb_samples, min_nb_samples), max_nb_samples); } - av_dlog(NULL, "diff=%f adiff=%f sample_diff=%d apts=%0.3f vpts=%0.3f %f\n", - diff, avg_diff, samples_size - samples_size1, - is->audio_clock, is->video_clock, is->audio_diff_threshold); + av_dlog(NULL, "diff=%f adiff=%f sample_diff=%d apts=%0.3f %f\n", + diff, avg_diff, wanted_nb_samples - nb_samples, + is->audio_clock, is->audio_diff_threshold); } } else { /* too big difference : may be initial PTS errors, so @@ -1825,138 +2113,202 @@ static int synchronize_audio(VideoState *is, short *samples, } } - return samples_size; + return wanted_nb_samples; } -/* decode one audio frame and returns its uncompressed size */ -static int audio_decode_frame(VideoState *is, double *pts_ptr) +/** + * Decode one audio frame and return its uncompressed size. + * + * The processed audio frame is decoded, converted if required, and + * stored in is->audio_buf, with size in bytes given by the return + * value. + */ +static int audio_decode_frame(VideoState *is) { AVPacket *pkt_temp = &is->audio_pkt_temp; AVPacket *pkt = &is->audio_pkt; AVCodecContext *dec = is->audio_st->codec; - int n, len1, data_size, got_frame; - double pts; + int len1, data_size, resampled_data_size; + int64_t dec_channel_layout; + int got_frame; + av_unused double audio_clock0; int new_packet = 0; int flush_complete = 0; + int wanted_nb_samples; + AVRational tb; + int ret; + int reconfigure; for (;;) { /* NOTE: the audio packet can contain several frames */ - while (pkt_temp->size > 0 || (!pkt_temp->data && new_packet)) { - int resample_changed, audio_resample; - + while (pkt_temp->size > 0 || (!pkt_temp->data && new_packet) || is->audio_buf_frames_pending) { if (!is->frame) { if (!(is->frame = avcodec_alloc_frame())) return AVERROR(ENOMEM); - } else + } else { + av_frame_unref(is->frame); avcodec_get_frame_defaults(is->frame); + } - if (flush_complete) - break; - new_packet = 0; - len1 = avcodec_decode_audio4(dec, is->frame, &got_frame, pkt_temp); - if (len1 < 0) { - /* if error, we skip the frame */ - pkt_temp->size = 0; + if (is->audioq.serial != is->audio_pkt_temp_serial) break; - } - pkt_temp->data += len1; - pkt_temp->size -= len1; + if (is->paused) + return -1; + + if (!is->audio_buf_frames_pending) { + if (flush_complete) + break; + new_packet = 0; + len1 = avcodec_decode_audio4(dec, is->frame, &got_frame, pkt_temp); + if (len1 < 0) { + /* if error, we skip the frame */ + pkt_temp->size = 0; + break; + } + + pkt_temp->data += len1; + pkt_temp->size -= len1; + + if (!got_frame) { + /* stop sending empty packets if the decoder is finished */ + if (!pkt_temp->data && dec->codec->capabilities & CODEC_CAP_DELAY) + flush_complete = 1; + continue; + } + + tb = (AVRational){1, is->frame->sample_rate}; + if (is->frame->pts != AV_NOPTS_VALUE) + is->frame->pts = av_rescale_q(is->frame->pts, dec->time_base, tb); + if (is->frame->pts == AV_NOPTS_VALUE && pkt_temp->pts != AV_NOPTS_VALUE) + is->frame->pts = av_rescale_q(pkt_temp->pts, is->audio_st->time_base, tb); + if (pkt_temp->pts != AV_NOPTS_VALUE) + pkt_temp->pts += (double) is->frame->nb_samples / is->frame->sample_rate / av_q2d(is->audio_st->time_base); + +#if CONFIG_AVFILTER + dec_channel_layout = get_valid_channel_layout(is->frame->channel_layout, av_frame_get_channels(is->frame)); + + reconfigure = + cmp_audio_fmts(is->audio_filter_src.fmt, is->audio_filter_src.channels, + is->frame->format, av_frame_get_channels(is->frame)) || + is->audio_filter_src.channel_layout != dec_channel_layout || + is->audio_filter_src.freq != is->frame->sample_rate || + is->audio_pkt_temp_serial != is->audio_last_serial; + + if (reconfigure) { + char buf1[1024], buf2[1024]; + av_get_channel_layout_string(buf1, sizeof(buf1), -1, is->audio_filter_src.channel_layout); + av_get_channel_layout_string(buf2, sizeof(buf2), -1, dec_channel_layout); + av_log(NULL, AV_LOG_DEBUG, + "Audio frame changed from rate:%d ch:%d fmt:%s layout:%s serial:%d to rate:%d ch:%d fmt:%s layout:%s serial:%d\n", + is->audio_filter_src.freq, is->audio_filter_src.channels, av_get_sample_fmt_name(is->audio_filter_src.fmt), buf1, is->audio_last_serial, + is->frame->sample_rate, av_frame_get_channels(is->frame), av_get_sample_fmt_name(is->frame->format), buf2, is->audio_pkt_temp_serial); + + is->audio_filter_src.fmt = is->frame->format; + is->audio_filter_src.channels = av_frame_get_channels(is->frame); + is->audio_filter_src.channel_layout = dec_channel_layout; + is->audio_filter_src.freq = is->frame->sample_rate; + is->audio_last_serial = is->audio_pkt_temp_serial; + + if ((ret = configure_audio_filters(is, afilters, 1)) < 0) + return ret; + } - if (!got_frame) { - /* stop sending empty packets if the decoder is finished */ - if (!pkt_temp->data && dec->codec->capabilities & CODEC_CAP_DELAY) - flush_complete = 1; - continue; + if ((ret = av_buffersrc_add_frame(is->in_audio_filter, is->frame)) < 0) + return ret; + av_frame_unref(is->frame); +#endif + } +#if CONFIG_AVFILTER + if ((ret = av_buffersink_get_frame_flags(is->out_audio_filter, is->frame, 0)) < 0) { + if (ret == AVERROR(EAGAIN)) { + is->audio_buf_frames_pending = 0; + continue; + } + return ret; } - data_size = av_samples_get_buffer_size(NULL, dec->channels, + is->audio_buf_frames_pending = 1; + tb = is->out_audio_filter->inputs[0]->time_base; +#endif + + data_size = av_samples_get_buffer_size(NULL, av_frame_get_channels(is->frame), is->frame->nb_samples, is->frame->format, 1); - audio_resample = is->frame->format != is->sdl_sample_fmt || - is->frame->channel_layout != is->sdl_channel_layout || - is->frame->sample_rate != is->sdl_sample_rate; - - resample_changed = is->frame->format != is->resample_sample_fmt || - is->frame->channel_layout != is->resample_channel_layout || - is->frame->sample_rate != is->resample_sample_rate; - - if ((!is->avr && audio_resample) || resample_changed) { - int ret; - if (is->avr) - avresample_close(is->avr); - else if (audio_resample) { - is->avr = avresample_alloc_context(); - if (!is->avr) { - fprintf(stderr, "error allocating AVAudioResampleContext\n"); - break; - } + dec_channel_layout = + (is->frame->channel_layout && av_frame_get_channels(is->frame) == av_get_channel_layout_nb_channels(is->frame->channel_layout)) ? + is->frame->channel_layout : av_get_default_channel_layout(av_frame_get_channels(is->frame)); + wanted_nb_samples = synchronize_audio(is, is->frame->nb_samples); + + if (is->frame->format != is->audio_src.fmt || + dec_channel_layout != is->audio_src.channel_layout || + is->frame->sample_rate != is->audio_src.freq || + (wanted_nb_samples != is->frame->nb_samples && !is->swr_ctx)) { + swr_free(&is->swr_ctx); + is->swr_ctx = swr_alloc_set_opts(NULL, + is->audio_tgt.channel_layout, is->audio_tgt.fmt, is->audio_tgt.freq, + dec_channel_layout, is->frame->format, is->frame->sample_rate, + 0, NULL); + if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) { + fprintf(stderr, "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n", + is->frame->sample_rate, av_get_sample_fmt_name(is->frame->format), av_frame_get_channels(is->frame), + is->audio_tgt.freq, av_get_sample_fmt_name(is->audio_tgt.fmt), is->audio_tgt.channels); + break; } - if (audio_resample) { - av_opt_set_int(is->avr, "in_channel_layout", is->frame->channel_layout, 0); - av_opt_set_int(is->avr, "in_sample_fmt", is->frame->format, 0); - av_opt_set_int(is->avr, "in_sample_rate", is->frame->sample_rate, 0); - av_opt_set_int(is->avr, "out_channel_layout", is->sdl_channel_layout, 0); - av_opt_set_int(is->avr, "out_sample_fmt", is->sdl_sample_fmt, 0); - av_opt_set_int(is->avr, "out_sample_rate", is->sdl_sample_rate, 0); - - if ((ret = avresample_open(is->avr)) < 0) { - fprintf(stderr, "error initializing libavresample\n"); + is->audio_src.channel_layout = dec_channel_layout; + is->audio_src.channels = av_frame_get_channels(is->frame); + is->audio_src.freq = is->frame->sample_rate; + is->audio_src.fmt = is->frame->format; + } + + if (is->swr_ctx) { + const uint8_t **in = (const uint8_t **)is->frame->extended_data; + uint8_t **out = &is->audio_buf1; + int out_count = (int64_t)wanted_nb_samples * is->audio_tgt.freq / is->frame->sample_rate + 256; + int out_size = av_samples_get_buffer_size(NULL, is->audio_tgt.channels, out_count, is->audio_tgt.fmt, 0); + int len2; + if (wanted_nb_samples != is->frame->nb_samples) { + if (swr_set_compensation(is->swr_ctx, (wanted_nb_samples - is->frame->nb_samples) * is->audio_tgt.freq / is->frame->sample_rate, + wanted_nb_samples * is->audio_tgt.freq / is->frame->sample_rate) < 0) { + fprintf(stderr, "swr_set_compensation() failed\n"); break; } } - is->resample_sample_fmt = is->frame->format; - is->resample_channel_layout = is->frame->channel_layout; - is->resample_sample_rate = is->frame->sample_rate; - } - - if (audio_resample) { - void *tmp_out; - int out_samples, out_size, out_linesize; - int osize = av_get_bytes_per_sample(is->sdl_sample_fmt); - int nb_samples = is->frame->nb_samples; - - out_size = av_samples_get_buffer_size(&out_linesize, - is->sdl_channels, - nb_samples, - is->sdl_sample_fmt, 0); - tmp_out = av_realloc(is->audio_buf1, out_size); - if (!tmp_out) + av_fast_malloc(&is->audio_buf1, &is->audio_buf1_size, out_size); + if (!is->audio_buf1) return AVERROR(ENOMEM); - is->audio_buf1 = tmp_out; - - out_samples = avresample_convert(is->avr, - &is->audio_buf1, - out_linesize, nb_samples, - is->frame->data, - is->frame->linesize[0], - is->frame->nb_samples); - if (out_samples < 0) { - fprintf(stderr, "avresample_convert() failed\n"); + len2 = swr_convert(is->swr_ctx, out, out_count, in, is->frame->nb_samples); + if (len2 < 0) { + fprintf(stderr, "swr_convert() failed\n"); break; } + if (len2 == out_count) { + fprintf(stderr, "warning: audio buffer is probably too small\n"); + swr_init(is->swr_ctx); + } is->audio_buf = is->audio_buf1; - data_size = out_samples * osize * is->sdl_channels; + resampled_data_size = len2 * is->audio_tgt.channels * av_get_bytes_per_sample(is->audio_tgt.fmt); } else { is->audio_buf = is->frame->data[0]; + resampled_data_size = data_size; } - /* if no pts, then compute it */ - pts = is->audio_clock; - *pts_ptr = pts; - n = is->sdl_channels * av_get_bytes_per_sample(is->sdl_sample_fmt); - is->audio_clock += (double)data_size / - (double)(n * is->sdl_sample_rate); + audio_clock0 = is->audio_clock; + /* update the audio clock with the pts */ + if (is->frame->pts != AV_NOPTS_VALUE) { + is->audio_clock = is->frame->pts * av_q2d(tb) + (double) is->frame->nb_samples / is->frame->sample_rate; + is->audio_clock_serial = is->audio_pkt_temp_serial; + } #ifdef DEBUG { static double last_clock; - printf("audio: delay=%0.3f clock=%0.3f pts=%0.3f\n", + printf("audio: delay=%0.3f clock=%0.3f clock0=%0.3f\n", is->audio_clock - last_clock, - is->audio_clock, pts); + is->audio_clock, audio_clock0); last_clock = is->audio_clock; } #endif - return data_size; + return resampled_data_size; } /* free the current packet */ @@ -1964,25 +2316,24 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr) av_free_packet(pkt); memset(pkt_temp, 0, sizeof(*pkt_temp)); - if (is->paused || is->audioq.abort_request) { + if (is->audioq.abort_request) { return -1; } + if (is->audioq.nb_packets == 0) + SDL_CondSignal(is->continue_read_thread); + /* read next packet */ - if ((new_packet = packet_queue_get(&is->audioq, pkt, 1)) < 0) + if ((new_packet = packet_queue_get(&is->audioq, pkt, 1, &is->audio_pkt_temp_serial)) < 0) return -1; if (pkt->data == flush_pkt.data) { avcodec_flush_buffers(dec); flush_complete = 0; + is->audio_buf_frames_pending = 0; } *pkt_temp = *pkt; - - /* if update the audio clock with the pts */ - if (pkt->pts != AV_NOPTS_VALUE) { - is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; - } } } @@ -1991,22 +2342,21 @@ static void sdl_audio_callback(void *opaque, Uint8 *stream, int len) { VideoState *is = opaque; int audio_size, len1; - double pts; + int bytes_per_sec; + int frame_size = av_samples_get_buffer_size(NULL, is->audio_tgt.channels, 1, is->audio_tgt.fmt, 1); audio_callback_time = av_gettime(); while (len > 0) { if (is->audio_buf_index >= is->audio_buf_size) { - audio_size = audio_decode_frame(is, &pts); + audio_size = audio_decode_frame(is); if (audio_size < 0) { /* if error, just output silence */ is->audio_buf = is->silence_buf; - is->audio_buf_size = sizeof(is->silence_buf); + is->audio_buf_size = sizeof(is->silence_buf) / frame_size * frame_size; } else { - if (is->show_audio) + if (is->show_mode != SHOW_MODE_VIDEO) update_sample_display(is, (int16_t *)is->audio_buf, audio_size); - audio_size = synchronize_audio(is, (int16_t *)is->audio_buf, audio_size, - pts); is->audio_buf_size = audio_size; } is->audio_buf_index = 0; @@ -2019,6 +2369,67 @@ static void sdl_audio_callback(void *opaque, Uint8 *stream, int len) stream += len1; is->audio_buf_index += len1; } + bytes_per_sec = is->audio_tgt.freq * is->audio_tgt.channels * av_get_bytes_per_sample(is->audio_tgt.fmt); + is->audio_write_buf_size = is->audio_buf_size - is->audio_buf_index; + /* Let's assume the audio driver that is used by SDL has two periods. */ + is->audio_current_pts = is->audio_clock - (double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / bytes_per_sec; + is->audio_current_pts_drift = is->audio_current_pts - audio_callback_time / 1000000.0; + if (is->audioq.serial == is->audio_clock_serial) + check_external_clock_sync(is, is->audio_current_pts); +} + +static int audio_open(void *opaque, int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate, struct AudioParams *audio_hw_params) +{ + SDL_AudioSpec wanted_spec, spec; + const char *env; + const int next_nb_channels[] = {0, 0, 1, 6, 2, 6, 4, 6}; + + env = SDL_getenv("SDL_AUDIO_CHANNELS"); + if (env) { + wanted_nb_channels = atoi(env); + wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels); + } + if (!wanted_channel_layout || wanted_nb_channels != av_get_channel_layout_nb_channels(wanted_channel_layout)) { + wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels); + wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX; + } + wanted_spec.channels = av_get_channel_layout_nb_channels(wanted_channel_layout); + wanted_spec.freq = wanted_sample_rate; + if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0) { + fprintf(stderr, "Invalid sample rate or channel count!\n"); + return -1; + } + wanted_spec.format = AUDIO_S16SYS; + wanted_spec.silence = 0; + wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; + wanted_spec.callback = sdl_audio_callback; + wanted_spec.userdata = opaque; + while (SDL_OpenAudio(&wanted_spec, &spec) < 0) { + fprintf(stderr, "SDL_OpenAudio (%d channels): %s\n", wanted_spec.channels, SDL_GetError()); + wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)]; + if (!wanted_spec.channels) { + fprintf(stderr, "No more channel combinations to try, audio open failed\n"); + return -1; + } + wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels); + } + if (spec.format != AUDIO_S16SYS) { + fprintf(stderr, "SDL advised audio format %d is not supported!\n", spec.format); + return -1; + } + if (spec.channels != wanted_spec.channels) { + wanted_channel_layout = av_get_default_channel_layout(spec.channels); + if (!wanted_channel_layout) { + fprintf(stderr, "SDL advised channel count %d is not supported!\n", spec.channels); + return -1; + } + } + + audio_hw_params->fmt = AV_SAMPLE_FMT_S16; + audio_hw_params->freq = spec.freq; + audio_hw_params->channel_layout = wanted_channel_layout; + audio_hw_params->channels = spec.channels; + return spec.size; } /* open a given stream. Return 0 if OK */ @@ -2027,78 +2438,89 @@ static int stream_component_open(VideoState *is, int stream_index) AVFormatContext *ic = is->ic; AVCodecContext *avctx; AVCodec *codec; - SDL_AudioSpec wanted_spec, spec; + const char *forced_codec_name = NULL; AVDictionary *opts; AVDictionaryEntry *t = NULL; + int sample_rate, nb_channels; + int64_t channel_layout; + int ret; if (stream_index < 0 || stream_index >= ic->nb_streams) return -1; avctx = ic->streams[stream_index]->codec; - opts = filter_codec_opts(codec_opts, avctx->codec_id, ic, ic->streams[stream_index], NULL); - codec = avcodec_find_decoder(avctx->codec_id); - avctx->debug_mv = debug_mv; + + switch(avctx->codec_type){ + case AVMEDIA_TYPE_AUDIO : is->last_audio_stream = stream_index; forced_codec_name = audio_codec_name; break; + case AVMEDIA_TYPE_SUBTITLE: is->last_subtitle_stream = stream_index; forced_codec_name = subtitle_codec_name; break; + case AVMEDIA_TYPE_VIDEO : is->last_video_stream = stream_index; forced_codec_name = video_codec_name; break; + } + if (forced_codec_name) + codec = avcodec_find_decoder_by_name(forced_codec_name); + if (!codec) { + if (forced_codec_name) fprintf(stderr, "No codec could be found with name '%s'\n", forced_codec_name); + else fprintf(stderr, "No codec could be found with id %d\n", avctx->codec_id); + return -1; + } + + avctx->codec_id = codec->id; avctx->workaround_bugs = workaround_bugs; + avctx->lowres = lowres; + if(avctx->lowres > codec->max_lowres){ + av_log(avctx, AV_LOG_WARNING, "The maximum value for lowres supported by the decoder is %d\n", + codec->max_lowres); + avctx->lowres= codec->max_lowres; + } avctx->idct_algo = idct; - avctx->skip_frame = skip_frame; - avctx->skip_idct = skip_idct; - avctx->skip_loop_filter = skip_loop_filter; avctx->error_concealment = error_concealment; + if(avctx->lowres) avctx->flags |= CODEC_FLAG_EMU_EDGE; if (fast) avctx->flags2 |= CODEC_FLAG2_FAST; + if(codec->capabilities & CODEC_CAP_DR1) + avctx->flags |= CODEC_FLAG_EMU_EDGE; + opts = filter_codec_opts(codec_opts, avctx->codec_id, ic, ic->streams[stream_index], codec); if (!av_dict_get(opts, "threads", NULL, 0)) av_dict_set(&opts, "threads", "auto", 0); - if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) + if (avctx->codec_type == AVMEDIA_TYPE_VIDEO || avctx->codec_type == AVMEDIA_TYPE_AUDIO) av_dict_set(&opts, "refcounted_frames", "1", 0); - if (!codec || - avcodec_open2(avctx, codec, &opts) < 0) + if (avcodec_open2(avctx, codec, &opts) < 0) return -1; if ((t = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) { av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key); return AVERROR_OPTION_NOT_FOUND; } - /* prepare audio output */ - if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) { - is->sdl_sample_rate = avctx->sample_rate; - - if (!avctx->channel_layout) - avctx->channel_layout = av_get_default_channel_layout(avctx->channels); - if (!avctx->channel_layout) { - fprintf(stderr, "unable to guess channel layout\n"); - return -1; - } - if (avctx->channels == 1) - is->sdl_channel_layout = AV_CH_LAYOUT_MONO; - else - is->sdl_channel_layout = AV_CH_LAYOUT_STEREO; - is->sdl_channels = av_get_channel_layout_nb_channels(is->sdl_channel_layout); - - wanted_spec.format = AUDIO_S16SYS; - wanted_spec.freq = is->sdl_sample_rate; - wanted_spec.channels = is->sdl_channels; - wanted_spec.silence = 0; - wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; - wanted_spec.callback = sdl_audio_callback; - wanted_spec.userdata = is; - if (SDL_OpenAudio(&wanted_spec, &spec) < 0) { - fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError()); - return -1; - } - is->audio_hw_buf_size = spec.size; - is->sdl_sample_fmt = AV_SAMPLE_FMT_S16; - is->resample_sample_fmt = is->sdl_sample_fmt; - is->resample_channel_layout = avctx->channel_layout; - is->resample_sample_rate = avctx->sample_rate; - } - ic->streams[stream_index]->discard = AVDISCARD_DEFAULT; switch (avctx->codec_type) { case AVMEDIA_TYPE_AUDIO: - is->audio_stream = stream_index; - is->audio_st = ic->streams[stream_index]; +#if CONFIG_AVFILTER + { + AVFilterLink *link; + + is->audio_filter_src.freq = avctx->sample_rate; + is->audio_filter_src.channels = avctx->channels; + is->audio_filter_src.channel_layout = get_valid_channel_layout(avctx->channel_layout, avctx->channels); + is->audio_filter_src.fmt = avctx->sample_fmt; + if ((ret = configure_audio_filters(is, afilters, 0)) < 0) + return ret; + link = is->out_audio_filter->inputs[0]; + sample_rate = link->sample_rate; + nb_channels = link->channels; + channel_layout = link->channel_layout; + } +#else + sample_rate = avctx->sample_rate; + nb_channels = avctx->channels; + channel_layout = avctx->channel_layout; +#endif + + /* prepare audio output */ + if ((ret = audio_open(is, channel_layout, nb_channels, sample_rate, &is->audio_tgt)) < 0) + return ret; + is->audio_hw_buf_size = ret; + is->audio_src = is->audio_tgt; is->audio_buf_size = 0; is->audio_buf_index = 0; @@ -2107,23 +2529,29 @@ static int stream_component_open(VideoState *is, int stream_index) is->audio_diff_avg_count = 0; /* since we do not have a precise anough audio fifo fullness, we correct audio sync only if larger than this threshold */ - is->audio_diff_threshold = 2.0 * SDL_AUDIO_BUFFER_SIZE / avctx->sample_rate; + is->audio_diff_threshold = 2.0 * is->audio_hw_buf_size / av_samples_get_buffer_size(NULL, is->audio_tgt.channels, is->audio_tgt.freq, is->audio_tgt.fmt, 1); memset(&is->audio_pkt, 0, sizeof(is->audio_pkt)); - packet_queue_init(&is->audioq); + memset(&is->audio_pkt_temp, 0, sizeof(is->audio_pkt_temp)); + + is->audio_stream = stream_index; + is->audio_st = ic->streams[stream_index]; + + packet_queue_start(&is->audioq); SDL_PauseAudio(0); break; case AVMEDIA_TYPE_VIDEO: is->video_stream = stream_index; is->video_st = ic->streams[stream_index]; - packet_queue_init(&is->videoq); + packet_queue_start(&is->videoq); is->video_tid = SDL_CreateThread(video_thread, is); + is->queue_attachments_req = 1; break; case AVMEDIA_TYPE_SUBTITLE: is->subtitle_stream = stream_index; is->subtitle_st = ic->streams[stream_index]; - packet_queue_init(&is->subtitleq); + packet_queue_start(&is->subtitleq); is->subtitle_tid = SDL_CreateThread(subtitle_thread, is); break; @@ -2148,13 +2576,13 @@ static void stream_component_close(VideoState *is, int stream_index) SDL_CloseAudio(); - packet_queue_end(&is->audioq); + packet_queue_flush(&is->audioq); av_free_packet(&is->audio_pkt); - if (is->avr) - avresample_free(&is->avr); + swr_free(&is->swr_ctx); av_freep(&is->audio_buf1); + is->audio_buf1_size = 0; is->audio_buf = NULL; - avcodec_free_frame(&is->frame); + av_frame_free(&is->frame); if (is->rdft) { av_rdft_end(is->rdft); @@ -2162,6 +2590,9 @@ static void stream_component_close(VideoState *is, int stream_index) is->rdft = NULL; is->rdft_bits = 0; } +#if CONFIG_AVFILTER + avfilter_graph_free(&is->agraph); +#endif break; case AVMEDIA_TYPE_VIDEO: packet_queue_abort(&is->videoq); @@ -2174,7 +2605,7 @@ static void stream_component_close(VideoState *is, int stream_index) SDL_WaitThread(is->video_tid, NULL); - packet_queue_end(&is->videoq); + packet_queue_flush(&is->videoq); break; case AVMEDIA_TYPE_SUBTITLE: packet_queue_abort(&is->subtitleq); @@ -2189,7 +2620,7 @@ static void stream_component_close(VideoState *is, int stream_index) SDL_WaitThread(is->subtitle_tid, NULL); - packet_queue_end(&is->subtitleq); + packet_queue_flush(&is->subtitleq); break; default: break; @@ -2215,17 +2646,30 @@ static void stream_component_close(VideoState *is, int stream_index) } } -/* since we have only one decoding thread, we can use a global - variable instead of a thread local variable */ -static VideoState *global_video_state; - static int decode_interrupt_cb(void *ctx) { - return global_video_state && global_video_state->abort_request; + VideoState *is = ctx; + return is->abort_request; +} + +static int is_realtime(AVFormatContext *s) +{ + if( !strcmp(s->iformat->name, "rtp") + || !strcmp(s->iformat->name, "rtsp") + || !strcmp(s->iformat->name, "sdp") + ) + return 1; + + if(s->pb && ( !strncmp(s->filename, "rtp:", 4) + || !strncmp(s->filename, "udp:", 4) + ) + ) + return 1; + return 0; } /* this thread gets the stream from the disk or the network */ -static int decode_thread(void *arg) +static int read_thread(void *arg) { VideoState *is = arg; AVFormatContext *ic = NULL; @@ -2237,16 +2681,16 @@ static int decode_thread(void *arg) AVDictionaryEntry *t; AVDictionary **opts; int orig_nb_streams; + SDL_mutex *wait_mutex = SDL_CreateMutex(); memset(st_index, -1, sizeof(st_index)); - is->video_stream = -1; - is->audio_stream = -1; - is->subtitle_stream = -1; - - global_video_state = is; + is->last_video_stream = is->video_stream = -1; + is->last_audio_stream = is->audio_stream = -1; + is->last_subtitle_stream = is->subtitle_stream = -1; ic = avformat_alloc_context(); ic->interrupt_callback.callback = decode_interrupt_cb; + ic->interrupt_callback.opaque = is; err = avformat_open_input(&ic, is->filename, is->iformat, &format_opts); if (err < 0) { print_error(is->filename, err); @@ -2277,10 +2721,12 @@ static int decode_thread(void *arg) av_freep(&opts); if (ic->pb) - ic->pb->eof_reached = 0; // FIXME hack, avplay maybe should not use url_feof() to test for the end + ic->pb->eof_reached = 0; // FIXME hack, ffplay maybe should not use url_feof() to test for the end if (seek_by_bytes < 0) - seek_by_bytes = !!(ic->iformat->flags & AVFMT_TS_DISCONT); + seek_by_bytes = !!(ic->iformat->flags & AVFMT_TS_DISCONT) && strcmp("ogg", ic->iformat->name); + + is->max_frame_duration = (ic->iformat->flags & AVFMT_TS_DISCONT) ? 10.0 : 3600.0; /* if seeking requested, we execute it */ if (start_time != AV_NOPTS_VALUE) { @@ -2297,6 +2743,8 @@ static int decode_thread(void *arg) } } + is->realtime = is_realtime(ic); + for (i = 0; i < ic->nb_streams; i++) ic->streams[i]->discard = AVDISCARD_ALL; if (!video_disable) @@ -2309,7 +2757,7 @@ static int decode_thread(void *arg) wanted_stream[AVMEDIA_TYPE_AUDIO], st_index[AVMEDIA_TYPE_VIDEO], NULL, 0); - if (!video_disable) + if (!video_disable && !subtitle_disable) st_index[AVMEDIA_TYPE_SUBTITLE] = av_find_best_stream(ic, AVMEDIA_TYPE_SUBTITLE, wanted_stream[AVMEDIA_TYPE_SUBTITLE], @@ -2321,6 +2769,8 @@ static int decode_thread(void *arg) av_dump_format(ic, 0, is->filename, 0); } + is->show_mode = show_mode; + /* open the streams */ if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) { stream_component_open(is, st_index[AVMEDIA_TYPE_AUDIO]); @@ -2330,11 +2780,8 @@ static int decode_thread(void *arg) if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) { ret = stream_component_open(is, st_index[AVMEDIA_TYPE_VIDEO]); } - is->refresh_tid = SDL_CreateThread(refresh_thread, is); - if (ret < 0) { - if (!display_disable) - is->show_audio = 2; - } + if (is->show_mode == SHOW_MODE_NONE) + is->show_mode = ret >= 0 ? SHOW_MODE_VIDEO : SHOW_MODE_RDFT; if (st_index[AVMEDIA_TYPE_SUBTITLE] >= 0) { stream_component_open(is, st_index[AVMEDIA_TYPE_SUBTITLE]); @@ -2346,6 +2793,9 @@ static int decode_thread(void *arg) goto fail; } + if (infinite_buffer < 0 && is->realtime) + infinite_buffer = 1; + for (;;) { if (is->abort_request) break; @@ -2356,8 +2806,10 @@ static int decode_thread(void *arg) else av_read_play(ic); } -#if CONFIG_RTSP_DEMUXER - if (is->paused && !strcmp(ic->iformat->name, "rtsp")) { +#if CONFIG_RTSP_DEMUXER || CONFIG_MMSH_PROTOCOL + if (is->paused && + (!strcmp(ic->iformat->name, "rtsp") || + (ic->pb && !strncmp(input_filename, "mmsh:", 5)))) { /* wait 10 ms to avoid trying to get another packet */ /* XXX: horrible */ SDL_Delay(10); @@ -2387,19 +2839,39 @@ static int decode_thread(void *arg) packet_queue_flush(&is->videoq); packet_queue_put(&is->videoq, &flush_pkt); } + if (is->seek_flags & AVSEEK_FLAG_BYTE) { + update_external_clock_pts(is, NAN); + } else { + update_external_clock_pts(is, seek_target / (double)AV_TIME_BASE); + } } is->seek_req = 0; + is->queue_attachments_req = 1; eof = 0; + if (is->paused) + step_to_next_frame(is); + } + if (is->queue_attachments_req) { + if (is->video_st && is->video_st->disposition & AV_DISPOSITION_ATTACHED_PIC) { + AVPacket copy; + if ((ret = av_copy_packet(©, &is->video_st->attached_pic)) < 0) + goto fail; + packet_queue_put(&is->videoq, ©); + } + is->queue_attachments_req = 0; } /* if the queue are full, no need to read more */ - if (!infinite_buffer && + if (infinite_buffer<1 && (is->audioq.size + is->videoq.size + is->subtitleq.size > MAX_QUEUE_SIZE - || ( (is->audioq .size > MIN_AUDIOQ_SIZE || is->audio_stream < 0) - && (is->videoq .nb_packets > MIN_FRAMES || is->video_stream < 0) - && (is->subtitleq.nb_packets > MIN_FRAMES || is->subtitle_stream < 0)))) { + || ( (is->audioq .nb_packets > MIN_FRAMES || is->audio_stream < 0 || is->audioq.abort_request) + && (is->videoq .nb_packets > MIN_FRAMES || is->video_stream < 0 || is->videoq.abort_request + || (is->video_st->disposition & AV_DISPOSITION_ATTACHED_PIC)) + && (is->subtitleq.nb_packets > MIN_FRAMES || is->subtitle_stream < 0 || is->subtitleq.abort_request)))) { /* wait 10 ms */ - SDL_Delay(10); + SDL_LockMutex(wait_mutex); + SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10); + SDL_UnlockMutex(wait_mutex); continue; } if (eof) { @@ -2421,21 +2893,24 @@ static int decode_thread(void *arg) SDL_Delay(10); if (is->audioq.size + is->videoq.size + is->subtitleq.size == 0) { if (loop != 1 && (!loop || --loop)) { - stream_seek(cur_stream, start_time != AV_NOPTS_VALUE ? start_time : 0, 0, 0); + stream_seek(is, start_time != AV_NOPTS_VALUE ? start_time : 0, 0, 0); } else if (autoexit) { ret = AVERROR_EOF; goto fail; } } + eof=0; continue; } ret = av_read_frame(ic, pkt); if (ret < 0) { - if (ret == AVERROR_EOF || (ic->pb && ic->pb->eof_reached)) + if (ret == AVERROR_EOF || url_feof(ic->pb)) eof = 1; if (ic->pb && ic->pb->error) break; - SDL_Delay(100); /* wait for user event */ + SDL_LockMutex(wait_mutex); + SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10); + SDL_UnlockMutex(wait_mutex); continue; } /* check if packet is in play range specified by user, then queue, otherwise discard */ @@ -2446,7 +2921,8 @@ static int decode_thread(void *arg) <= ((double)duration / 1000000); if (pkt->stream_index == is->audio_stream && pkt_in_play_range) { packet_queue_put(&is->audioq, pkt); - } else if (pkt->stream_index == is->video_stream && pkt_in_play_range) { + } else if (pkt->stream_index == is->video_stream && pkt_in_play_range + && !(is->video_st->disposition & AV_DISPOSITION_ATTACHED_PIC)) { packet_queue_put(&is->videoq, pkt); } else if (pkt->stream_index == is->subtitle_stream && pkt_in_play_range) { packet_queue_put(&is->subtitleq, pkt); @@ -2461,9 +2937,6 @@ static int decode_thread(void *arg) ret = 0; fail: - /* disable interrupting */ - global_video_state = NULL; - /* close each stream */ if (is->audio_stream >= 0) stream_component_close(is, is->audio_stream); @@ -2482,6 +2955,7 @@ static int decode_thread(void *arg) event.user.data1 = is; SDL_PushEvent(&event); } + SDL_DestroyMutex(wait_mutex); return 0; } @@ -2504,9 +2978,22 @@ static VideoState *stream_open(const char *filename, AVInputFormat *iformat) is->subpq_mutex = SDL_CreateMutex(); is->subpq_cond = SDL_CreateCond(); + packet_queue_init(&is->videoq); + packet_queue_init(&is->audioq); + packet_queue_init(&is->subtitleq); + + is->continue_read_thread = SDL_CreateCond(); + + update_external_clock_pts(is, NAN); + update_external_clock_speed(is, 1.0); + is->audio_current_pts_drift = -av_gettime() / 1000000.0; + is->video_current_pts_drift = is->audio_current_pts_drift; + is->audio_clock_serial = -1; + is->video_clock_serial = -1; + is->audio_last_serial = -1; is->av_sync_type = av_sync_type; - is->parse_tid = SDL_CreateThread(decode_thread, is); - if (!is->parse_tid) { + is->read_tid = SDL_CreateThread(read_thread, is); + if (!is->read_tid) { av_free(is); return NULL; } @@ -2517,16 +3004,19 @@ static void stream_cycle_channel(VideoState *is, int codec_type) { AVFormatContext *ic = is->ic; int start_index, stream_index; + int old_index; AVStream *st; - if (codec_type == AVMEDIA_TYPE_VIDEO) - start_index = is->video_stream; - else if (codec_type == AVMEDIA_TYPE_AUDIO) - start_index = is->audio_stream; - else - start_index = is->subtitle_stream; - if (start_index < (codec_type == AVMEDIA_TYPE_SUBTITLE ? -1 : 0)) - return; + if (codec_type == AVMEDIA_TYPE_VIDEO) { + start_index = is->last_video_stream; + old_index = is->video_stream; + } else if (codec_type == AVMEDIA_TYPE_AUDIO) { + start_index = is->last_audio_stream; + old_index = is->audio_stream; + } else { + start_index = is->last_subtitle_stream; + old_index = is->subtitle_stream; + } stream_index = start_index; for (;;) { if (++stream_index >= is->ic->nb_streams) @@ -2534,9 +3024,12 @@ static void stream_cycle_channel(VideoState *is, int codec_type) if (codec_type == AVMEDIA_TYPE_SUBTITLE) { stream_index = -1; + is->last_subtitle_stream = -1; goto the_end; - } else - stream_index = 0; + } + if (start_index == -1) + return; + stream_index = 0; } if (stream_index == start_index) return; @@ -2558,97 +3051,105 @@ static void stream_cycle_channel(VideoState *is, int codec_type) } } the_end: - stream_component_close(is, start_index); + stream_component_close(is, old_index); stream_component_open(is, stream_index); } -static void toggle_full_screen(void) +static void toggle_full_screen(VideoState *is) { #if defined(__APPLE__) && SDL_VERSION_ATLEAST(1, 2, 14) - /* OS X needs to empty the picture_queue */ + /* OS X needs to reallocate the SDL overlays */ int i; for (i = 0; i < VIDEO_PICTURE_QUEUE_SIZE; i++) - cur_stream->pictq[i].reallocate = 1; + is->pictq[i].reallocate = 1; #endif is_full_screen = !is_full_screen; - video_open(cur_stream); -} - -static void toggle_pause(void) -{ - if (cur_stream) - stream_pause(cur_stream); - step = 0; + video_open(is, 1, NULL); } -static void step_to_next_frame(void) +static void toggle_audio_display(VideoState *is) { - if (cur_stream) { - /* if the stream is paused unpause it, then step */ - if (cur_stream->paused) - stream_pause(cur_stream); + int bgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0x00); + int next = is->show_mode; + do { + next = (next + 1) % SHOW_MODE_NB; + } while (next != is->show_mode && (next == SHOW_MODE_VIDEO && !is->video_st || next != SHOW_MODE_VIDEO && !is->audio_st)); + if (is->show_mode != next) { + fill_rectangle(screen, + is->xleft, is->ytop, is->width, is->height, + bgcolor, 1); + is->force_refresh = 1; + is->show_mode = next; } - step = 1; } -static void toggle_audio_display(void) -{ - if (cur_stream) { - int bgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0x00); - cur_stream->show_audio = (cur_stream->show_audio + 1) % 3; - fill_rectangle(screen, - cur_stream->xleft, cur_stream->ytop, cur_stream->width, cur_stream->height, - bgcolor); - SDL_UpdateRect(screen, cur_stream->xleft, cur_stream->ytop, cur_stream->width, cur_stream->height); +static void refresh_loop_wait_event(VideoState *is, SDL_Event *event) { + double remaining_time = 0.0; + SDL_PumpEvents(); + while (!SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) { + if (!cursor_hidden && av_gettime() - cursor_last_shown > CURSOR_HIDE_DELAY) { + SDL_ShowCursor(0); + cursor_hidden = 1; + } + if (remaining_time > 0.0) + av_usleep((int64_t)(remaining_time * 1000000.0)); + remaining_time = REFRESH_RATE; + if (is->show_mode != SHOW_MODE_NONE && (!is->paused || is->force_refresh)) + video_refresh(is, &remaining_time); + SDL_PumpEvents(); } } /* handle an event sent by the GUI */ -static void event_loop(void) +static void event_loop(VideoState *cur_stream) { SDL_Event event; double incr, pos, frac; for (;;) { double x; - SDL_WaitEvent(&event); + refresh_loop_wait_event(cur_stream, &event); switch (event.type) { case SDL_KEYDOWN: if (exit_on_keydown) { - do_exit(); + do_exit(cur_stream); break; } switch (event.key.keysym.sym) { case SDLK_ESCAPE: case SDLK_q: - do_exit(); + do_exit(cur_stream); break; case SDLK_f: - toggle_full_screen(); + toggle_full_screen(cur_stream); + cur_stream->force_refresh = 1; break; case SDLK_p: case SDLK_SPACE: - toggle_pause(); + toggle_pause(cur_stream); break; case SDLK_s: // S: Step to next frame - step_to_next_frame(); + step_to_next_frame(cur_stream); break; case SDLK_a: - if (cur_stream) - stream_cycle_channel(cur_stream, AVMEDIA_TYPE_AUDIO); + stream_cycle_channel(cur_stream, AVMEDIA_TYPE_AUDIO); break; case SDLK_v: - if (cur_stream) - stream_cycle_channel(cur_stream, AVMEDIA_TYPE_VIDEO); + stream_cycle_channel(cur_stream, AVMEDIA_TYPE_VIDEO); break; case SDLK_t: - if (cur_stream) - stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE); + stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE); break; case SDLK_w: - toggle_audio_display(); + toggle_audio_display(cur_stream); break; + case SDLK_PAGEUP: + incr = 600.0; + goto do_seek; + case SDLK_PAGEDOWN: + incr = -600.0; + goto do_seek; case SDLK_LEFT: incr = -10.0; goto do_seek; @@ -2661,7 +3162,6 @@ static void event_loop(void) case SDLK_DOWN: incr = -60.0; do_seek: - if (cur_stream) { if (seek_by_bytes) { if (cur_stream->video_stream >= 0 && cur_stream->video_current_pos >= 0) { pos = cur_stream->video_current_pos; @@ -2677,21 +3177,32 @@ static void event_loop(void) stream_seek(cur_stream, pos, incr, 1); } else { pos = get_master_clock(cur_stream); + if (isnan(pos)) + pos = (double)cur_stream->seek_pos / AV_TIME_BASE; pos += incr; + if (cur_stream->ic->start_time != AV_NOPTS_VALUE && pos < cur_stream->ic->start_time / (double)AV_TIME_BASE) + pos = cur_stream->ic->start_time / (double)AV_TIME_BASE; stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE), (int64_t)(incr * AV_TIME_BASE), 0); } - } break; default: break; } break; + case SDL_VIDEOEXPOSE: + cur_stream->force_refresh = 1; + break; case SDL_MOUSEBUTTONDOWN: if (exit_on_mousedown) { - do_exit(); + do_exit(cur_stream); break; } case SDL_MOUSEMOTION: + if (cursor_hidden) { + SDL_ShowCursor(1); + cursor_hidden = 0; + } + cursor_last_shown = av_gettime(); if (event.type == SDL_MOUSEBUTTONDOWN) { x = event.button.x; } else { @@ -2699,7 +3210,6 @@ static void event_loop(void) break; x = event.motion.x; } - if (cur_stream) { if (seek_by_bytes || cur_stream->ic->duration <= 0) { uint64_t size = avio_size(cur_stream->ic->pb); stream_seek(cur_stream, size*x/cur_stream->width, 0, 1); @@ -2723,28 +3233,21 @@ static void event_loop(void) ts += cur_stream->ic->start_time; stream_seek(cur_stream, ts, 0, 0); } - } break; case SDL_VIDEORESIZE: - if (cur_stream) { screen = SDL_SetVideoMode(event.resize.w, event.resize.h, 0, SDL_HWSURFACE|SDL_RESIZABLE|SDL_ASYNCBLIT|SDL_HWACCEL); screen_width = cur_stream->width = event.resize.w; screen_height = cur_stream->height = event.resize.h; - } + cur_stream->force_refresh = 1; break; case SDL_QUIT: case FF_QUIT_EVENT: - do_exit(); + do_exit(cur_stream); break; case FF_ALLOC_EVENT: - video_open(event.user.data1); alloc_picture(event.user.data1); break; - case FF_REFRESH_EVENT: - video_refresh_timer(event.user.data1); - cur_stream->refresh = 0; - break; default: break; } @@ -2753,9 +3256,8 @@ static void event_loop(void) static int opt_frame_size(void *optctx, const char *opt, const char *arg) { - av_log(NULL, AV_LOG_ERROR, - "Option '%s' has been removed, use private format options instead\n", opt); - return AVERROR(EINVAL); + av_log(NULL, AV_LOG_WARNING, "Option -s is deprecated, use -video_size.\n"); + return opt_default(NULL, "video_size", arg); } static int opt_width(void *optctx, const char *opt, const char *arg) @@ -2782,9 +3284,8 @@ static int opt_format(void *optctx, const char *opt, const char *arg) static int opt_frame_pix_fmt(void *optctx, const char *opt, const char *arg) { - av_log(NULL, AV_LOG_ERROR, - "Option '%s' has been removed, use private format options instead\n", opt); - return AVERROR(EINVAL); + av_log(NULL, AV_LOG_WARNING, "Option -pix_fmt is deprecated, use -pixel_format.\n"); + return opt_default(NULL, "pixel_format", arg); } static int opt_sync(void *optctx, const char *opt, const char *arg) @@ -2814,12 +3315,49 @@ static int opt_duration(void *optctx, const char *opt, const char *arg) return 0; } -static int opt_vismv(void *optctx, const char *opt, const char *arg) +static int opt_show_mode(void *optctx, const char *opt, const char *arg) { - debug_mv = parse_number_or_die(opt, arg, OPT_INT64, INT_MIN, INT_MAX); + show_mode = !strcmp(arg, "video") ? SHOW_MODE_VIDEO : + !strcmp(arg, "waves") ? SHOW_MODE_WAVES : + !strcmp(arg, "rdft" ) ? SHOW_MODE_RDFT : + parse_number_or_die(opt, arg, OPT_INT, 0, SHOW_MODE_NB-1); return 0; } +static void opt_input_file(void *optctx, const char *filename) +{ + if (input_filename) { + fprintf(stderr, "Argument '%s' provided as input filename, but '%s' was already specified.\n", + filename, input_filename); + exit(1); + } + if (!strcmp(filename, "-")) + filename = "pipe:"; + input_filename = filename; +} + +static int opt_codec(void *optctx, const char *opt, const char *arg) +{ + const char *spec = strchr(opt, ':'); + if (!spec) { + fprintf(stderr, "No media specifier was specified in '%s' in option '%s'\n", + arg, opt); + return AVERROR(EINVAL); + } + spec++; + switch (spec[0]) { + case 'a' : audio_codec_name = arg; break; + case 's' : subtitle_codec_name = arg; break; + case 'v' : video_codec_name = arg; break; + default: + fprintf(stderr, "Invalid media specifier '%s' in option '%s'\n", spec, opt); + return AVERROR(EINVAL); + } + return 0; +} + +static int dummy; + static const OptionDef options[] = { #include "cmdutils_common_opts.h" { "x", HAS_ARG, { .func_arg = opt_width }, "force displayed width", "width" }, @@ -2828,6 +3366,7 @@ static const OptionDef options[] = { { "fs", OPT_BOOL, { &is_full_screen }, "force full screen" }, { "an", OPT_BOOL, { &audio_disable }, "disable audio" }, { "vn", OPT_BOOL, { &video_disable }, "disable video" }, + { "sn", OPT_BOOL, { &subtitle_disable }, "disable subtitling" }, { "ast", OPT_INT | HAS_ARG | OPT_EXPERT, { &wanted_stream[AVMEDIA_TYPE_AUDIO] }, "select desired audio stream", "stream_number" }, { "vst", OPT_INT | HAS_ARG | OPT_EXPERT, { &wanted_stream[AVMEDIA_TYPE_VIDEO] }, "select desired video stream", "stream_number" }, { "sst", OPT_INT | HAS_ARG | OPT_EXPERT, { &wanted_stream[AVMEDIA_TYPE_SUBTITLE] }, "select desired subtitle stream", "stream_number" }, @@ -2839,13 +3378,10 @@ static const OptionDef options[] = { { "pix_fmt", HAS_ARG | OPT_EXPERT | OPT_VIDEO, { .func_arg = opt_frame_pix_fmt }, "set pixel format", "format" }, { "stats", OPT_BOOL | OPT_EXPERT, { &show_status }, "show status", "" }, { "bug", OPT_INT | HAS_ARG | OPT_EXPERT, { &workaround_bugs }, "workaround bugs", "" }, - { "vismv", HAS_ARG | OPT_EXPERT, { .func_arg = opt_vismv }, "visualize motion vectors", "" }, { "fast", OPT_BOOL | OPT_EXPERT, { &fast }, "non spec compliant optimizations", "" }, { "genpts", OPT_BOOL | OPT_EXPERT, { &genpts }, "generate pts", "" }, { "drp", OPT_INT | HAS_ARG | OPT_EXPERT, { &decoder_reorder_pts }, "let decoder reorder pts 0=off 1=on -1=auto", ""}, - { "skiploop", OPT_INT | HAS_ARG | OPT_EXPERT, { &skip_loop_filter }, "", "" }, - { "skipframe", OPT_INT | HAS_ARG | OPT_EXPERT, { &skip_frame }, "", "" }, - { "skipidct", OPT_INT | HAS_ARG | OPT_EXPERT, { &skip_idct }, "", "" }, + { "lowres", OPT_INT | HAS_ARG | OPT_EXPERT, { &lowres }, "", "" }, { "idct", OPT_INT | HAS_ARG | OPT_EXPERT, { &idct }, "set idct algo", "algo" }, { "ec", OPT_INT | HAS_ARG | OPT_EXPERT, { &error_concealment }, "set error concealment options", "bit_mask" }, { "sync", HAS_ARG | OPT_EXPERT, { .func_arg = opt_sync }, "set audio-video sync. type (type=audio/video/ext)", "type" }, @@ -2857,19 +3393,25 @@ static const OptionDef options[] = { { "infbuf", OPT_BOOL | OPT_EXPERT, { &infinite_buffer }, "don't limit the input buffer size (useful with realtime streams)", "" }, { "window_title", OPT_STRING | HAS_ARG, { &window_title }, "set window title", "window title" }, #if CONFIG_AVFILTER - { "vf", OPT_STRING | HAS_ARG, { &vfilters }, "video filters", "filter list" }, + { "vf", OPT_STRING | HAS_ARG, { &vfilters }, "set video filters", "filter_graph" }, + { "af", OPT_STRING | HAS_ARG, { &afilters }, "set audio filters", "filter_graph" }, #endif { "rdftspeed", OPT_INT | HAS_ARG| OPT_AUDIO | OPT_EXPERT, { &rdftspeed }, "rdft speed", "msecs" }, - { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, { opt_default }, "generic catch all option", "" }, - { "i", 0, { NULL }, "avconv compatibility dummy option", ""}, + { "showmode", HAS_ARG, { .func_arg = opt_show_mode}, "select show mode (0 = video, 1 = waves, 2 = RDFT)", "mode" }, + { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, { .func_arg = opt_default }, "generic catch all option", "" }, + { "i", OPT_BOOL, { &dummy}, "read specified file", "input_file"}, + { "codec", HAS_ARG, { .func_arg = opt_codec}, "force decoder", "decoder_name" }, + { "acodec", HAS_ARG | OPT_STRING | OPT_EXPERT, { &audio_codec_name }, "force audio decoder", "decoder_name" }, + { "scodec", HAS_ARG | OPT_STRING | OPT_EXPERT, { &subtitle_codec_name }, "force subtitle decoder", "decoder_name" }, + { "vcodec", HAS_ARG | OPT_STRING | OPT_EXPERT, { &video_codec_name }, "force video decoder", "decoder_name" }, { NULL, }, }; static void show_usage(void) { - printf("Simple media player\n"); - printf("usage: %s [options] input_file\n", program_name); - printf("\n"); + av_log(NULL, AV_LOG_INFO, "Simple media player\n"); + av_log(NULL, AV_LOG_INFO, "usage: %s [options] input_file\n", program_name); + av_log(NULL, AV_LOG_INFO, "\n"); } void show_help_default(const char *opt, const char *arg) @@ -2883,6 +3425,8 @@ void show_help_default(const char *opt, const char *arg) show_help_children(avformat_get_class(), AV_OPT_FLAG_DECODING_PARAM); #if !CONFIG_AVFILTER show_help_children(sws_get_class(), AV_OPT_FLAG_ENCODING_PARAM); +#else + show_help_children(avfilter_get_class(), AV_OPT_FLAG_FILTERING_PARAM); #endif printf("\nWhile playing:\n" "q, ESC quit\n" @@ -2895,26 +3439,36 @@ void show_help_default(const char *opt, const char *arg) "s activate frame-step mode\n" "left/right seek backward/forward 10 seconds\n" "down/up seek backward/forward 1 minute\n" + "page down/page up seek backward/forward 10 minutes\n" "mouse click seek to percentage in file corresponding to fraction of width\n" ); } -static void opt_input_file(void *optctx, const char *filename) +static int lockmgr(void **mtx, enum AVLockOp op) { - if (input_filename) { - fprintf(stderr, "Argument '%s' provided as input filename, but '%s' was already specified.\n", - filename, input_filename); - exit(1); - } - if (!strcmp(filename, "-")) - filename = "pipe:"; - input_filename = filename; + switch(op) { + case AV_LOCK_CREATE: + *mtx = SDL_CreateMutex(); + if(!*mtx) + return 1; + return 0; + case AV_LOCK_OBTAIN: + return !!SDL_LockMutex(*mtx); + case AV_LOCK_RELEASE: + return !!SDL_UnlockMutex(*mtx); + case AV_LOCK_DESTROY: + SDL_DestroyMutex(*mtx); + return 0; + } + return 1; } /* Called from the main */ int main(int argc, char **argv) { int flags; + VideoState *is; + char dummy_videodriver[] = "SDL_VIDEODRIVER=dummy"; av_log_set_flags(AV_LOG_SKIP_REPEATED); parse_loglevel(argc, argv, options); @@ -2932,7 +3486,10 @@ int main(int argc, char **argv) init_opts(); - show_banner(); + signal(SIGINT , sigterm_handler); /* Interrupt (ANSI). */ + signal(SIGTERM, sigterm_handler); /* Termination (ANSI). */ + + show_banner(argc, argv, options); parse_options(NULL, argc, argv, options, opt_input_file); @@ -2947,11 +3504,16 @@ int main(int argc, char **argv) video_disable = 1; } flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER; + if (audio_disable) + flags &= ~SDL_INIT_AUDIO; + if (display_disable) + SDL_putenv(dummy_videodriver); /* For the event queue, we always need a video driver. */ #if !defined(__MINGW32__) && !defined(__APPLE__) flags |= SDL_INIT_EVENTTHREAD; /* Not supported on Windows or Mac OS X */ #endif if (SDL_Init (flags)) { fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError()); + fprintf(stderr, "(Did you set the DISPLAY variable?)\n"); exit(1); } @@ -2965,12 +3527,21 @@ int main(int argc, char **argv) SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE); SDL_EventState(SDL_USEREVENT, SDL_IGNORE); + if (av_lockmgr_register(lockmgr)) { + fprintf(stderr, "Could not initialize lock manager!\n"); + do_exit(NULL); + } + av_init_packet(&flush_pkt); - flush_pkt.data = "FLUSH"; + flush_pkt.data = (char *)(intptr_t)"FLUSH"; - cur_stream = stream_open(input_filename, file_iformat); + is = stream_open(input_filename, file_iformat); + if (!is) { + fprintf(stderr, "Failed to initialize VideoState!\n"); + do_exit(NULL); + } - event_loop(); + event_loop(is); /* never returns */