2003-06-07 18:34:02 +00:00
/*
2010-02-21 11:35:17 +00:00
* FFplay : Simple Media Player based on the FFmpeg libraries
2003-06-07 18:34:02 +00:00
* Copyright (c) 2003 Fabrice Bellard
*
2006-10-07 15:30:46 +00:00
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
2003-06-07 18:34:02 +00:00
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
2006-10-07 15:30:46 +00:00
* version 2.1 of the License, or (at your option) any later version.
2003-06-07 18:34:02 +00:00
*
2006-10-07 15:30:46 +00:00
* FFmpeg is distributed in the hope that it will be useful,
2003-06-07 18:34:02 +00:00
* 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
2006-10-07 15:30:46 +00:00
* License along with FFmpeg; if not, write to the Free Software
2006-01-12 22:43:26 +00:00
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2003-06-07 18:34:02 +00:00
*/
2007-06-10 14:34:56 +00:00
2009-11-11 23:46:11 +00:00
#include "config.h"
2010-03-11 02:32:03 +00:00
#include <inttypes.h>
2007-07-02 07:43:23 +00:00
#include <math.h>
#include <limits.h>
2008-05-09 11:56:36 +00:00
#include "libavutil/avstring.h"
2010-01-30 19:10:26 +00:00
#include "libavutil/pixdesc.h"
2008-05-09 11:56:36 +00:00
#include "libavformat/avformat.h"
#include "libavdevice/avdevice.h"
#include "libswscale/swscale.h"
2008-08-02 05:01:30 +00:00
#include "libavcodec/audioconvert.h"
2009-05-23 23:32:29 +00:00
#include "libavcodec/colorspace.h"
2008-09-28 19:39:18 +00:00
#include "libavcodec/opt.h"
2010-03-07 21:56:48 +00:00
#include "libavcodec/avfft.h"
2003-06-07 18:34:02 +00:00
2010-03-05 02:20:10 +00:00
#if CONFIG_AVFILTER
# include "libavfilter/avfilter.h"
# include "libavfilter/avfiltergraph.h"
# include "libavfilter/graphparser.h"
#endif
2003-06-07 18:34:02 +00:00
#include "cmdutils.h"
#include <SDL.h>
#include <SDL_thread.h>
2006-07-11 21:45:45 +00:00
#ifdef __MINGW32__
2003-06-14 11:09:57 +00:00
#undef main /* We don't want SDL to override our main() */
#endif
2010-03-11 02:35:04 +00:00
#include <unistd.h>
#include <assert.h>
2008-05-21 23:36:32 +00:00
const char program_name [] = "FFplay" ;
2008-05-29 08:48:51 +00:00
const int program_birth_year = 2003 ;
2007-09-27 13:52:33 +00:00
2003-08-06 13:07:23 +00:00
//#define DEBUG_SYNC
2010-01-30 21:27:17 +00:00
#define MAX_QUEUE_SIZE (15 * 1024 * 1024)
#define MIN_AUDIOQ_SIZE (20 * 16 * 1024)
#define MIN_FRAMES 5
2003-06-07 18:34:02 +00:00
2003-08-06 13:07:23 +00:00
/* SDL audio buffer size, in samples. Should be small to have precise
A/V sync as SDL does not have hardware buffer fullness info. */
#define SDL_AUDIO_BUFFER_SIZE 1024
/* no AV sync correction is done if below the AV sync threshold */
2004-05-30 00:38:09 +00:00
#define AV_SYNC_THRESHOLD 0.01
2003-08-06 13:07:23 +00:00
/* no AV correction is done if too big error */
#define AV_NOSYNC_THRESHOLD 10.0
2010-03-11 02:35:04 +00:00
#define FRAME_SKIP_FACTOR 0.05
2003-08-06 13:07:23 +00:00
/* maximum audio speed change to get correct sync */
#define SAMPLE_CORRECTION_PERCENT_MAX 10
/* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */
#define AUDIO_DIFF_AVG_NB 20
2003-06-07 18:34:02 +00:00
/* NOTE: the size must be big enough to compensate the hardware audio buffersize size */
#define SAMPLE_ARRAY_SIZE (2*65536)
2010-03-05 02:20:10 +00:00
#if !CONFIG_AVFILTER
2006-08-03 16:55:36 +00:00
static int sws_flags = SWS_BICUBIC ;
2010-03-05 02:20:10 +00:00
#endif
2006-08-03 16:55:36 +00:00
2003-06-07 18:34:02 +00:00
typedef struct PacketQueue {
AVPacketList * first_pkt , * last_pkt ;
int nb_packets ;
int size ;
int abort_request ;
SDL_mutex * mutex ;
SDL_cond * cond ;
} PacketQueue ;
2010-03-10 15:45:46 +00:00
#define VIDEO_PICTURE_QUEUE_SIZE 2
2005-08-14 01:29:34 +00:00
#define SUBPICTURE_QUEUE_SIZE 4
2003-06-07 18:34:02 +00:00
typedef struct VideoPicture {
2005-09-20 18:47:08 +00:00
double pts ; ///<presentation time stamp for this picture
2010-03-11 02:35:04 +00:00
double target_clock ; ///<av_gettime() time at which this should be displayed ideally
2010-02-02 16:51:02 +00:00
int64_t pos ; ///<byte position in file
2003-06-07 18:34:02 +00:00
SDL_Overlay * bmp ;
int width , height ; /* source height & width */
int allocated ;
2010-03-05 02:20:10 +00:00
enum PixelFormat pix_fmt ;
#if CONFIG_AVFILTER
AVFilterPicRef * picref ;
#endif
2003-06-07 18:34:02 +00:00
} VideoPicture ;
2005-08-14 01:29:34 +00:00
typedef struct SubPicture {
double pts ; /* presentation time stamp for this picture */
AVSubtitle sub ;
} SubPicture ;
2003-06-07 18:34:02 +00:00
enum {
AV_SYNC_AUDIO_MASTER , /* default choice */
AV_SYNC_VIDEO_MASTER ,
2003-08-06 13:07:23 +00:00
AV_SYNC_EXTERNAL_CLOCK , /* synchronize to an external clock */
2003-06-07 18:34:02 +00:00
};
typedef struct VideoState {
SDL_Thread * parse_tid ;
SDL_Thread * video_tid ;
2010-03-11 02:35:04 +00:00
SDL_Thread * refresh_tid ;
2003-08-06 13:07:23 +00:00
AVInputFormat * iformat ;
2003-06-07 18:34:02 +00:00
int no_background ;
int abort_request ;
int paused ;
2003-07-17 10:29:50 +00:00
int last_paused ;
2003-11-10 18:59:45 +00:00
int seek_req ;
2004-10-10 22:05:43 +00:00
int seek_flags ;
2003-11-10 18:59:45 +00:00
int64_t seek_pos ;
2009-03-14 16:24:30 +00:00
int64_t seek_rel ;
2010-02-01 10:32:17 +00:00
int read_pause_return ;
2003-06-07 18:34:02 +00:00
AVFormatContext * ic ;
int dtg_active_format ;
int audio_stream ;
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
int av_sync_type ;
2003-08-06 13:07:23 +00:00
double external_clock ; /* external clock base */
int64_t external_clock_time ;
2005-12-17 18:14:38 +00:00
2003-08-06 13:07:23 +00:00
double audio_clock ;
double audio_diff_cum ; /* used for AV difference average computation */
double audio_diff_avg_coef ;
double audio_diff_threshold ;
int audio_diff_avg_count ;
2003-06-07 18:34:02 +00:00
AVStream * audio_st ;
PacketQueue audioq ;
int audio_hw_buf_size ;
/* samples output by the codec. we reserve more space for avsync
compensation */
2010-01-22 03:25:11 +00:00
DECLARE_ALIGNED ( 16 , uint8_t , audio_buf1 )[( AVCODEC_MAX_AUDIO_FRAME_SIZE * 3 ) / 2 ];
DECLARE_ALIGNED ( 16 , uint8_t , audio_buf2 )[( AVCODEC_MAX_AUDIO_FRAME_SIZE * 3 ) / 2 ];
2008-08-02 05:01:30 +00:00
uint8_t * audio_buf ;
2004-10-06 08:50:46 +00:00
unsigned int audio_buf_size ; /* in bytes */
2003-06-07 18:34:02 +00:00
int audio_buf_index ; /* in bytes */
2009-04-10 12:07:06 +00:00
AVPacket audio_pkt_temp ;
2003-06-07 18:34:02 +00:00
AVPacket audio_pkt ;
2008-08-02 05:01:30 +00:00
enum SampleFormat audio_src_fmt ;
AVAudioConvert * reformat_ctx ;
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
int show_audio ; /* if true, display audio samples */
int16_t sample_array [ SAMPLE_ARRAY_SIZE ];
int sample_array_index ;
2003-06-09 20:48:06 +00:00
int last_i_start ;
2010-03-07 21:56:48 +00:00
RDFTContext * rdft ;
2010-02-05 02:06:38 +00:00
int rdft_bits ;
int xpos ;
2005-12-17 18:14:38 +00:00
2005-08-14 01:29:34 +00:00
SDL_Thread * subtitle_tid ;
int subtitle_stream ;
int subtitle_stream_changed ;
AVStream * subtitle_st ;
PacketQueue subtitleq ;
SubPicture subpq [ SUBPICTURE_QUEUE_SIZE ];
int subpq_size , subpq_rindex , subpq_windex ;
SDL_mutex * subpq_mutex ;
SDL_cond * subpq_cond ;
2005-12-17 18:14:38 +00:00
2003-08-06 13:07:23 +00:00
double frame_timer ;
double frame_last_pts ;
double frame_last_delay ;
2005-12-17 18:14:38 +00:00
double video_clock ; ///<pts of last decoded frame / predicted pts of next decoded frame
2003-06-07 18:34:02 +00:00
int video_stream ;
AVStream * video_st ;
PacketQueue videoq ;
2005-09-20 18:47:08 +00:00
double video_current_pts ; ///<current displayed pts (different from video_clock if frame fifos are used)
2010-02-01 10:55:51 +00:00
double video_current_pts_drift ; ///<video_current_pts - time (av_gettime) at which we updated video_current_pts - used to have running video pts
2010-02-02 16:51:02 +00:00
int64_t video_current_pos ; ///<current displayed file pos
2003-06-07 18:34:02 +00:00
VideoPicture pictq [ VIDEO_PICTURE_QUEUE_SIZE ];
int pictq_size , pictq_rindex , pictq_windex ;
SDL_mutex * pictq_mutex ;
SDL_cond * pictq_cond ;
2010-03-05 02:20:10 +00:00
#if !CONFIG_AVFILTER
2009-05-16 10:29:55 +00:00
struct SwsContext * img_convert_ctx ;
2010-03-05 02:20:10 +00:00
#endif
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
// QETimer *video_timer;
char filename [ 1024 ];
int width , height , xleft , ytop ;
2010-01-31 18:54:32 +00:00
int64_t faulty_pts ;
int64_t faulty_dts ;
int64_t last_dts_for_fault_detection ;
int64_t last_pts_for_fault_detection ;
2010-03-05 02:20:10 +00:00
#if CONFIG_AVFILTER
AVFilterContext * out_video_filter ; ///<the last filter in the video chain
#endif
2010-03-11 02:35:04 +00:00
float skip_frames ;
float skip_frames_index ;
int refresh ;
2003-06-07 18:34:02 +00:00
} VideoState ;
2008-05-01 15:10:44 +00:00
static void show_help ( void );
2003-08-06 13:07:23 +00:00
static int audio_write_get_buf_size ( VideoState * is );
2003-06-07 18:34:02 +00:00
/* options specified by the user */
static AVInputFormat * file_iformat ;
static const char * input_filename ;
2010-03-17 23:39:18 +00:00
static const char * window_title ;
2003-06-07 18:34:02 +00:00
static int fs_screen_width ;
static int fs_screen_height ;
2006-12-30 11:22:46 +00:00
static int screen_width = 0 ;
static int screen_height = 0 ;
2007-02-22 00:33:09 +00:00
static int frame_width = 0 ;
static int frame_height = 0 ;
static enum PixelFormat frame_pix_fmt = PIX_FMT_NONE ;
2003-06-07 18:34:02 +00:00
static int audio_disable ;
static int video_disable ;
2010-02-23 16:56:23 +00:00
static int wanted_stream [ CODEC_TYPE_NB ] = {
2010-02-23 17:35:44 +00:00
[ CODEC_TYPE_AUDIO ] =- 1 ,
[ CODEC_TYPE_VIDEO ] =- 1 ,
2010-02-23 16:56:23 +00:00
[ CODEC_TYPE_SUBTITLE ] =- 1 ,
};
2010-02-02 18:02:16 +00:00
static int seek_by_bytes =- 1 ;
2003-06-07 18:34:02 +00:00
static int display_disable ;
2009-07-30 21:54:50 +00:00
static int show_status = 1 ;
2003-08-06 13:07:23 +00:00
static int av_sync_type = AV_SYNC_AUDIO_MASTER ;
2003-11-10 18:59:45 +00:00
static int64_t start_time = AV_NOPTS_VALUE ;
2003-11-28 15:14:24 +00:00
static int debug = 0 ;
2003-12-30 02:12:12 +00:00
static int debug_mv = 0 ;
2003-12-28 01:19:41 +00:00
static int step = 0 ;
2004-02-23 20:56:56 +00:00
static int thread_count = 1 ;
2004-04-30 18:54:36 +00:00
static int workaround_bugs = 1 ;
2004-09-02 15:30:46 +00:00
static int fast = 0 ;
2005-08-15 14:22:43 +00:00
static int genpts = 0 ;
2004-09-25 23:18:58 +00:00
static int lowres = 0 ;
static int idct = FF_IDCT_AUTO ;
2005-07-14 21:39:36 +00:00
static enum AVDiscard skip_frame = AVDISCARD_DEFAULT ;
static enum AVDiscard skip_idct = AVDISCARD_DEFAULT ;
static enum AVDiscard skip_loop_filter = AVDISCARD_DEFAULT ;
2008-09-08 18:18:49 +00:00
static int error_recognition = FF_ER_CAREFUL ;
2005-07-17 10:18:10 +00:00
static int error_concealment = 3 ;
2010-01-31 18:54:32 +00:00
static int decoder_reorder_pts = - 1 ;
2010-02-04 01:54:24 +00:00
static int autoexit ;
2010-03-23 17:39:51 +00:00
static int loop = 1 ;
2010-03-11 02:35:04 +00:00
static int framedrop = 1 ;
2010-03-11 11:25:51 +00:00
static int rdftspeed = 20 ;
2010-03-05 02:20:10 +00:00
#if CONFIG_AVFILTER
static char * vfilters = NULL ;
#endif
2003-06-07 18:34:02 +00:00
/* current context */
static int is_full_screen ;
static VideoState * cur_stream ;
2003-06-09 20:48:06 +00:00
static int64_t audio_callback_time ;
2003-06-07 18:34:02 +00:00
2008-10-04 09:23:18 +00:00
static AVPacket flush_pkt ;
2006-11-16 11:58:27 +00:00
2003-06-07 18:34:02 +00:00
#define FF_ALLOC_EVENT (SDL_USEREVENT)
#define FF_REFRESH_EVENT (SDL_USEREVENT + 1)
2003-08-06 13:07:23 +00:00
#define FF_QUIT_EVENT (SDL_USEREVENT + 2)
2003-06-07 18:34:02 +00:00
2008-10-04 09:23:18 +00:00
static SDL_Surface * screen ;
2003-06-07 18:34:02 +00:00
2010-02-01 12:22:12 +00:00
static int packet_queue_put ( PacketQueue * q , AVPacket * pkt );
2003-06-07 18:34:02 +00:00
/* packet queue handling */
static void packet_queue_init ( PacketQueue * q )
{
memset ( q , 0 , sizeof ( PacketQueue ));
q -> mutex = SDL_CreateMutex ();
q -> cond = SDL_CreateCond ();
2010-02-01 12:22:12 +00:00
packet_queue_put ( q , & flush_pkt );
2003-06-07 18:34:02 +00:00
}
2003-11-10 18:59:45 +00:00
static void packet_queue_flush ( PacketQueue * q )
2003-06-07 18:34:02 +00:00
{
AVPacketList * pkt , * pkt1 ;
2005-06-24 08:32:55 +00:00
SDL_LockMutex ( q -> mutex );
2003-06-07 18:34:02 +00:00
for ( pkt = q -> first_pkt ; pkt != NULL ; pkt = pkt1 ) {
pkt1 = pkt -> next ;
av_free_packet ( & pkt -> pkt );
2004-08-16 23:17:32 +00:00
av_freep ( & pkt );
2003-06-07 18:34:02 +00:00
}
2003-11-10 18:59:45 +00:00
q -> last_pkt = NULL ;
q -> first_pkt = NULL ;
q -> nb_packets = 0 ;
q -> size = 0 ;
2005-06-24 08:32:55 +00:00
SDL_UnlockMutex ( q -> mutex );
2003-11-10 18:59:45 +00:00
}
static void packet_queue_end ( PacketQueue * q )
{
packet_queue_flush ( q );
2003-06-07 18:34:02 +00:00
SDL_DestroyMutex ( q -> mutex );
SDL_DestroyCond ( q -> cond );
}
static int packet_queue_put ( PacketQueue * q , AVPacket * pkt )
{
AVPacketList * pkt1 ;
2003-11-10 18:59:45 +00:00
/* duplicate the packet */
2006-11-16 11:58:27 +00:00
if ( pkt !=& flush_pkt && av_dup_packet ( pkt ) < 0 )
2003-11-10 18:59:45 +00:00
return - 1 ;
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
pkt1 = av_malloc ( sizeof ( AVPacketList ));
if ( ! pkt1 )
return - 1 ;
pkt1 -> pkt = * pkt ;
pkt1 -> next = NULL ;
2003-11-10 18:59:45 +00:00
2003-06-07 18:34:02 +00:00
SDL_LockMutex ( q -> mutex );
if ( ! q -> last_pkt )
q -> first_pkt = pkt1 ;
else
q -> last_pkt -> next = pkt1 ;
q -> last_pkt = pkt1 ;
q -> nb_packets ++ ;
2009-02-21 16:01:52 +00:00
q -> size += pkt1 -> pkt . size + sizeof ( * pkt1 );
2003-06-07 18:34:02 +00:00
/* 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 )
{
SDL_LockMutex ( q -> mutex );
q -> abort_request = 1 ;
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
SDL_CondSignal ( q -> cond );
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 )
{
AVPacketList * pkt1 ;
int ret ;
SDL_LockMutex ( q -> mutex );
for (;;) {
if ( q -> abort_request ) {
ret = - 1 ;
break ;
}
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
pkt1 = q -> first_pkt ;
if ( pkt1 ) {
q -> first_pkt = pkt1 -> next ;
if ( ! q -> first_pkt )
q -> last_pkt = NULL ;
q -> nb_packets -- ;
2009-02-21 16:01:52 +00:00
q -> size -= pkt1 -> pkt . size + sizeof ( * pkt1 );
2003-06-07 18:34:02 +00:00
* pkt = pkt1 -> pkt ;
av_free ( pkt1 );
ret = 1 ;
break ;
} else if ( ! block ) {
ret = 0 ;
break ;
} else {
SDL_CondWait ( q -> cond , q -> mutex );
}
}
SDL_UnlockMutex ( q -> mutex );
return ret ;
}
2005-12-17 18:14:38 +00:00
static inline void fill_rectangle ( SDL_Surface * screen ,
2003-06-07 18:34:02 +00:00
int x , int y , int w , int h , int color )
{
SDL_Rect rect ;
rect . x = x ;
rect . y = y ;
rect . w = w ;
rect . h = h ;
SDL_FillRect ( screen , & rect , color );
}
#if 0
/* draw only the border of a rectangle */
void fill_border(VideoState *s, int x, int y, int w, int h, int color)
{
int w1, w2, h1, h2;
/* fill the background */
w1 = x;
if (w1 < 0)
w1 = 0;
w2 = s->width - (x + w);
if (w2 < 0)
w2 = 0;
h1 = y;
if (h1 < 0)
h1 = 0;
h2 = s->height - (y + h);
if (h2 < 0)
h2 = 0;
2005-12-17 18:14:38 +00:00
fill_rectangle(screen,
s->xleft, s->ytop,
w1, s->height,
2003-06-07 18:34:02 +00:00
color);
2005-12-17 18:14:38 +00:00
fill_rectangle(screen,
s->xleft + s->width - w2, s->ytop,
w2, s->height,
2003-06-07 18:34:02 +00:00
color);
2005-12-17 18:14:38 +00:00
fill_rectangle(screen,
s->xleft + w1, s->ytop,
s->width - w1 - w2, h1,
2003-06-07 18:34:02 +00:00
color);
2005-12-17 18:14:38 +00:00
fill_rectangle(screen,
2003-06-07 18:34:02 +00:00
s->xleft + w1, s->ytop + s->height - h2,
s->width - w1 - w2, h2,
color);
}
#endif
2005-08-14 01:29:34 +00:00
#define ALPHA_BLEND(a, oldp, newp, s)\
((((oldp << s) * (255 - (a))) + (newp * (a))) / (255 << s))
#define RGBA_IN(r, g, b, a, s)\
{\
unsigned int v = ((const uint32_t *)(s))[0];\
a = (v >> 24) & 0xff;\
r = (v >> 16) & 0xff;\
g = (v >> 8) & 0xff;\
b = v & 0xff;\
}
#define YUVA_IN(y, u, v, a, s, pal)\
{\
2007-08-09 18:58:35 +00:00
unsigned int val = ((const uint32_t *)(pal))[*(const uint8_t*)(s)];\
2005-08-14 01:29:34 +00:00
a = (val >> 24) & 0xff;\
y = (val >> 16) & 0xff;\
u = (val >> 8) & 0xff;\
v = val & 0xff;\
}
#define YUVA_OUT(d, y, u, v, a)\
{\
((uint32_t *)(d))[0] = (a << 24) | (y << 16) | (u << 8) | v;\
}
#define BPP 1
2007-08-09 18:52:49 +00:00
static void blend_subrect ( AVPicture * dst , const AVSubtitleRect * rect , int imgw , int imgh )
2005-08-14 01:29:34 +00:00
{
int wrap , wrap3 , width2 , skip2 ;
int y , u , v , a , u1 , v1 , a1 , w , h ;
uint8_t * lum , * cb , * cr ;
const uint8_t * p ;
const uint32_t * pal ;
2007-08-09 16:15:59 +00:00
int dstx , dsty , dstw , dsth ;
2005-08-14 01:29:34 +00:00
2009-01-03 17:50:00 +00:00
dstw = av_clip ( rect -> w , 0 , imgw );
dsth = av_clip ( rect -> h , 0 , imgh );
dstx = av_clip ( rect -> x , 0 , imgw - dstw );
dsty = av_clip ( rect -> y , 0 , imgh - dsth );
2007-08-09 16:15:59 +00:00
lum = dst -> data [ 0 ] + dsty * dst -> linesize [ 0 ];
cb = dst -> data [ 1 ] + ( dsty >> 1 ) * dst -> linesize [ 1 ];
cr = dst -> data [ 2 ] + ( dsty >> 1 ) * dst -> linesize [ 2 ];
2005-08-14 01:29:34 +00:00
2009-02-09 00:27:04 +00:00
width2 = (( dstw + 1 ) >> 1 ) + ( dstx & ~ dstw & 1 );
2007-08-09 16:15:59 +00:00
skip2 = dstx >> 1 ;
2005-08-14 01:29:34 +00:00
wrap = dst -> linesize [ 0 ];
2009-01-03 19:17:18 +00:00
wrap3 = rect -> pict . linesize [ 0 ];
p = rect -> pict . data [ 0 ];
pal = ( const uint32_t * ) rect -> pict . data [ 1 ]; /* Now in YCrCb! */
2005-12-17 18:14:38 +00:00
2007-08-09 16:15:59 +00:00
if ( dsty & 1 ) {
lum += dstx ;
2005-08-14 01:29:34 +00:00
cb += skip2 ;
cr += skip2 ;
2005-12-17 18:14:38 +00:00
2007-08-09 16:15:59 +00:00
if ( dstx & 1 ) {
2005-08-14 01:29:34 +00:00
YUVA_IN ( y , u , v , a , p , pal );
lum [ 0 ] = ALPHA_BLEND ( a , lum [ 0 ], y , 0 );
cb [ 0 ] = ALPHA_BLEND ( a >> 2 , cb [ 0 ], u , 0 );
cr [ 0 ] = ALPHA_BLEND ( a >> 2 , cr [ 0 ], v , 0 );
cb ++ ;
cr ++ ;
lum ++ ;
p += BPP ;
}
2007-08-09 16:15:59 +00:00
for ( w = dstw - ( dstx & 1 ); w >= 2 ; w -= 2 ) {
2005-08-14 01:29:34 +00:00
YUVA_IN ( y , u , v , a , p , pal );
u1 = u ;
v1 = v ;
a1 = a ;
lum [ 0 ] = ALPHA_BLEND ( a , lum [ 0 ], y , 0 );
YUVA_IN ( y , u , v , a , p + BPP , pal );
u1 += u ;
v1 += v ;
a1 += a ;
lum [ 1 ] = ALPHA_BLEND ( a , lum [ 1 ], y , 0 );
cb [ 0 ] = ALPHA_BLEND ( a1 >> 2 , cb [ 0 ], u1 , 1 );
cr [ 0 ] = ALPHA_BLEND ( a1 >> 2 , cr [ 0 ], v1 , 1 );
cb ++ ;
cr ++ ;
p += 2 * BPP ;
lum += 2 ;
}
if ( w ) {
YUVA_IN ( y , u , v , a , p , pal );
lum [ 0 ] = ALPHA_BLEND ( a , lum [ 0 ], y , 0 );
cb [ 0 ] = ALPHA_BLEND ( a >> 2 , cb [ 0 ], u , 0 );
cr [ 0 ] = ALPHA_BLEND ( a >> 2 , cr [ 0 ], v , 0 );
2009-02-05 23:07:24 +00:00
p ++ ;
lum ++ ;
2005-08-14 01:29:34 +00:00
}
2009-02-05 23:10:05 +00:00
p += wrap3 - dstw * BPP ;
lum += wrap - dstw - dstx ;
2005-08-14 01:29:34 +00:00
cb += dst -> linesize [ 1 ] - width2 - skip2 ;
cr += dst -> linesize [ 2 ] - width2 - skip2 ;
}
2007-08-09 16:15:59 +00:00
for ( h = dsth - ( dsty & 1 ); h >= 2 ; h -= 2 ) {
lum += dstx ;
2005-08-14 01:29:34 +00:00
cb += skip2 ;
cr += skip2 ;
2005-12-17 18:14:38 +00:00
2007-08-09 16:15:59 +00:00
if ( dstx & 1 ) {
2005-08-14 01:29:34 +00:00
YUVA_IN ( y , u , v , a , p , pal );
u1 = u ;
v1 = v ;
a1 = a ;
lum [ 0 ] = ALPHA_BLEND ( a , lum [ 0 ], y , 0 );
p += wrap3 ;
lum += wrap ;
YUVA_IN ( y , u , v , a , p , pal );
u1 += u ;
v1 += v ;
a1 += a ;
lum [ 0 ] = ALPHA_BLEND ( a , lum [ 0 ], y , 0 );
cb [ 0 ] = ALPHA_BLEND ( a1 >> 2 , cb [ 0 ], u1 , 1 );
cr [ 0 ] = ALPHA_BLEND ( a1 >> 2 , cr [ 0 ], v1 , 1 );
cb ++ ;
cr ++ ;
p += - wrap3 + BPP ;
lum += - wrap + 1 ;
}
2007-08-09 16:15:59 +00:00
for ( w = dstw - ( dstx & 1 ); w >= 2 ; w -= 2 ) {
2005-08-14 01:29:34 +00:00
YUVA_IN ( y , u , v , a , p , pal );
u1 = u ;
v1 = v ;
a1 = a ;
lum [ 0 ] = ALPHA_BLEND ( a , lum [ 0 ], y , 0 );
2009-02-02 23:24:28 +00:00
YUVA_IN ( y , u , v , a , p + BPP , pal );
2005-08-14 01:29:34 +00:00
u1 += u ;
v1 += v ;
a1 += a ;
lum [ 1 ] = ALPHA_BLEND ( a , lum [ 1 ], y , 0 );
p += wrap3 ;
lum += wrap ;
YUVA_IN ( y , u , v , a , p , pal );
u1 += u ;
v1 += v ;
a1 += a ;
lum [ 0 ] = ALPHA_BLEND ( a , lum [ 0 ], y , 0 );
2009-02-02 23:24:28 +00:00
YUVA_IN ( y , u , v , a , p + BPP , pal );
2005-08-14 01:29:34 +00:00
u1 += u ;
v1 += v ;
a1 += a ;
lum [ 1 ] = ALPHA_BLEND ( a , lum [ 1 ], y , 0 );
cb [ 0 ] = ALPHA_BLEND ( a1 >> 2 , cb [ 0 ], u1 , 2 );
cr [ 0 ] = ALPHA_BLEND ( a1 >> 2 , cr [ 0 ], v1 , 2 );
cb ++ ;
cr ++ ;
p += - wrap3 + 2 * BPP ;
lum += - wrap + 2 ;
}
if ( w ) {
YUVA_IN ( y , u , v , a , p , pal );
u1 = u ;
v1 = v ;
a1 = a ;
lum [ 0 ] = ALPHA_BLEND ( a , lum [ 0 ], y , 0 );
p += wrap3 ;
lum += wrap ;
YUVA_IN ( y , u , v , a , p , pal );
u1 += u ;
v1 += v ;
a1 += a ;
lum [ 0 ] = ALPHA_BLEND ( a , lum [ 0 ], y , 0 );
cb [ 0 ] = ALPHA_BLEND ( a1 >> 2 , cb [ 0 ], u1 , 1 );
cr [ 0 ] = ALPHA_BLEND ( a1 >> 2 , cr [ 0 ], v1 , 1 );
cb ++ ;
cr ++ ;
p += - wrap3 + BPP ;
lum += - wrap + 1 ;
}
2007-08-09 16:15:59 +00:00
p += wrap3 + ( wrap3 - dstw * BPP );
lum += wrap + ( wrap - dstw - dstx );
2005-08-14 01:29:34 +00:00
cb += dst -> linesize [ 1 ] - width2 - skip2 ;
cr += dst -> linesize [ 2 ] - width2 - skip2 ;
}
/* handle odd height */
if ( h ) {
2007-08-09 16:15:59 +00:00
lum += dstx ;
2005-08-14 01:29:34 +00:00
cb += skip2 ;
cr += skip2 ;
2005-12-17 18:14:38 +00:00
2007-08-09 16:15:59 +00:00
if ( dstx & 1 ) {
2005-08-14 01:29:34 +00:00
YUVA_IN ( y , u , v , a , p , pal );
lum [ 0 ] = ALPHA_BLEND ( a , lum [ 0 ], y , 0 );
cb [ 0 ] = ALPHA_BLEND ( a >> 2 , cb [ 0 ], u , 0 );
cr [ 0 ] = ALPHA_BLEND ( a >> 2 , cr [ 0 ], v , 0 );
cb ++ ;
cr ++ ;
lum ++ ;
p += BPP ;
}
2007-08-09 16:15:59 +00:00
for ( w = dstw - ( dstx & 1 ); w >= 2 ; w -= 2 ) {
2005-08-14 01:29:34 +00:00
YUVA_IN ( y , u , v , a , p , pal );
u1 = u ;
v1 = v ;
a1 = a ;
lum [ 0 ] = ALPHA_BLEND ( a , lum [ 0 ], y , 0 );
YUVA_IN ( y , u , v , a , p + BPP , pal );
u1 += u ;
v1 += v ;
a1 += a ;
lum [ 1 ] = ALPHA_BLEND ( a , lum [ 1 ], y , 0 );
cb [ 0 ] = ALPHA_BLEND ( a1 >> 2 , cb [ 0 ], u , 1 );
cr [ 0 ] = ALPHA_BLEND ( a1 >> 2 , cr [ 0 ], v , 1 );
cb ++ ;
cr ++ ;
p += 2 * BPP ;
lum += 2 ;
}
if ( w ) {
YUVA_IN ( y , u , v , a , p , pal );
lum [ 0 ] = ALPHA_BLEND ( a , lum [ 0 ], y , 0 );
cb [ 0 ] = ALPHA_BLEND ( a >> 2 , cb [ 0 ], u , 0 );
cr [ 0 ] = ALPHA_BLEND ( a >> 2 , cr [ 0 ], v , 0 );
}
}
}
static void free_subpicture ( SubPicture * sp )
{
int i ;
2005-12-17 18:14:38 +00:00
2005-08-14 01:29:34 +00:00
for ( i = 0 ; i < sp -> sub . num_rects ; i ++ )
{
2009-01-03 19:17:18 +00:00
av_freep ( & sp -> sub . rects [ i ] -> pict . data [ 0 ]);
av_freep ( & sp -> sub . rects [ i ] -> pict . data [ 1 ]);
2009-01-03 17:54:48 +00:00
av_freep ( & sp -> sub . rects [ i ]);
2005-08-14 01:29:34 +00:00
}
2005-12-17 18:14:38 +00:00
2005-08-14 01:29:34 +00:00
av_free ( sp -> sub . rects );
2005-12-17 18:14:38 +00:00
2005-08-14 01:29:34 +00:00
memset ( & sp -> sub , 0 , sizeof ( AVSubtitle ));
}
2003-06-07 18:34:02 +00:00
static void video_image_display ( VideoState * is )
{
VideoPicture * vp ;
2005-08-14 01:29:34 +00:00
SubPicture * sp ;
AVPicture pict ;
2003-06-07 18:34:02 +00:00
float aspect_ratio ;
int width , height , x , y ;
SDL_Rect rect ;
2005-08-14 01:29:34 +00:00
int i ;
2003-06-07 18:34:02 +00:00
vp = & is -> pictq [ is -> pictq_rindex ];
if ( vp -> bmp ) {
2010-03-05 02:20:10 +00:00
#if CONFIG_AVFILTER
if ( vp -> picref -> pixel_aspect . num == 0 )
aspect_ratio = 0 ;
else
aspect_ratio = av_q2d ( vp -> picref -> pixel_aspect );
#else
2003-06-07 18:34:02 +00:00
/* XXX: use variable in the frame */
2008-08-23 23:13:58 +00:00
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 );
2003-11-10 18:59:45 +00:00
else
2008-08-23 23:13:58 +00:00
aspect_ratio = 0 ;
2010-03-05 02:20:10 +00:00
#endif
2003-06-07 18:34:02 +00:00
if ( aspect_ratio <= 0.0 )
2008-08-23 23:13:58 +00:00
aspect_ratio = 1.0 ;
2010-03-05 02:20:10 +00:00
aspect_ratio *= ( float ) vp -> width / ( float ) vp -> height ;
2003-06-07 18:34:02 +00:00
/* if an active format is indicated, then it overrides the
mpeg format */
#if 0
2005-07-17 22:24:36 +00:00
if (is->video_st->codec->dtg_active_format != is->dtg_active_format) {
is->dtg_active_format = is->video_st->codec->dtg_active_format;
2003-06-07 18:34:02 +00:00
printf("dtg_active_format=%d\n", is->dtg_active_format);
}
#endif
#if 0
2005-07-17 22:24:36 +00:00
switch(is->video_st->codec->dtg_active_format) {
2003-06-07 18:34:02 +00:00
case FF_DTG_AFD_SAME:
default:
/* nothing to do */
break;
case FF_DTG_AFD_4_3:
aspect_ratio = 4.0 / 3.0;
break;
case FF_DTG_AFD_16_9:
aspect_ratio = 16.0 / 9.0;
break;
case FF_DTG_AFD_14_9:
aspect_ratio = 14.0 / 9.0;
break;
case FF_DTG_AFD_4_3_SP_14_9:
aspect_ratio = 14.0 / 9.0;
break;
case FF_DTG_AFD_16_9_SP_14_9:
aspect_ratio = 14.0 / 9.0;
break;
case FF_DTG_AFD_SP_4_3:
aspect_ratio = 4.0 / 3.0;
break;
}
#endif
2005-08-14 01:29:34 +00:00
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 ))
{
SDL_LockYUVOverlay ( vp -> bmp );
pict . data [ 0 ] = vp -> bmp -> pixels [ 0 ];
pict . data [ 1 ] = vp -> bmp -> pixels [ 2 ];
pict . data [ 2 ] = vp -> bmp -> pixels [ 1 ];
pict . linesize [ 0 ] = vp -> bmp -> pitches [ 0 ];
pict . linesize [ 1 ] = vp -> bmp -> pitches [ 2 ];
pict . linesize [ 2 ] = vp -> bmp -> pitches [ 1 ];
for ( i = 0 ; i < sp -> sub . num_rects ; i ++ )
2009-01-03 17:54:48 +00:00
blend_subrect ( & pict , sp -> sub . rects [ i ],
2007-08-09 18:52:49 +00:00
vp -> bmp -> w , vp -> bmp -> h );
2005-08-14 01:29:34 +00:00
SDL_UnlockYUVOverlay ( vp -> bmp );
}
}
}
2003-06-07 18:34:02 +00:00
/* XXX: we suppose the screen has a 1.0 pixel ratio */
height = is -> height ;
2008-09-03 11:16:29 +00:00
width = (( int ) rint ( height * aspect_ratio )) & ~ 1 ;
2003-06-07 18:34:02 +00:00
if ( width > is -> width ) {
width = is -> width ;
2008-09-03 11:16:29 +00:00
height = (( int ) rint ( width / aspect_ratio )) & ~ 1 ;
2003-06-07 18:34:02 +00:00
}
x = ( is -> width - width ) / 2 ;
y = ( is -> height - height ) / 2 ;
if ( ! is -> no_background ) {
/* fill the background */
// fill_border(is, x, y, width, height, QERGB(0x00, 0x00, 0x00));
} else {
is -> no_background = 0 ;
}
rect . x = is -> xleft + x ;
2007-03-02 10:40:24 +00:00
rect . y = is -> ytop + y ;
2003-06-07 18:34:02 +00:00
rect . w = width ;
rect . h = height ;
SDL_DisplayYUVOverlay ( vp -> bmp , & rect );
} else {
#if 0
2005-12-17 18:14:38 +00:00
fill_rectangle(screen,
is->xleft, is->ytop, is->width, is->height,
2003-06-07 18:34:02 +00:00
QERGB(0x00, 0x00, 0x00));
#endif
}
}
static inline int compute_mod ( int a , int b )
{
a = a % b ;
2005-12-17 18:14:38 +00:00
if ( a >= 0 )
2003-06-07 18:34:02 +00:00
return a ;
else
return 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 ;
2010-02-05 15:16:38 +00:00
int rdft_bits , nb_freq ;
for ( rdft_bits = 1 ; ( 1 << rdft_bits ) < 2 * s -> height ; rdft_bits ++ )
;
nb_freq = 1 << ( rdft_bits - 1 );
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
/* compute display index : center on currently output samples */
2005-07-17 22:24:36 +00:00
channels = s -> audio_st -> codec -> channels ;
2003-06-07 18:34:02 +00:00
nb_display_channels = channels ;
2003-06-09 20:48:06 +00:00
if ( ! s -> paused ) {
2010-02-05 15:16:38 +00:00
int data_used = s -> show_audio == 1 ? s -> width : ( 2 * nb_freq );
2003-06-09 20:48:06 +00:00
n = 2 * channels ;
delay = audio_write_get_buf_size ( s );
delay /= n ;
2005-12-17 18:14:38 +00:00
2003-06-09 20:48:06 +00:00
/* 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 ;
2010-03-10 22:43:23 +00:00
delay -= ( time_diff * s -> audio_st -> codec -> sample_rate ) / 1000000 ;
2003-06-09 20:48:06 +00:00
}
2005-12-17 18:14:38 +00:00
2010-03-10 22:43:23 +00:00
delay += 2 * data_used ;
2010-02-05 15:16:38 +00:00
if ( delay < data_used )
delay = data_used ;
2007-01-29 22:06:55 +00:00
i_start = x = compute_mod ( s -> sample_array_index - delay * channels , SAMPLE_ARRAY_SIZE );
2010-02-05 02:06:38 +00:00
if ( s -> show_audio == 1 ){
2010-02-06 13:15:15 +00:00
h = INT_MIN ;
for ( i = 0 ; i < 1000 ; i += channels ){
int idx = ( SAMPLE_ARRAY_SIZE + x - i ) % SAMPLE_ARRAY_SIZE ;
int a = s -> sample_array [ idx ];
int b = s -> sample_array [( idx + 4 * channels ) % SAMPLE_ARRAY_SIZE ];
int c = s -> sample_array [( idx + 5 * channels ) % SAMPLE_ARRAY_SIZE ];
int d = s -> sample_array [( idx + 9 * channels ) % SAMPLE_ARRAY_SIZE ];
int score = a - d ;
if ( h < score && ( b ^ c ) < 0 ){
h = score ;
i_start = idx ;
}
2007-01-29 22:06:55 +00:00
}
}
2003-06-09 20:48:06 +00:00
s -> last_i_start = i_start ;
} else {
i_start = s -> last_i_start ;
2003-06-07 18:34:02 +00:00
}
bgcolor = SDL_MapRGB ( screen -> format , 0x00 , 0x00 , 0x00 );
2010-02-05 02:06:38 +00:00
if ( s -> show_audio == 1 ){
2005-12-17 18:14:38 +00:00
fill_rectangle ( screen ,
2010-02-06 13:15:15 +00:00
s -> xleft , s -> ytop , s -> width , s -> height ,
bgcolor );
fgcolor = SDL_MapRGB ( screen -> format , 0xff , 0xff , 0xff );
/* total height for one channel */
h = s -> height / nb_display_channels ;
/* graph height / 2 */
h2 = ( h * 9 ) / 20 ;
for ( ch = 0 ; ch < nb_display_channels ; ch ++ ) {
i = i_start + ch ;
y1 = s -> ytop + ch * h + ( h / 2 ); /* position of center line */
for ( x = 0 ; x < s -> width ; x ++ ) {
y = ( s -> sample_array [ i ] * h2 ) >> 15 ;
if ( y < 0 ) {
y = - y ;
ys = y1 - y ;
} else {
ys = y1 ;
}
fill_rectangle ( screen ,
s -> xleft + x , ys , 1 , y ,
fgcolor );
i += channels ;
if ( i >= SAMPLE_ARRAY_SIZE )
i -= SAMPLE_ARRAY_SIZE ;
}
}
fgcolor = SDL_MapRGB ( screen -> format , 0x00 , 0x00 , 0xff );
for ( ch = 1 ; ch < nb_display_channels ; ch ++ ) {
y = s -> ytop + ch * h ;
fill_rectangle ( screen ,
s -> xleft , y , s -> width , 1 ,
fgcolor );
}
SDL_UpdateRect ( screen , s -> xleft , s -> ytop , s -> width , s -> height );
2010-02-05 02:06:38 +00:00
} else {
nb_display_channels = FFMIN ( nb_display_channels , 2 );
if ( rdft_bits != s -> rdft_bits ){
2010-03-07 21:56:48 +00:00
av_rdft_end ( s -> rdft );
s -> rdft = av_rdft_init ( rdft_bits , DFT_R2C );
2010-02-05 02:06:38 +00:00
s -> rdft_bits = rdft_bits ;
}
{
FFTSample data [ 2 ][ 2 * nb_freq ];
for ( ch = 0 ; ch < nb_display_channels ; ch ++ ) {
i = i_start + ch ;
for ( x = 0 ; x < 2 * nb_freq ; x ++ ) {
double w = ( x - nb_freq ) * ( 1.0 / nb_freq );
data [ ch ][ x ] = s -> sample_array [ i ] * ( 1.0 - w * w );
i += channels ;
if ( i >= SAMPLE_ARRAY_SIZE )
i -= SAMPLE_ARRAY_SIZE ;
}
2010-03-07 21:56:48 +00:00
av_rdft_calc ( s -> rdft , data [ ch ]);
2010-02-05 02:06:38 +00:00
}
//least efficient way to do this, we should of course directly access it but its more than fast enough
2010-02-05 11:25:58 +00:00
for ( y = 0 ; y < s -> height ; y ++ ){
2010-02-05 02:06:38 +00:00
double w = 1 / sqrt ( nb_freq );
int a = sqrt ( w * sqrt ( data [ 0 ][ 2 * y + 0 ] * data [ 0 ][ 2 * y + 0 ] + data [ 0 ][ 2 * y + 1 ] * data [ 0 ][ 2 * y + 1 ]));
int b = sqrt ( w * sqrt ( data [ 1 ][ 2 * y + 0 ] * data [ 1 ][ 2 * y + 0 ] + data [ 1 ][ 2 * y + 1 ] * data [ 1 ][ 2 * y + 1 ]));
a = FFMIN ( a , 255 );
b = FFMIN ( b , 255 );
fgcolor = SDL_MapRGB ( screen -> format , a , b , ( a + b ) / 2 );
fill_rectangle ( screen ,
s -> xpos , s -> height - y , 1 , 1 ,
fgcolor );
}
}
SDL_UpdateRect ( screen , s -> xpos , s -> ytop , 1 , s -> height );
s -> xpos ++ ;
if ( s -> xpos >= s -> width )
s -> xpos = s -> xleft ;
}
2003-06-07 18:34:02 +00:00
}
2006-12-31 18:01:13 +00:00
static int video_open ( VideoState * is ){
int flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL ;
int w , h ;
2006-12-31 20:04:08 +00:00
if ( is_full_screen ) flags |= SDL_FULLSCREEN ;
else flags |= SDL_RESIZABLE ;
2006-12-31 18:01:13 +00:00
if ( is_full_screen && fs_screen_width ) {
w = fs_screen_width ;
h = fs_screen_height ;
2006-12-31 20:04:08 +00:00
} else if ( ! is_full_screen && screen_width ){
w = screen_width ;
h = screen_height ;
2010-03-05 02:20:10 +00:00
#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
2006-12-31 20:04:08 +00:00
} else if ( is -> video_st && is -> video_st -> codec -> width ){
w = is -> video_st -> codec -> width ;
h = is -> video_st -> codec -> height ;
2010-03-05 02:20:10 +00:00
#endif
2006-12-31 18:01:13 +00:00
} else {
2006-12-31 20:04:08 +00:00
w = 640 ;
h = 480 ;
2006-12-31 18:01:13 +00:00
}
2010-03-10 15:39:31 +00:00
if ( screen && is -> width == screen -> w && screen -> w == w
&& is -> height == screen -> h && screen -> h == h )
return 0 ;
2007-08-27 09:17:03 +00:00
#ifndef __APPLE__
2006-12-31 18:01:13 +00:00
screen = SDL_SetVideoMode ( w , h , 0 , flags );
#else
/* setting bits_per_pixel = 0 or 32 causes blank video on OS X */
screen = SDL_SetVideoMode ( w , h , 24 , flags );
#endif
if ( ! screen ) {
fprintf ( stderr , "SDL: could not set video mode - exiting \n " );
return - 1 ;
}
2010-03-17 23:39:18 +00:00
if ( ! window_title )
window_title = input_filename ;
SDL_WM_SetCaption ( window_title , window_title );
2006-12-31 18:01:13 +00:00
is -> width = screen -> w ;
is -> height = screen -> h ;
return 0 ;
}
2006-12-31 17:59:10 +00:00
2003-06-07 18:34:02 +00:00
/* display the current picture, if any */
static void video_display ( VideoState * is )
{
2006-12-31 17:59:10 +00:00
if ( ! screen )
video_open ( cur_stream );
2005-12-17 18:14:38 +00:00
if ( is -> audio_st && is -> show_audio )
2003-06-07 18:34:02 +00:00
video_audio_display ( is );
else if ( is -> video_st )
video_image_display ( is );
}
2010-03-11 02:35:04 +00:00
static int refresh_thread ( void * opaque )
2003-06-07 18:34:02 +00:00
{
2010-03-11 02:35:04 +00:00
VideoState * is = opaque ;
while ( ! is -> abort_request ){
2003-06-07 18:34:02 +00:00
SDL_Event event ;
event . type = FF_REFRESH_EVENT ;
event . user . data1 = opaque ;
2010-03-11 02:35:04 +00:00
if ( ! is -> refresh ){
is -> refresh = 1 ;
2003-06-07 18:34:02 +00:00
SDL_PushEvent ( & event );
2010-03-11 02:35:04 +00:00
}
2010-03-11 11:25:51 +00:00
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
2010-03-11 02:35:04 +00:00
}
return 0 ;
2003-06-07 18:34:02 +00:00
}
2003-08-06 13:07:23 +00:00
/* 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 ) {
2005-12-17 18:14:38 +00:00
bytes_per_sec = is -> audio_st -> codec -> sample_rate *
2005-07-17 22:24:36 +00:00
2 * is -> audio_st -> codec -> channels ;
2003-08-06 13:07:23 +00:00
}
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 )
{
2004-01-11 00:22:03 +00:00
if ( is -> paused ) {
2010-02-01 09:26:30 +00:00
return is -> video_current_pts ;
2003-11-10 18:59:45 +00:00
} else {
2010-02-01 10:55:51 +00:00
return is -> video_current_pts_drift + av_gettime () / 1000000.0 ;
2003-11-10 18:59:45 +00:00
}
2003-08-06 13:07:23 +00:00
}
/* 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 );
}
/* get the current master clock value */
static double get_master_clock ( VideoState * is )
{
double val ;
2003-11-10 18:59:45 +00:00
if ( is -> av_sync_type == AV_SYNC_VIDEO_MASTER ) {
if ( is -> video_st )
val = get_video_clock ( is );
else
val = get_audio_clock ( is );
} else if ( is -> av_sync_type == AV_SYNC_AUDIO_MASTER ) {
if ( is -> audio_st )
val = get_audio_clock ( is );
else
val = get_video_clock ( is );
} else {
2003-08-06 13:07:23 +00:00
val = get_external_clock ( is );
2003-11-10 18:59:45 +00:00
}
2003-08-06 13:07:23 +00:00
return val ;
}
2003-11-10 18:59:45 +00:00
/* seek in the stream */
2010-01-30 23:19:59 +00:00
static void stream_seek ( VideoState * is , int64_t pos , int64_t rel , int seek_by_bytes )
2003-11-10 18:59:45 +00:00
{
2005-06-24 08:32:55 +00:00
if ( ! is -> seek_req ) {
is -> seek_pos = pos ;
2009-03-14 16:24:30 +00:00
is -> seek_rel = rel ;
2010-02-02 16:01:22 +00:00
is -> seek_flags &= ~ AVSEEK_FLAG_BYTE ;
2006-11-07 22:35:41 +00:00
if ( seek_by_bytes )
is -> seek_flags |= AVSEEK_FLAG_BYTE ;
2005-06-24 08:32:55 +00:00
is -> seek_req = 1 ;
}
2003-11-10 18:59:45 +00:00
}
/* pause or resume the video */
static void stream_pause ( VideoState * is )
{
2010-02-01 10:55:51 +00:00
if ( is -> paused ) {
is -> frame_timer += av_gettime () / 1000000.0 + is -> video_current_pts_drift - is -> video_current_pts ;
2010-02-01 10:32:17 +00:00
if ( is -> read_pause_return != AVERROR ( ENOSYS )){
2010-02-01 10:55:51 +00:00
is -> video_current_pts = is -> video_current_pts_drift + av_gettime () / 1000000.0 ;
2010-02-01 10:32:17 +00:00
}
2010-02-01 10:55:51 +00:00
is -> video_current_pts_drift = is -> video_current_pts - av_gettime () / 1000000.0 ;
2003-11-10 18:59:45 +00:00
}
2010-02-01 10:55:51 +00:00
is -> paused = ! is -> paused ;
2003-11-10 18:59:45 +00:00
}
2010-03-11 02:35:04 +00:00
static double compute_target_time ( double frame_current_pts , VideoState * is )
2009-02-18 15:17:39 +00:00
{
2010-03-11 02:35:04 +00:00
double delay , sync_threshold , diff ;
2009-02-18 15:17:39 +00:00
/* 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 ;
2009-02-18 15:23:05 +00:00
} else {
2009-02-18 15:23:30 +00:00
is -> frame_last_delay = delay ;
2009-02-18 15:23:05 +00:00
}
2009-02-18 15:17:39 +00:00
is -> frame_last_pts = frame_current_pts ;
/* 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 video is slave, we try to correct big delays by
duplicating or deleting a frame */
2010-02-02 23:22:35 +00:00
diff = get_video_clock ( is ) - get_master_clock ( is );
2009-02-18 15:17:39 +00:00
/* skip or repeat frame. We take into account the
delay to compute the threshold. I still don't know
if it is the best guess */
sync_threshold = FFMAX ( AV_SYNC_THRESHOLD , delay );
if ( fabs ( diff ) < AV_NOSYNC_THRESHOLD ) {
if ( diff <= - sync_threshold )
delay = 0 ;
else if ( diff >= sync_threshold )
delay = 2 * delay ;
}
}
is -> frame_timer += delay ;
2009-02-18 15:25:57 +00:00
#if defined(DEBUG_SYNC)
printf ( "video: delay=%0.3f actual_delay=%0.3f pts=%0.3f A-V=%f \n " ,
delay , actual_delay , frame_current_pts , - diff );
#endif
2010-03-11 02:35:04 +00:00
return is -> frame_timer ;
2009-02-18 15:17:39 +00:00
}
2003-06-07 18:34:02 +00:00
/* called to display each frame */
static void video_refresh_timer ( void * opaque )
{
VideoState * is = opaque ;
VideoPicture * vp ;
2003-08-06 13:07:23 +00:00
2005-08-14 01:29:34 +00:00
SubPicture * sp , * sp2 ;
2003-06-07 18:34:02 +00:00
if ( is -> video_st ) {
2010-03-11 02:35:04 +00:00
retry :
2003-06-07 18:34:02 +00:00
if ( is -> pictq_size == 0 ) {
2010-03-11 02:35:04 +00:00
//nothing to do, no picture to display in the que
2003-06-07 18:34:02 +00:00
} else {
2010-03-11 02:35:04 +00:00
double time = av_gettime () / 1000000.0 ;
double next_target ;
2003-08-06 13:07:23 +00:00
/* dequeue the picture */
2003-06-07 18:34:02 +00:00
vp = & is -> pictq [ is -> pictq_rindex ];
2003-08-06 13:07:23 +00:00
2010-03-11 02:35:04 +00:00
if ( time < vp -> target_clock )
return ;
2003-08-06 13:07:23 +00:00
/* update current video pts */
is -> video_current_pts = vp -> pts ;
2010-03-11 02:35:04 +00:00
is -> video_current_pts_drift = is -> video_current_pts - time ;
2010-02-02 21:31:20 +00:00
is -> video_current_pos = vp -> pos ;
2010-03-11 02:35:04 +00:00
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 );
goto retry ;
}
}
2003-08-06 13:07:23 +00:00
2005-08-14 01:29:34 +00:00
if ( is -> subtitle_st ) {
if ( is -> subtitle_stream_changed ) {
SDL_LockMutex ( is -> subpq_mutex );
2005-12-17 18:14:38 +00:00
2005-08-14 01:29:34 +00:00
while ( is -> subpq_size ) {
free_subpicture ( & is -> subpq [ is -> subpq_rindex ]);
2005-12-17 18:14:38 +00:00
2005-08-14 01:29:34 +00:00
/* update queue size and signal for next picture */
if ( ++ is -> subpq_rindex == SUBPICTURE_QUEUE_SIZE )
is -> subpq_rindex = 0 ;
2005-12-17 18:14:38 +00:00
2005-08-14 01:29:34 +00:00
is -> subpq_size -- ;
}
is -> subtitle_stream_changed = 0 ;
SDL_CondSignal ( is -> subpq_cond );
SDL_UnlockMutex ( is -> subpq_mutex );
} else {
if ( is -> subpq_size > 0 ) {
sp = & is -> subpq [ is -> subpq_rindex ];
if ( is -> subpq_size > 1 )
sp2 = & is -> subpq [( is -> subpq_rindex + 1 ) % SUBPICTURE_QUEUE_SIZE ];
else
sp2 = NULL ;
if (( is -> video_current_pts > ( sp -> pts + (( float ) sp -> sub . end_display_time / 1000 )))
|| ( sp2 && is -> video_current_pts > ( sp2 -> pts + (( float ) sp2 -> sub . start_display_time / 1000 ))))
{
free_subpicture ( sp );
/* update queue size and signal for next picture */
if ( ++ is -> subpq_rindex == SUBPICTURE_QUEUE_SIZE )
is -> subpq_rindex = 0 ;
SDL_LockMutex ( is -> subpq_mutex );
is -> subpq_size -- ;
SDL_CondSignal ( is -> subpq_cond );
SDL_UnlockMutex ( is -> subpq_mutex );
}
}
}
}
2003-06-07 18:34:02 +00:00
/* display picture */
video_display ( is );
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
/* update queue size and signal for next picture */
if ( ++ is -> pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE )
is -> pictq_rindex = 0 ;
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
SDL_LockMutex ( is -> pictq_mutex );
is -> pictq_size -- ;
SDL_CondSignal ( is -> pictq_cond );
SDL_UnlockMutex ( is -> pictq_mutex );
}
} 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 */
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
/* display picture */
video_display ( is );
}
if ( show_status ) {
static int64_t last_time ;
int64_t cur_time ;
2005-08-14 01:29:34 +00:00
int aqsize , vqsize , sqsize ;
2003-08-06 13:07:23 +00:00
double av_diff ;
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
cur_time = av_gettime ();
2009-07-30 21:54:50 +00:00
if ( ! last_time || ( cur_time - last_time ) >= 30000 ) {
2003-06-07 18:34:02 +00:00
aqsize = 0 ;
vqsize = 0 ;
2005-08-14 01:29:34 +00:00
sqsize = 0 ;
2003-06-07 18:34:02 +00:00
if ( is -> audio_st )
aqsize = is -> audioq . size ;
if ( is -> video_st )
vqsize = is -> videoq . size ;
2005-08-14 01:29:34 +00:00
if ( is -> subtitle_st )
sqsize = is -> subtitleq . size ;
2003-08-06 13:07:23 +00:00
av_diff = 0 ;
if ( is -> audio_st && is -> video_st )
av_diff = get_audio_clock ( is ) - get_video_clock ( is );
2010-03-12 15:12:05 +00:00
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 -> faulty_dts , is -> faulty_pts );
2003-06-07 18:34:02 +00:00
fflush ( stdout );
last_time = cur_time ;
}
}
}
/* allocate a picture (needs to do that in main thread to avoid
potential locking problems */
static void alloc_picture ( void * opaque )
{
VideoState * is = opaque ;
VideoPicture * vp ;
vp = & is -> pictq [ is -> pictq_windex ];
if ( vp -> bmp )
SDL_FreeYUVOverlay ( vp -> bmp );
2010-03-05 02:20:10 +00:00
#if CONFIG_AVFILTER
if ( vp -> picref )
avfilter_unref_pic ( vp -> picref );
vp -> picref = NULL ;
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
vp -> bmp = SDL_CreateYUVOverlay ( vp -> width , vp -> height ,
2005-12-17 18:14:38 +00:00
SDL_YV12_OVERLAY ,
2003-09-16 19:37:27 +00:00
screen );
2003-06-07 18:34:02 +00:00
SDL_LockMutex ( is -> pictq_mutex );
vp -> allocated = 1 ;
SDL_CondSignal ( is -> pictq_cond );
SDL_UnlockMutex ( is -> pictq_mutex );
}
2005-09-20 18:47:08 +00:00
/**
*
* @param pts the dts of the pkt / pts of the frame and guessed if not known
*/
2010-02-02 16:51:02 +00:00
static int queue_picture ( VideoState * is , AVFrame * src_frame , double pts , int64_t pos )
2003-06-07 18:34:02 +00:00
{
VideoPicture * vp ;
int dst_pix_fmt ;
2010-03-05 02:20:10 +00:00
#if CONFIG_AVFILTER
AVPicture pict_src ;
#endif
2003-06-07 18:34:02 +00:00
/* wait until we have space to put a new picture */
SDL_LockMutex ( is -> pictq_mutex );
2010-03-11 02:35:04 +00:00
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 ));
2003-06-07 18:34:02 +00:00
while ( is -> pictq_size >= VIDEO_PICTURE_QUEUE_SIZE &&
! is -> videoq . abort_request ) {
SDL_CondWait ( is -> pictq_cond , is -> pictq_mutex );
}
SDL_UnlockMutex ( is -> pictq_mutex );
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
if ( is -> videoq . abort_request )
return - 1 ;
vp = & is -> pictq [ is -> pictq_windex ];
/* alloc or resize hardware picture buffer */
2005-12-17 18:14:38 +00:00
if ( ! vp -> bmp ||
2010-03-05 02:20:10 +00:00
#if CONFIG_AVFILTER
vp -> width != is -> out_video_filter -> inputs [ 0 ] -> w ||
vp -> height != is -> out_video_filter -> inputs [ 0 ] -> h ) {
#else
2005-07-17 22:24:36 +00:00
vp -> width != is -> video_st -> codec -> width ||
vp -> height != is -> video_st -> codec -> height ) {
2010-03-05 02:20:10 +00:00
#endif
2003-06-07 18:34:02 +00:00
SDL_Event event ;
vp -> allocated = 0 ;
/* the allocation must be done in the main thread to avoid
locking problems */
event . type = FF_ALLOC_EVENT ;
event . user . data1 = is ;
SDL_PushEvent ( & event );
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
/* wait until the picture is allocated */
SDL_LockMutex ( is -> pictq_mutex );
while ( ! vp -> allocated && ! is -> videoq . abort_request ) {
SDL_CondWait ( is -> pictq_cond , is -> pictq_mutex );
}
SDL_UnlockMutex ( is -> pictq_mutex );
if ( is -> videoq . abort_request )
return - 1 ;
}
2003-08-06 13:07:23 +00:00
/* if the frame is not skipped, then display it */
2003-06-07 18:34:02 +00:00
if ( vp -> bmp ) {
2009-03-23 16:43:06 +00:00
AVPicture pict ;
2010-03-05 02:20:10 +00:00
#if CONFIG_AVFILTER
if ( vp -> picref )
avfilter_unref_pic ( vp -> picref );
vp -> picref = src_frame -> opaque ;
#endif
2009-03-23 16:43:06 +00:00
2003-06-07 18:34:02 +00:00
/* get a pointer on the bitmap */
SDL_LockYUVOverlay ( vp -> bmp );
dst_pix_fmt = PIX_FMT_YUV420P ;
2009-03-23 16:43:06 +00:00
memset ( & pict , 0 , sizeof ( AVPicture ));
2003-06-07 18:34:02 +00:00
pict . data [ 0 ] = vp -> bmp -> pixels [ 0 ];
pict . data [ 1 ] = vp -> bmp -> pixels [ 2 ];
pict . data [ 2 ] = vp -> bmp -> pixels [ 1 ];
pict . linesize [ 0 ] = vp -> bmp -> pitches [ 0 ];
pict . linesize [ 1 ] = vp -> bmp -> pitches [ 2 ];
pict . linesize [ 2 ] = vp -> bmp -> pitches [ 1 ];
2010-03-05 02:20:10 +00:00
#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 );
#else
2008-09-28 19:39:18 +00:00
sws_flags = av_get_int ( sws_opts , "sws_flags" , NULL );
2009-05-16 10:29:55 +00:00
is -> img_convert_ctx = sws_getCachedContext ( is -> img_convert_ctx ,
2010-03-05 02:20:10 +00:00
vp -> width , vp -> height , vp -> pix_fmt , vp -> width , vp -> height ,
2007-08-03 18:46:59 +00:00
dst_pix_fmt , sws_flags , NULL , NULL , NULL );
2009-05-16 10:29:55 +00:00
if ( is -> img_convert_ctx == NULL ) {
2007-08-03 18:45:44 +00:00
fprintf ( stderr , "Cannot initialize the conversion context \n " );
exit ( 1 );
}
2009-05-16 10:29:55 +00:00
sws_scale ( is -> img_convert_ctx , src_frame -> data , src_frame -> linesize ,
2010-03-05 02:20:10 +00:00
0 , vp -> height , pict . data , pict . linesize );
#endif
2003-06-07 18:34:02 +00:00
/* update the bitmap content */
SDL_UnlockYUVOverlay ( vp -> bmp );
2003-08-06 13:07:23 +00:00
vp -> pts = pts ;
2010-02-02 16:51:02 +00:00
vp -> pos = pos ;
2003-06-07 18:34:02 +00:00
/* now we can update the picture count */
if ( ++ is -> pictq_windex == VIDEO_PICTURE_QUEUE_SIZE )
is -> pictq_windex = 0 ;
SDL_LockMutex ( is -> pictq_mutex );
2010-03-11 02:35:04 +00:00
vp -> target_clock = compute_target_time ( vp -> pts , is );
2003-06-07 18:34:02 +00:00
is -> pictq_size ++ ;
SDL_UnlockMutex ( is -> pictq_mutex );
}
2003-08-06 13:07:23 +00:00
return 0 ;
}
2005-12-17 18:14:38 +00:00
/**
* compute the exact PTS for the picture if it is omitted in the stream
2005-09-20 18:47:08 +00:00
* @param pts1 the dts of the pkt / pts of the frame
*/
2010-02-02 16:51:02 +00:00
static int output_picture2 ( VideoState * is , AVFrame * src_frame , double pts1 , int64_t pos )
2003-08-06 13:07:23 +00:00
{
double frame_delay , pts ;
2005-12-17 18:14:38 +00:00
2003-08-06 13:07:23 +00:00
pts = pts1 ;
2003-06-07 18:34:02 +00:00
if ( pts != 0 ) {
2003-08-06 13:07:23 +00:00
/* update video clock with pts, if present */
2003-06-07 18:34:02 +00:00
is -> video_clock = pts ;
} else {
2003-11-10 18:59:45 +00:00
pts = is -> video_clock ;
2003-06-07 18:34:02 +00:00
}
2003-11-10 18:59:45 +00:00
/* update video clock for next frame */
2005-07-17 22:24:36 +00:00
frame_delay = av_q2d ( is -> video_st -> codec -> time_base );
2003-11-10 18:59:45 +00:00
/* for MPEG2, the frame can be repeated, so we update the
clock accordingly */
2005-09-20 18:47:08 +00:00
frame_delay += src_frame -> repeat_pict * ( frame_delay * 0.5 );
2003-11-10 18:59:45 +00:00
is -> video_clock += frame_delay ;
2003-08-06 13:07:23 +00:00
#if defined(DEBUG_SYNC) && 0
2010-03-06 19:19:13 +00:00
printf ( "frame_type=%c clock=%0.3f pts=%0.3f \n " ,
av_get_pict_type_char ( src_frame -> pict_type ), pts , pts1 );
2003-08-06 13:07:23 +00:00
#endif
2010-02-02 16:51:02 +00:00
return queue_picture ( is , src_frame , pts , pos );
2003-06-07 18:34:02 +00:00
}
2010-03-05 03:54:39 +00:00
static int get_video_frame ( VideoState * is , AVFrame * frame , int64_t * pts , AVPacket * pkt )
2003-06-07 18:34:02 +00:00
{
2010-02-01 12:19:28 +00:00
int len1 , got_picture , i ;
2003-06-07 18:34:02 +00:00
if ( packet_queue_get ( & is -> videoq , pkt , 1 ) < 0 )
2010-03-05 02:20:10 +00:00
return - 1 ;
2006-11-16 11:58:27 +00:00
if ( pkt -> data == flush_pkt . data ){
avcodec_flush_buffers ( is -> video_st -> codec );
2010-02-01 12:19:28 +00:00
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 ++ ){
2010-03-11 02:35:04 +00:00
is -> pictq [ i ]. target_clock = 0 ;
2010-02-01 12:19:28 +00:00
}
while ( is -> pictq_size && ! is -> videoq . abort_request ) {
SDL_CondWait ( is -> pictq_cond , is -> pictq_mutex );
}
2010-02-02 16:51:02 +00:00
is -> video_current_pos = - 1 ;
2010-02-01 12:19:28 +00:00
SDL_UnlockMutex ( is -> pictq_mutex );
2010-01-31 18:54:32 +00:00
is -> last_dts_for_fault_detection =
is -> last_pts_for_fault_detection = INT64_MIN ;
2010-02-01 12:27:26 +00:00
is -> frame_last_pts = AV_NOPTS_VALUE ;
2010-02-01 12:31:28 +00:00
is -> frame_last_delay = 0 ;
2010-02-03 21:33:31 +00:00
is -> frame_timer = ( double ) av_gettime () / 1000000.0 ;
2010-03-11 02:35:04 +00:00
is -> skip_frames = 1 ;
is -> skip_frames_index = 0 ;
2010-03-05 02:20:10 +00:00
return 0 ;
2006-11-16 11:58:27 +00:00
}
2003-08-06 13:07:23 +00:00
/* NOTE: ipts is the PTS of the _first_ picture beginning in
this packet, if any */
2008-08-20 23:17:43 +00:00
is -> video_st -> codec -> reordered_opaque = pkt -> pts ;
2009-04-10 12:07:06 +00:00
len1 = avcodec_decode_video2 ( is -> video_st -> codec ,
2007-03-15 23:45:45 +00:00
frame , & got_picture ,
2009-04-10 12:07:06 +00:00
pkt );
2007-03-15 23:39:43 +00:00
2010-02-01 13:03:46 +00:00
if ( got_picture ) {
2010-02-01 22:17:15 +00:00
if ( pkt -> dts != AV_NOPTS_VALUE ){
is -> faulty_dts += pkt -> dts <= is -> last_dts_for_fault_detection ;
is -> last_dts_for_fault_detection = pkt -> dts ;
}
if ( frame -> reordered_opaque != AV_NOPTS_VALUE ){
is -> faulty_pts += frame -> reordered_opaque <= is -> last_pts_for_fault_detection ;
is -> last_pts_for_fault_detection = frame -> reordered_opaque ;
}
2010-02-01 13:03:46 +00:00
}
2010-01-31 18:54:32 +00:00
if ( ( decoder_reorder_pts == 1
2010-02-01 22:23:17 +00:00
|| ( decoder_reorder_pts && is -> faulty_pts < is -> faulty_dts )
2010-01-31 18:54:32 +00:00
|| pkt -> dts == AV_NOPTS_VALUE )
2008-08-20 23:17:43 +00:00
&& frame -> reordered_opaque != AV_NOPTS_VALUE )
2010-03-05 02:20:10 +00:00
* pts = frame -> reordered_opaque ;
2007-03-15 23:45:45 +00:00
else if ( pkt -> dts != AV_NOPTS_VALUE )
2010-03-05 02:20:10 +00:00
* pts = pkt -> dts ;
2007-03-15 23:45:45 +00:00
else
2010-03-05 02:20:10 +00:00
* pts = 0 ;
2004-01-11 00:37:25 +00:00
// if (len1 < 0)
// break;
2010-03-11 02:35:04 +00:00
if ( got_picture ){
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 ;
}
}
2010-03-05 02:20:10 +00:00
return 0 ;
}
#if CONFIG_AVFILTER
typedef struct {
VideoState * is ;
AVFrame * frame ;
} FilterPriv ;
static int input_init ( AVFilterContext * ctx , const char * args , void * opaque )
{
FilterPriv * priv = ctx -> priv ;
if ( ! opaque ) return - 1 ;
priv -> is = opaque ;
priv -> frame = avcodec_alloc_frame ();
return 0 ;
}
static void input_uninit ( AVFilterContext * ctx )
{
FilterPriv * priv = ctx -> priv ;
av_free ( priv -> frame );
}
static int input_request_frame ( AVFilterLink * link )
{
FilterPriv * priv = link -> src -> priv ;
AVFilterPicRef * picref ;
2010-03-05 03:54:39 +00:00
int64_t pts = 0 ;
2010-03-05 02:20:10 +00:00
AVPacket pkt ;
int ret ;
while ( ! ( ret = get_video_frame ( priv -> is , priv -> frame , & pts , & pkt )))
av_free_packet ( & pkt );
if ( ret < 0 )
return - 1 ;
/* FIXME: until I figure out how to hook everything up to the codec
* right, we're just copying the entire frame. */
picref = avfilter_get_video_buffer ( link , AV_PERM_WRITE , link -> w , link -> h );
av_picture_copy (( AVPicture * ) & picref -> data , ( AVPicture * ) priv -> frame ,
picref -> pic -> format , link -> w , link -> h );
av_free_packet ( & pkt );
picref -> pts = pts ;
2010-03-13 11:27:07 +00:00
picref -> pos = pkt . pos ;
2010-03-05 02:20:10 +00:00
picref -> pixel_aspect = priv -> is -> video_st -> codec -> sample_aspect_ratio ;
avfilter_start_frame ( link , avfilter_ref_pic ( picref , ~ 0 ));
avfilter_draw_slice ( link , 0 , link -> h , 1 );
avfilter_end_frame ( link );
avfilter_unref_pic ( picref );
return 0 ;
}
static int input_query_formats ( AVFilterContext * ctx )
{
FilterPriv * priv = ctx -> priv ;
enum PixelFormat pix_fmts [] = {
priv -> is -> video_st -> codec -> pix_fmt , PIX_FMT_NONE
};
avfilter_set_common_formats ( ctx , avfilter_make_format_list ( pix_fmts ));
return 0 ;
}
static int input_config_props ( AVFilterLink * link )
{
FilterPriv * priv = link -> src -> priv ;
AVCodecContext * c = priv -> is -> video_st -> codec ;
link -> w = c -> width ;
link -> h = c -> height ;
return 0 ;
}
static AVFilter input_filter =
{
. name = "ffplay_input" ,
. priv_size = sizeof ( FilterPriv ),
. init = input_init ,
. uninit = input_uninit ,
. query_formats = input_query_formats ,
. inputs = ( AVFilterPad []) {{ . name = NULL }},
. outputs = ( AVFilterPad []) {{ . name = "default" ,
. type = CODEC_TYPE_VIDEO ,
. request_frame = input_request_frame ,
. config_props = input_config_props , },
{ . name = NULL }},
};
static void output_end_frame ( AVFilterLink * link )
{
}
static int output_query_formats ( AVFilterContext * ctx )
{
enum PixelFormat pix_fmts [] = { PIX_FMT_YUV420P , PIX_FMT_NONE };
avfilter_set_common_formats ( ctx , avfilter_make_format_list ( pix_fmts ));
return 0 ;
}
static int get_filtered_video_frame ( AVFilterContext * ctx , AVFrame * frame ,
2010-03-13 11:27:07 +00:00
int64_t * pts , int64_t * pos )
2010-03-05 02:20:10 +00:00
{
AVFilterPicRef * pic ;
if ( avfilter_request_frame ( ctx -> inputs [ 0 ]))
return - 1 ;
if ( ! ( pic = ctx -> inputs [ 0 ] -> cur_pic ))
return - 1 ;
ctx -> inputs [ 0 ] -> cur_pic = NULL ;
frame -> opaque = pic ;
* pts = pic -> pts ;
2010-03-13 11:27:07 +00:00
* pos = pic -> pos ;
2010-03-05 02:20:10 +00:00
memcpy ( frame -> data , pic -> data , sizeof ( frame -> data ));
memcpy ( frame -> linesize , pic -> linesize , sizeof ( frame -> linesize ));
return 1 ;
}
static AVFilter output_filter =
{
. name = "ffplay_output" ,
. query_formats = output_query_formats ,
. inputs = ( AVFilterPad []) {{ . name = "default" ,
. type = CODEC_TYPE_VIDEO ,
. end_frame = output_end_frame ,
. min_perms = AV_PERM_READ , },
{ . name = NULL }},
. outputs = ( AVFilterPad []) {{ . name = NULL }},
};
#endif /* CONFIG_AVFILTER */
static int video_thread ( void * arg )
{
VideoState * is = arg ;
AVFrame * frame = avcodec_alloc_frame ();
2010-03-20 21:39:50 +00:00
int64_t pts_int ;
2010-03-05 02:20:10 +00:00
double pts ;
int ret ;
#if CONFIG_AVFILTER
2010-03-20 21:39:50 +00:00
int64_t pos ;
2010-03-05 02:20:10 +00:00
AVFilterContext * filt_src = NULL , * filt_out = NULL ;
AVFilterGraph * graph = av_mallocz ( sizeof ( AVFilterGraph ));
graph -> scale_sws_opts = av_strdup ( "sws_flags=bilinear" );
if ( ! ( filt_src = avfilter_open ( & input_filter , "src" ))) goto the_end ;
if ( ! ( filt_out = avfilter_open ( & output_filter , "out" ))) goto the_end ;
if ( avfilter_init_filter ( filt_src , NULL , is )) goto the_end ;
if ( avfilter_init_filter ( filt_out , NULL , frame )) goto the_end ;
if ( vfilters ) {
AVFilterInOut * outputs = av_malloc ( sizeof ( AVFilterInOut ));
AVFilterInOut * inputs = av_malloc ( sizeof ( AVFilterInOut ));
outputs -> name = av_strdup ( "in" );
outputs -> filter = filt_src ;
outputs -> pad_idx = 0 ;
outputs -> next = NULL ;
inputs -> name = av_strdup ( "out" );
inputs -> filter = filt_out ;
inputs -> pad_idx = 0 ;
inputs -> next = NULL ;
if ( avfilter_graph_parse ( graph , vfilters , inputs , outputs , NULL ) < 0 )
goto the_end ;
av_freep ( & vfilters );
} else {
if ( avfilter_link ( filt_src , 0 , filt_out , 0 ) < 0 ) goto the_end ;
}
avfilter_graph_add_filter ( graph , filt_src );
avfilter_graph_add_filter ( graph , filt_out );
if ( avfilter_graph_check_validity ( graph , NULL )) goto the_end ;
if ( avfilter_graph_config_formats ( graph , NULL )) goto the_end ;
if ( avfilter_graph_config_links ( graph , NULL )) goto the_end ;
is -> out_video_filter = filt_out ;
#endif
for (;;) {
#if !CONFIG_AVFILTER
AVPacket pkt ;
#endif
while ( is -> paused && ! is -> videoq . abort_request )
SDL_Delay ( 10 );
#if CONFIG_AVFILTER
2010-03-13 11:27:07 +00:00
ret = get_filtered_video_frame ( filt_out , frame , & pts_int , & pos );
2010-03-05 02:20:10 +00:00
#else
ret = get_video_frame ( is , frame , & pts_int , & pkt );
#endif
if ( ret < 0 ) goto the_end ;
if ( ! ret )
continue ;
2010-03-05 03:54:39 +00:00
pts = pts_int * av_q2d ( is -> video_st -> time_base );
2010-03-05 02:20:10 +00:00
#if CONFIG_AVFILTER
2010-03-13 11:27:07 +00:00
ret = output_picture2 ( is , frame , pts , pos );
2010-03-05 02:20:10 +00:00
#else
2010-03-05 03:34:47 +00:00
ret = output_picture2 ( is , frame , pts , pkt . pos );
2010-03-05 02:20:10 +00:00
av_free_packet ( & pkt );
#endif
if ( ret < 0 )
goto the_end ;
2005-12-17 18:14:38 +00:00
if ( step )
2003-12-28 01:19:41 +00:00
if ( cur_stream )
stream_pause ( cur_stream );
2003-06-07 18:34:02 +00:00
}
the_end :
2010-03-05 02:20:10 +00:00
#if CONFIG_AVFILTER
avfilter_graph_destroy ( graph );
av_freep ( & graph );
#endif
2003-09-28 20:47:07 +00:00
av_free ( frame );
2003-06-07 18:34:02 +00:00
return 0 ;
}
2005-08-14 01:29:34 +00:00
static int subtitle_thread ( void * arg )
{
VideoState * is = arg ;
SubPicture * sp ;
AVPacket pkt1 , * pkt = & pkt1 ;
int len1 , got_subtitle ;
double pts ;
int i , j ;
int r , g , b , y , u , v , a ;
for (;;) {
while ( is -> paused && ! is -> subtitleq . abort_request ) {
SDL_Delay ( 10 );
}
if ( packet_queue_get ( & is -> subtitleq , pkt , 1 ) < 0 )
break ;
2005-12-17 18:14:38 +00:00
2006-11-16 11:58:27 +00:00
if ( pkt -> data == flush_pkt . data ){
avcodec_flush_buffers ( is -> subtitle_st -> codec );
continue ;
}
2005-08-14 01:29:34 +00:00
SDL_LockMutex ( is -> subpq_mutex );
while ( is -> subpq_size >= SUBPICTURE_QUEUE_SIZE &&
! is -> subtitleq . abort_request ) {
SDL_CondWait ( is -> subpq_cond , is -> subpq_mutex );
}
SDL_UnlockMutex ( is -> subpq_mutex );
2005-12-17 18:14:38 +00:00
2005-08-14 01:29:34 +00:00
if ( is -> subtitleq . abort_request )
goto the_end ;
2005-12-17 18:14:38 +00:00
2005-08-14 01:29:34 +00:00
sp = & is -> subpq [ is -> subpq_windex ];
/* NOTE: ipts is the PTS of the _first_ picture beginning in
this packet, if any */
pts = 0 ;
if ( pkt -> pts != AV_NOPTS_VALUE )
pts = av_q2d ( is -> subtitle_st -> time_base ) * pkt -> pts ;
2009-04-10 12:07:06 +00:00
len1 = avcodec_decode_subtitle2 ( is -> subtitle_st -> codec ,
2005-12-17 18:14:38 +00:00
& sp -> sub , & got_subtitle ,
2009-04-10 12:07:06 +00:00
pkt );
2005-08-14 01:29:34 +00:00
// if (len1 < 0)
// break;
if ( got_subtitle && sp -> sub . format == 0 ) {
sp -> pts = pts ;
2005-12-17 18:14:38 +00:00
2005-08-14 01:29:34 +00:00
for ( i = 0 ; i < sp -> sub . num_rects ; i ++ )
{
2009-01-03 17:54:48 +00:00
for ( j = 0 ; j < sp -> sub . rects [ i ] -> nb_colors ; j ++ )
2005-08-14 01:29:34 +00:00
{
2009-01-03 19:17:18 +00:00
RGBA_IN ( r , g , b , a , ( uint32_t * ) sp -> sub . rects [ i ] -> pict . data [ 1 ] + j );
2005-08-14 01:29:34 +00:00
y = RGB_TO_Y_CCIR ( r , g , b );
u = RGB_TO_U_CCIR ( r , g , b , 0 );
v = RGB_TO_V_CCIR ( r , g , b , 0 );
2009-01-03 19:17:18 +00:00
YUVA_OUT (( uint32_t * ) sp -> sub . rects [ i ] -> pict . data [ 1 ] + j , y , u , v , a );
2005-08-14 01:29:34 +00:00
}
}
/* now we can update the picture count */
if ( ++ is -> subpq_windex == SUBPICTURE_QUEUE_SIZE )
is -> subpq_windex = 0 ;
SDL_LockMutex ( is -> subpq_mutex );
is -> subpq_size ++ ;
SDL_UnlockMutex ( is -> subpq_mutex );
}
av_free_packet ( pkt );
2005-12-17 18:14:38 +00:00
// if (step)
2005-08-14 01:29:34 +00:00
// if (cur_stream)
// stream_pause(cur_stream);
}
the_end :
return 0 ;
}
2003-06-07 18:34:02 +00:00
/* copy samples for viewing in editor window */
static void update_sample_display ( VideoState * is , short * samples , int samples_size )
{
int size , len , channels ;
2005-07-17 22:24:36 +00:00
channels = is -> audio_st -> codec -> channels ;
2003-06-07 18:34:02 +00:00
size = samples_size / sizeof ( short );
while ( size > 0 ) {
len = SAMPLE_ARRAY_SIZE - is -> sample_array_index ;
if ( len > size )
len = size ;
memcpy ( is -> sample_array + is -> sample_array_index , samples , len * sizeof ( short ));
samples += len ;
is -> sample_array_index += len ;
if ( is -> sample_array_index >= SAMPLE_ARRAY_SIZE )
is -> sample_array_index = 0 ;
size -= len ;
}
}
/* return the new audio buffer size (samples can be added or deleted
to get better sync if video or external master clock) */
2005-12-17 18:14:38 +00:00
static int synchronize_audio ( VideoState * is , short * samples ,
2003-08-06 13:07:23 +00:00
int samples_size1 , double pts )
2003-06-07 18:34:02 +00:00
{
2003-08-06 13:07:23 +00:00
int n , samples_size ;
2003-06-07 18:34:02 +00:00
double ref_clock ;
2005-12-17 18:14:38 +00:00
2005-07-17 22:24:36 +00:00
n = 2 * is -> audio_st -> codec -> channels ;
2003-08-06 13:07:23 +00:00
samples_size = samples_size1 ;
2003-06-07 18:34:02 +00:00
/* 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 ) ||
2003-08-06 13:07:23 +00:00
is -> av_sync_type == AV_SYNC_EXTERNAL_CLOCK )) {
double diff , avg_diff ;
2003-06-07 18:34:02 +00:00
int wanted_size , min_size , max_size , nb_samples ;
2005-12-17 18:14:38 +00:00
2003-08-06 13:07:23 +00:00
ref_clock = get_master_clock ( is );
diff = get_audio_clock ( is ) - ref_clock ;
2005-12-17 18:14:38 +00:00
2003-08-06 13:07:23 +00:00
if ( 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 */
is -> audio_diff_avg_count ++ ;
} else {
/* estimate the A-V difference */
avg_diff = is -> audio_diff_cum * ( 1.0 - is -> audio_diff_avg_coef );
2003-06-07 18:34:02 +00:00
2003-08-06 13:07:23 +00:00
if ( fabs ( avg_diff ) >= is -> audio_diff_threshold ) {
2005-07-17 22:24:36 +00:00
wanted_size = samples_size + (( int )( diff * is -> audio_st -> codec -> sample_rate ) * n );
2003-08-06 13:07:23 +00:00
nb_samples = samples_size / n ;
2005-12-17 18:14:38 +00:00
2003-08-06 13:07:23 +00:00
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 ;
2005-12-17 18:14:38 +00:00
2003-08-06 13:07:23 +00:00
/* 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 ;
2005-12-17 18:14:38 +00:00
2003-08-06 13:07:23 +00:00
/* 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 ;
}
}
#if 0
2005-12-17 18:14:38 +00:00
printf("diff=%f adiff=%f sample_diff=%d apts=%0.3f vpts=%0.3f %f\n",
diff, avg_diff, samples_size - samples_size1,
2003-08-06 13:07:23 +00:00
is->audio_clock, is->video_clock, is->audio_diff_threshold);
#endif
2003-06-07 18:34:02 +00:00
}
2003-08-06 13:07:23 +00:00
} else {
/* too big difference : may be initial PTS errors, so
reset A-V filter */
is -> audio_diff_avg_count = 0 ;
is -> audio_diff_cum = 0 ;
2003-06-07 18:34:02 +00:00
}
}
return samples_size ;
}
/* decode one audio frame and returns its uncompressed size */
2008-08-02 05:01:30 +00:00
static int audio_decode_frame ( VideoState * is , double * pts_ptr )
2003-06-07 18:34:02 +00:00
{
2009-04-10 12:07:06 +00:00
AVPacket * pkt_temp = & is -> audio_pkt_temp ;
2003-06-07 18:34:02 +00:00
AVPacket * pkt = & is -> audio_pkt ;
2008-08-02 01:26:38 +00:00
AVCodecContext * dec = is -> audio_st -> codec ;
2003-11-10 18:59:45 +00:00
int n , len1 , data_size ;
2003-06-07 18:34:02 +00:00
double pts ;
for (;;) {
2003-11-10 18:59:45 +00:00
/* NOTE: the audio packet can contain several frames */
2009-04-10 12:07:06 +00:00
while ( pkt_temp -> size > 0 ) {
2008-08-02 05:01:30 +00:00
data_size = sizeof ( is -> audio_buf1 );
2009-04-10 12:07:06 +00:00
len1 = avcodec_decode_audio3 ( dec ,
2008-08-02 05:01:30 +00:00
( int16_t * ) is -> audio_buf1 , & data_size ,
2009-04-10 12:07:06 +00:00
pkt_temp );
2003-11-10 18:59:45 +00:00
if ( len1 < 0 ) {
/* if error, we skip the frame */
2009-04-10 12:07:06 +00:00
pkt_temp -> size = 0 ;
2003-06-07 18:34:02 +00:00
break ;
2003-11-10 18:59:45 +00:00
}
2005-12-17 18:14:38 +00:00
2009-04-10 12:07:06 +00:00
pkt_temp -> data += len1 ;
pkt_temp -> size -= len1 ;
2003-11-10 18:59:45 +00:00
if ( data_size <= 0 )
continue ;
2008-08-02 05:01:30 +00:00
if ( dec -> sample_fmt != is -> audio_src_fmt ) {
if ( is -> reformat_ctx )
av_audio_convert_free ( is -> reformat_ctx );
is -> reformat_ctx = av_audio_convert_alloc ( SAMPLE_FMT_S16 , 1 ,
dec -> sample_fmt , 1 , NULL , 0 );
if ( ! is -> reformat_ctx ) {
fprintf ( stderr , "Cannot convert %s sample format to %s sample format \n " ,
avcodec_get_sample_fmt_name ( dec -> sample_fmt ),
avcodec_get_sample_fmt_name ( SAMPLE_FMT_S16 ));
break ;
}
is -> audio_src_fmt = dec -> sample_fmt ;
}
if ( is -> reformat_ctx ) {
const void * ibuf [ 6 ] = { is -> audio_buf1 };
void * obuf [ 6 ] = { is -> audio_buf2 };
int istride [ 6 ] = { av_get_bits_per_sample_format ( dec -> sample_fmt ) / 8 };
int ostride [ 6 ] = { 2 };
int len = data_size / istride [ 0 ];
if ( av_audio_convert ( is -> reformat_ctx , obuf , ostride , ibuf , istride , len ) < 0 ) {
printf ( "av_audio_convert() failed \n " );
break ;
}
is -> audio_buf = is -> audio_buf2 ;
/* FIXME: existing code assume that data_size equals framesize*channels*2
remove this legacy cruft */
data_size = len * 2 ;
} else {
is -> audio_buf = is -> audio_buf1 ;
}
2003-11-10 18:59:45 +00:00
/* if no pts, then compute it */
pts = is -> audio_clock ;
* pts_ptr = pts ;
2008-08-02 01:26:38 +00:00
n = 2 * dec -> channels ;
2005-12-17 18:14:38 +00:00
is -> audio_clock += ( double ) data_size /
2008-08-02 01:26:38 +00:00
( double )( n * dec -> sample_rate );
2003-08-06 13:07:23 +00:00
#if defined(DEBUG_SYNC)
2003-11-10 18:59:45 +00:00
{
static double last_clock ;
printf ( "audio: delay=%0.3f clock=%0.3f pts=%0.3f \n " ,
is -> audio_clock - last_clock ,
is -> audio_clock , pts );
last_clock = is -> audio_clock ;
2003-06-07 18:34:02 +00:00
}
2003-11-10 18:59:45 +00:00
#endif
return data_size ;
2003-06-07 18:34:02 +00:00
}
2003-11-10 18:59:45 +00:00
/* free the current packet */
if ( pkt -> data )
2003-06-07 18:34:02 +00:00
av_free_packet ( pkt );
2005-12-17 18:14:38 +00:00
2003-11-10 18:59:45 +00:00
if ( is -> paused || is -> audioq . abort_request ) {
return - 1 ;
}
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
/* read next packet */
if ( packet_queue_get ( & is -> audioq , pkt , 1 ) < 0 )
return - 1 ;
2006-11-16 11:58:27 +00:00
if ( pkt -> data == flush_pkt . data ){
2008-08-02 01:26:38 +00:00
avcodec_flush_buffers ( dec );
2006-11-16 11:58:27 +00:00
continue ;
}
2009-04-10 12:07:06 +00:00
pkt_temp -> data = pkt -> data ;
pkt_temp -> size = pkt -> size ;
2005-12-17 18:14:38 +00:00
2003-11-10 18:59:45 +00:00
/* if update the audio clock with the pts */
if ( pkt -> pts != AV_NOPTS_VALUE ) {
2005-04-30 21:43:59 +00:00
is -> audio_clock = av_q2d ( is -> audio_st -> time_base ) * pkt -> pts ;
2003-11-10 18:59:45 +00:00
}
2003-06-07 18:34:02 +00:00
}
}
2003-08-06 13:07:23 +00:00
/* 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 )
2003-06-07 18:34:02 +00:00
{
2007-03-02 13:26:42 +00:00
return is -> audio_buf_size - is -> audio_buf_index ;
2003-06-07 18:34:02 +00:00
}
/* prepare a new audio buffer */
2008-05-01 15:10:44 +00:00
static void sdl_audio_callback ( void * opaque , Uint8 * stream , int len )
2003-06-07 18:34:02 +00:00
{
VideoState * is = opaque ;
int audio_size , len1 ;
double pts ;
audio_callback_time = av_gettime ();
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
while ( len > 0 ) {
if ( is -> audio_buf_index >= is -> audio_buf_size ) {
2008-08-02 05:01:30 +00:00
audio_size = audio_decode_frame ( is , & pts );
2003-06-07 18:34:02 +00:00
if ( audio_size < 0 ) {
/* if error, just output silence */
2009-01-09 23:54:57 +00:00
is -> audio_buf = is -> audio_buf1 ;
2003-06-07 18:34:02 +00:00
is -> audio_buf_size = 1024 ;
memset ( is -> audio_buf , 0 , is -> audio_buf_size );
} else {
if ( is -> show_audio )
update_sample_display ( is , ( int16_t * ) is -> audio_buf , audio_size );
2005-12-17 18:14:38 +00:00
audio_size = synchronize_audio ( is , ( int16_t * ) is -> audio_buf , audio_size ,
2003-06-07 18:34:02 +00:00
pts );
is -> audio_buf_size = audio_size ;
}
is -> audio_buf_index = 0 ;
}
len1 = is -> audio_buf_size - is -> audio_buf_index ;
if ( len1 > len )
len1 = len ;
memcpy ( stream , ( uint8_t * ) is -> audio_buf + is -> audio_buf_index , len1 );
len -= len1 ;
stream += len1 ;
is -> audio_buf_index += len1 ;
}
}
/* open a given stream. Return 0 if OK */
static int stream_component_open ( VideoState * is , int stream_index )
{
AVFormatContext * ic = is -> ic ;
2010-02-16 22:38:43 +00:00
AVCodecContext * avctx ;
2003-06-07 18:34:02 +00:00
AVCodec * codec ;
SDL_AudioSpec wanted_spec , spec ;
if ( stream_index < 0 || stream_index >= ic -> nb_streams )
return - 1 ;
2010-02-16 22:38:43 +00:00
avctx = ic -> streams [ stream_index ] -> codec ;
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
/* prepare audio output */
2010-02-16 22:38:43 +00:00
if ( avctx -> codec_type == CODEC_TYPE_AUDIO ) {
if ( avctx -> channels > 0 ) {
avctx -> request_channels = FFMIN ( 2 , avctx -> channels );
2007-12-15 07:05:14 +00:00
} else {
2010-02-16 22:38:43 +00:00
avctx -> request_channels = 2 ;
2007-12-15 07:05:14 +00:00
}
2003-06-07 18:34:02 +00:00
}
2010-02-16 22:38:43 +00:00
codec = avcodec_find_decoder ( avctx -> codec_id );
avctx -> debug_mv = debug_mv ;
avctx -> debug = debug ;
avctx -> workaround_bugs = workaround_bugs ;
avctx -> lowres = lowres ;
if ( lowres ) avctx -> flags |= CODEC_FLAG_EMU_EDGE ;
avctx -> idct_algo = idct ;
if ( fast ) avctx -> flags2 |= CODEC_FLAG2_FAST ;
avctx -> skip_frame = skip_frame ;
avctx -> skip_idct = skip_idct ;
avctx -> skip_loop_filter = skip_loop_filter ;
avctx -> error_recognition = error_recognition ;
avctx -> error_concealment = error_concealment ;
avcodec_thread_init ( avctx , thread_count );
2008-09-28 19:39:18 +00:00
2010-02-16 22:38:43 +00:00
set_context_opts ( avctx , avcodec_opts [ avctx -> codec_type ], 0 );
2008-09-28 19:39:18 +00:00
2003-06-07 18:34:02 +00:00
if ( ! codec ||
2010-02-16 22:38:43 +00:00
avcodec_open ( avctx , codec ) < 0 )
2003-06-07 18:34:02 +00:00
return - 1 ;
2008-01-06 02:44:05 +00:00
/* prepare audio output */
2010-02-16 22:38:43 +00:00
if ( avctx -> codec_type == CODEC_TYPE_AUDIO ) {
wanted_spec . freq = avctx -> sample_rate ;
2008-01-06 02:44:05 +00:00
wanted_spec . format = AUDIO_S16SYS ;
2010-02-16 22:38:43 +00:00
wanted_spec . channels = avctx -> channels ;
2008-01-06 02:44:05 +00:00
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 ;
2008-08-02 05:01:30 +00:00
is -> audio_src_fmt = SAMPLE_FMT_S16 ;
2008-01-06 02:44:05 +00:00
}
2008-08-18 17:09:34 +00:00
ic -> streams [ stream_index ] -> discard = AVDISCARD_DEFAULT ;
2010-02-16 22:38:43 +00:00
switch ( avctx -> codec_type ) {
2003-06-07 18:34:02 +00:00
case CODEC_TYPE_AUDIO :
is -> audio_stream = stream_index ;
is -> audio_st = ic -> streams [ stream_index ];
is -> audio_buf_size = 0 ;
is -> audio_buf_index = 0 ;
2003-08-06 13:07:23 +00:00
/* init averaging filter */
is -> audio_diff_avg_coef = exp ( log ( 0.01 ) / AUDIO_DIFF_AVG_NB );
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 */
2010-02-16 22:38:43 +00:00
is -> audio_diff_threshold = 2.0 * SDL_AUDIO_BUFFER_SIZE / avctx -> sample_rate ;
2003-08-06 13:07:23 +00:00
2003-06-07 18:34:02 +00:00
memset ( & is -> audio_pkt , 0 , sizeof ( is -> audio_pkt ));
packet_queue_init ( & is -> audioq );
2005-12-22 01:10:11 +00:00
SDL_PauseAudio ( 0 );
2003-06-07 18:34:02 +00:00
break ;
case CODEC_TYPE_VIDEO :
is -> video_stream = stream_index ;
is -> video_st = ic -> streams [ stream_index ];
2010-02-01 10:55:51 +00:00
// is->video_current_pts_time = av_gettime();
2003-08-06 13:07:23 +00:00
2003-06-07 18:34:02 +00:00
packet_queue_init ( & is -> videoq );
is -> video_tid = SDL_CreateThread ( video_thread , is );
break ;
2005-08-14 01:29:34 +00:00
case CODEC_TYPE_SUBTITLE :
is -> subtitle_stream = stream_index ;
is -> subtitle_st = ic -> streams [ stream_index ];
packet_queue_init ( & is -> subtitleq );
2005-12-17 18:14:38 +00:00
2005-08-14 01:29:34 +00:00
is -> subtitle_tid = SDL_CreateThread ( subtitle_thread , is );
break ;
2003-06-07 18:34:02 +00:00
default :
break ;
}
return 0 ;
}
static void stream_component_close ( VideoState * is , int stream_index )
{
AVFormatContext * ic = is -> ic ;
2010-02-16 22:38:43 +00:00
AVCodecContext * avctx ;
2005-12-17 18:14:38 +00:00
2005-08-14 01:29:34 +00:00
if ( stream_index < 0 || stream_index >= ic -> nb_streams )
return ;
2010-02-16 22:38:43 +00:00
avctx = ic -> streams [ stream_index ] -> codec ;
2003-06-07 18:34:02 +00:00
2010-02-16 22:38:43 +00:00
switch ( avctx -> codec_type ) {
2003-06-07 18:34:02 +00:00
case CODEC_TYPE_AUDIO :
packet_queue_abort ( & is -> audioq );
SDL_CloseAudio ();
packet_queue_end ( & is -> audioq );
2008-08-02 05:01:30 +00:00
if ( is -> reformat_ctx )
av_audio_convert_free ( is -> reformat_ctx );
2010-02-24 14:45:18 +00:00
is -> reformat_ctx = NULL ;
2003-06-07 18:34:02 +00:00
break ;
case CODEC_TYPE_VIDEO :
packet_queue_abort ( & is -> videoq );
/* note: we also signal this mutex to make sure we deblock the
video thread in all cases */
SDL_LockMutex ( is -> pictq_mutex );
SDL_CondSignal ( is -> pictq_cond );
SDL_UnlockMutex ( is -> pictq_mutex );
SDL_WaitThread ( is -> video_tid , NULL );
packet_queue_end ( & is -> videoq );
break ;
2005-08-14 01:29:34 +00:00
case CODEC_TYPE_SUBTITLE :
packet_queue_abort ( & is -> subtitleq );
2005-12-17 18:14:38 +00:00
2005-08-14 01:29:34 +00:00
/* note: we also signal this mutex to make sure we deblock the
video thread in all cases */
SDL_LockMutex ( is -> subpq_mutex );
is -> subtitle_stream_changed = 1 ;
2005-12-17 18:14:38 +00:00
2005-08-14 01:29:34 +00:00
SDL_CondSignal ( is -> subpq_cond );
SDL_UnlockMutex ( is -> subpq_mutex );
SDL_WaitThread ( is -> subtitle_tid , NULL );
packet_queue_end ( & is -> subtitleq );
break ;
2003-06-07 18:34:02 +00:00
default :
break ;
}
2008-08-18 17:09:34 +00:00
ic -> streams [ stream_index ] -> discard = AVDISCARD_ALL ;
2010-02-16 22:38:43 +00:00
avcodec_close ( avctx );
switch ( avctx -> codec_type ) {
2003-06-07 18:34:02 +00:00
case CODEC_TYPE_AUDIO :
is -> audio_st = NULL ;
is -> audio_stream = - 1 ;
break ;
case CODEC_TYPE_VIDEO :
is -> video_st = NULL ;
is -> video_stream = - 1 ;
break ;
2005-08-14 01:29:34 +00:00
case CODEC_TYPE_SUBTITLE :
is -> subtitle_st = NULL ;
is -> subtitle_stream = - 1 ;
break ;
2003-06-07 18:34:02 +00:00
default :
break ;
}
}
2003-07-17 10:29:50 +00:00
/* 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 )
{
return ( global_video_state && global_video_state -> abort_request );
}
2003-06-07 18:34:02 +00:00
/* this thread gets the stream from the disk or the network */
static int decode_thread ( void * arg )
{
VideoState * is = arg ;
AVFormatContext * ic ;
2010-02-23 16:46:40 +00:00
int err , i , ret ;
int st_index [ CODEC_TYPE_NB ];
2010-02-23 17:09:53 +00:00
int st_count [ CODEC_TYPE_NB ] = { 0 };
2010-02-23 17:35:44 +00:00
int st_best_packet_count [ CODEC_TYPE_NB ];
2003-06-07 18:34:02 +00:00
AVPacket pkt1 , * pkt = & pkt1 ;
2003-09-16 19:37:27 +00:00
AVFormatParameters params , * ap = & params ;
2009-04-19 20:24:44 +00:00
int eof = 0 ;
2003-06-07 18:34:02 +00:00
2010-01-30 22:47:08 +00:00
ic = avformat_alloc_context ();
2010-02-23 16:46:40 +00:00
memset ( st_index , - 1 , sizeof ( st_index ));
2010-02-23 17:35:44 +00:00
memset ( st_best_packet_count , - 1 , sizeof ( st_best_packet_count ));
2003-06-07 18:34:02 +00:00
is -> video_stream = - 1 ;
is -> audio_stream = - 1 ;
2005-08-14 01:29:34 +00:00
is -> subtitle_stream = - 1 ;
2003-06-07 18:34:02 +00:00
2003-07-17 10:29:50 +00:00
global_video_state = is ;
url_set_interrupt_cb ( decode_interrupt_cb );
2003-09-16 19:37:27 +00:00
memset ( ap , 0 , sizeof ( * ap ));
2005-12-17 18:14:38 +00:00
2010-01-30 22:47:08 +00:00
ap -> prealloced_context = 1 ;
2007-02-22 00:33:09 +00:00
ap -> width = frame_width ;
ap -> height = frame_height ;
2007-02-09 13:24:08 +00:00
ap -> time_base = ( AVRational ){ 1 , 25 };
2007-02-22 00:33:09 +00:00
ap -> pix_fmt = frame_pix_fmt ;
2007-02-09 13:24:08 +00:00
2010-01-30 22:47:08 +00:00
set_context_opts ( ic , avformat_opts , AV_OPT_FLAG_DECODING_PARAM );
2003-09-16 19:37:27 +00:00
err = av_open_input_file ( & ic , is -> filename , is -> iformat , 0 , ap );
2003-08-06 13:07:23 +00:00
if ( err < 0 ) {
print_error ( is -> filename , err );
ret = - 1 ;
goto fail ;
}
2003-06-07 18:34:02 +00:00
is -> ic = ic ;
2005-08-15 14:22:43 +00:00
if ( genpts )
ic -> flags |= AVFMT_FLAG_GENPTS ;
2007-08-24 07:20:28 +00:00
err = av_find_stream_info ( ic );
if ( err < 0 ) {
fprintf ( stderr , "%s: could not find codec parameters \n " , is -> filename );
ret = - 1 ;
goto fail ;
}
2007-11-21 07:41:00 +00:00
if ( ic -> pb )
ic -> pb -> eof_reached = 0 ; //FIXME hack, ffplay maybe should not use url_feof() to test for the end
2003-11-10 18:59:45 +00:00
2010-02-02 18:02:16 +00:00
if ( seek_by_bytes < 0 )
seek_by_bytes = !! ( ic -> iformat -> flags & AVFMT_TS_DISCONT );
2003-11-10 18:59:45 +00:00
/* if seeking requested, we execute it */
if ( start_time != AV_NOPTS_VALUE ) {
int64_t timestamp ;
timestamp = start_time ;
/* add the stream start time */
if ( ic -> start_time != AV_NOPTS_VALUE )
timestamp += ic -> start_time ;
2009-03-14 16:24:30 +00:00
ret = avformat_seek_file ( ic , - 1 , INT64_MIN , timestamp , INT64_MAX , 0 );
2003-11-10 18:59:45 +00:00
if ( ret < 0 ) {
2005-12-17 18:14:38 +00:00
fprintf ( stderr , "%s: could not seek to position %0.3f \n " ,
2003-11-10 18:59:45 +00:00
is -> filename , ( double ) timestamp / AV_TIME_BASE );
}
}
2003-06-07 18:34:02 +00:00
for ( i = 0 ; i < ic -> nb_streams ; i ++ ) {
2010-02-23 17:35:44 +00:00
AVStream * st = ic -> streams [ i ];
AVCodecContext * avctx = st -> codec ;
2008-08-18 17:09:34 +00:00
ic -> streams [ i ] -> discard = AVDISCARD_ALL ;
2010-02-23 17:09:53 +00:00
if ( avctx -> codec_type >= ( unsigned ) CODEC_TYPE_NB )
2010-03-05 00:22:37 +00:00
continue ;
2010-02-23 17:09:53 +00:00
if ( st_count [ avctx -> codec_type ] ++ != wanted_stream [ avctx -> codec_type ] && wanted_stream [ avctx -> codec_type ] >= 0 )
continue ;
2010-02-23 17:35:44 +00:00
if ( st_best_packet_count [ avctx -> codec_type ] >= st -> codec_info_nb_frames )
continue ;
st_best_packet_count [ avctx -> codec_type ] = st -> codec_info_nb_frames ;
2010-02-16 22:38:43 +00:00
switch ( avctx -> codec_type ) {
2003-06-07 18:34:02 +00:00
case CODEC_TYPE_AUDIO :
2010-02-23 17:09:53 +00:00
if ( ! audio_disable )
2010-02-23 16:46:40 +00:00
st_index [ CODEC_TYPE_AUDIO ] = i ;
2003-06-07 18:34:02 +00:00
break ;
case CODEC_TYPE_VIDEO :
2009-02-09 01:38:12 +00:00
case CODEC_TYPE_SUBTITLE :
2010-02-23 17:09:53 +00:00
if ( ! video_disable )
st_index [ avctx -> codec_type ] = i ;
2009-02-09 01:38:12 +00:00
break ;
2003-06-07 18:34:02 +00:00
default :
break ;
}
}
if ( show_status ) {
dump_format ( ic , 0 , is -> filename , 0 );
}
/* open the streams */
2010-02-23 16:46:40 +00:00
if ( st_index [ CODEC_TYPE_AUDIO ] >= 0 ) {
stream_component_open ( is , st_index [ CODEC_TYPE_AUDIO ]);
2003-06-07 18:34:02 +00:00
}
2010-02-18 00:19:50 +00:00
ret =- 1 ;
2010-02-23 16:46:40 +00:00
if ( st_index [ CODEC_TYPE_VIDEO ] >= 0 ) {
ret = stream_component_open ( is , st_index [ CODEC_TYPE_VIDEO ]);
2010-02-18 00:19:50 +00:00
}
2010-03-11 02:35:04 +00:00
is -> refresh_tid = SDL_CreateThread ( refresh_thread , is );
2010-02-18 00:19:50 +00:00
if ( ret < 0 ) {
2003-06-07 18:34:02 +00:00
if ( ! display_disable )
2010-02-05 15:26:42 +00:00
is -> show_audio = 2 ;
2003-06-07 18:34:02 +00:00
}
2010-02-23 16:46:40 +00:00
if ( st_index [ CODEC_TYPE_SUBTITLE ] >= 0 ) {
stream_component_open ( is , st_index [ CODEC_TYPE_SUBTITLE ]);
2009-02-09 01:38:12 +00:00
}
2003-06-07 18:34:02 +00:00
if ( is -> video_stream < 0 && is -> audio_stream < 0 ) {
2003-08-06 13:07:23 +00:00
fprintf ( stderr , "%s: could not open codecs \n " , is -> filename );
ret = - 1 ;
2003-06-07 18:34:02 +00:00
goto fail ;
}
for (;;) {
if ( is -> abort_request )
break ;
2003-07-17 10:29:50 +00:00
if ( is -> paused != is -> last_paused ) {
is -> last_paused = is -> paused ;
2003-11-10 18:59:45 +00:00
if ( is -> paused )
2010-02-01 10:32:17 +00:00
is -> read_pause_return = av_read_pause ( ic );
2003-11-10 18:59:45 +00:00
else
av_read_play ( ic );
2003-07-17 10:29:50 +00:00
}
2009-01-19 21:39:07 +00:00
#if CONFIG_RTSP_DEMUXER
if ( is -> paused && ! strcmp ( ic -> iformat -> name , "rtsp" )) {
2003-07-17 10:29:50 +00:00
/* wait 10 ms to avoid trying to get another packet */
/* XXX: horrible */
SDL_Delay ( 10 );
continue ;
}
2003-08-28 23:11:23 +00:00
#endif
2003-11-10 18:59:45 +00:00
if ( is -> seek_req ) {
2007-01-23 14:22:25 +00:00
int64_t seek_target = is -> seek_pos ;
2009-03-14 16:24:30 +00:00
int64_t seek_min = is -> seek_rel > 0 ? seek_target - is -> seek_rel + 2 : INT64_MIN ;
int64_t seek_max = is -> seek_rel < 0 ? seek_target - is -> seek_rel - 2 : INT64_MAX ;
//FIXME the +-2 is due to rounding being not done in the correct direction in generation
// of the seek_pos/seek_rel variables
2007-01-23 14:22:25 +00:00
2009-03-14 16:24:30 +00:00
ret = avformat_seek_file ( is -> ic , - 1 , seek_min , seek_target , seek_max , is -> seek_flags );
2003-11-10 18:59:45 +00:00
if ( ret < 0 ) {
fprintf ( stderr , "%s: error while seeking \n " , is -> ic -> filename );
2004-01-10 22:36:35 +00:00
} else {
if ( is -> audio_stream >= 0 ) {
packet_queue_flush ( & is -> audioq );
2006-11-16 11:58:27 +00:00
packet_queue_put ( & is -> audioq , & flush_pkt );
2004-01-10 22:36:35 +00:00
}
2005-08-14 01:29:34 +00:00
if ( is -> subtitle_stream >= 0 ) {
packet_queue_flush ( & is -> subtitleq );
2006-11-16 11:58:27 +00:00
packet_queue_put ( & is -> subtitleq , & flush_pkt );
2005-08-14 01:29:34 +00:00
}
2004-01-10 22:36:35 +00:00
if ( is -> video_stream >= 0 ) {
packet_queue_flush ( & is -> videoq );
2006-11-16 11:58:27 +00:00
packet_queue_put ( & is -> videoq , & flush_pkt );
2004-01-10 22:36:35 +00:00
}
2003-11-10 18:59:45 +00:00
}
is -> seek_req = 0 ;
2009-04-28 18:02:21 +00:00
eof = 0 ;
2003-11-10 18:59:45 +00:00
}
2003-07-17 10:29:50 +00:00
2003-06-07 18:34:02 +00:00
/* if the queue are full, no need to read more */
2010-01-30 21:27:17 +00:00
if ( 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 ))) {
2003-06-07 18:34:02 +00:00
/* wait 10 ms */
SDL_Delay ( 10 );
continue ;
}
2009-04-19 20:24:44 +00:00
if ( url_feof ( ic -> pb ) || eof ) {
2009-04-19 21:44:03 +00:00
if ( is -> video_stream >= 0 ){
2009-04-19 21:44:25 +00:00
av_init_packet ( pkt );
pkt -> data = NULL ;
pkt -> size = 0 ;
pkt -> stream_index = is -> video_stream ;
packet_queue_put ( & is -> videoq , pkt );
2009-04-19 21:44:03 +00:00
}
2009-04-19 20:25:20 +00:00
SDL_Delay ( 10 );
2010-03-23 17:39:51 +00:00
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 );
} else if ( autoexit ){
ret = AVERROR_EOF ;
goto fail ;
}
2010-02-04 01:54:24 +00:00
}
2009-01-07 20:45:39 +00:00
continue ;
}
2003-11-10 18:59:45 +00:00
ret = av_read_frame ( ic , pkt );
2003-06-07 18:34:02 +00:00
if ( ret < 0 ) {
2009-04-19 20:24:44 +00:00
if ( ret == AVERROR_EOF )
eof = 1 ;
if ( url_ferror ( ic -> pb ))
2005-12-22 01:10:11 +00:00
break ;
2009-04-19 20:24:44 +00:00
SDL_Delay ( 100 ); /* wait for user event */
continue ;
2003-06-07 18:34:02 +00:00
}
if ( pkt -> stream_index == is -> audio_stream ) {
packet_queue_put ( & is -> audioq , pkt );
} else if ( pkt -> stream_index == is -> video_stream ) {
packet_queue_put ( & is -> videoq , pkt );
2005-08-14 01:29:34 +00:00
} else if ( pkt -> stream_index == is -> subtitle_stream ) {
packet_queue_put ( & is -> subtitleq , pkt );
2003-06-07 18:34:02 +00:00
} else {
av_free_packet ( pkt );
}
}
/* wait until the end */
while ( ! is -> abort_request ) {
SDL_Delay ( 100 );
}
2003-08-06 13:07:23 +00:00
ret = 0 ;
2003-06-07 18:34:02 +00:00
fail :
2003-07-17 10:29:50 +00:00
/* disable interrupting */
global_video_state = NULL ;
2003-06-07 18:34:02 +00:00
/* close each stream */
if ( is -> audio_stream >= 0 )
stream_component_close ( is , is -> audio_stream );
if ( is -> video_stream >= 0 )
stream_component_close ( is , is -> video_stream );
2005-08-14 01:29:34 +00:00
if ( is -> subtitle_stream >= 0 )
stream_component_close ( is , is -> subtitle_stream );
2003-08-06 13:07:23 +00:00
if ( is -> ic ) {
av_close_input_file ( is -> ic );
is -> ic = NULL ; /* safety */
}
2003-07-17 10:29:50 +00:00
url_set_interrupt_cb ( NULL );
2003-08-06 13:07:23 +00:00
if ( ret != 0 ) {
SDL_Event event ;
2005-12-17 18:14:38 +00:00
2003-08-06 13:07:23 +00:00
event . type = FF_QUIT_EVENT ;
event . user . data1 = is ;
SDL_PushEvent ( & event );
}
2003-06-07 18:34:02 +00:00
return 0 ;
}
2003-08-06 13:07:23 +00:00
static VideoState * stream_open ( const char * filename , AVInputFormat * iformat )
2003-06-07 18:34:02 +00:00
{
VideoState * is ;
is = av_mallocz ( sizeof ( VideoState ));
if ( ! is )
return NULL ;
2007-06-24 11:27:12 +00:00
av_strlcpy ( is -> filename , filename , sizeof ( is -> filename ));
2003-08-06 13:07:23 +00:00
is -> iformat = iformat ;
2003-06-07 18:34:02 +00:00
is -> ytop = 0 ;
is -> xleft = 0 ;
/* start video display */
is -> pictq_mutex = SDL_CreateMutex ();
is -> pictq_cond = SDL_CreateCond ();
2005-12-17 18:14:38 +00:00
2005-08-14 01:29:34 +00:00
is -> subpq_mutex = SDL_CreateMutex ();
is -> subpq_cond = SDL_CreateCond ();
2005-12-17 18:14:38 +00:00
2003-08-06 13:07:23 +00:00
is -> av_sync_type = av_sync_type ;
2003-06-07 18:34:02 +00:00
is -> parse_tid = SDL_CreateThread ( decode_thread , is );
if ( ! is -> parse_tid ) {
av_free ( is );
return NULL ;
}
return 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 -> parse_tid , NULL );
2010-03-11 02:35:04 +00:00
SDL_WaitThread ( is -> refresh_tid , NULL );
2003-06-07 18:34:02 +00:00
/* free all pictures */
for ( i = 0 ; i < VIDEO_PICTURE_QUEUE_SIZE ; i ++ ) {
vp = & is -> pictq [ i ];
2010-03-05 02:20:10 +00:00
#if CONFIG_AVFILTER
if ( vp -> picref ) {
avfilter_unref_pic ( vp -> picref );
vp -> picref = NULL ;
}
#endif
2003-06-07 18:34:02 +00:00
if ( vp -> bmp ) {
SDL_FreeYUVOverlay ( vp -> bmp );
vp -> bmp = NULL ;
}
}
SDL_DestroyMutex ( is -> pictq_mutex );
SDL_DestroyCond ( is -> pictq_cond );
2005-08-14 01:29:34 +00:00
SDL_DestroyMutex ( is -> subpq_mutex );
SDL_DestroyCond ( is -> subpq_cond );
2010-03-05 02:20:10 +00:00
#if !CONFIG_AVFILTER
2009-05-16 10:29:55 +00:00
if ( is -> img_convert_ctx )
sws_freeContext ( is -> img_convert_ctx );
2010-03-05 02:20:10 +00:00
#endif
2009-05-16 10:26:41 +00:00
av_free ( is );
2003-06-07 18:34:02 +00:00
}
2006-06-18 11:33:14 +00:00
static void stream_cycle_channel ( VideoState * is , int codec_type )
2003-08-06 13:07:23 +00:00
{
AVFormatContext * ic = is -> ic ;
int start_index , stream_index ;
AVStream * st ;
if ( codec_type == CODEC_TYPE_VIDEO )
start_index = is -> video_stream ;
2005-08-14 01:29:34 +00:00
else if ( codec_type == CODEC_TYPE_AUDIO )
2003-08-06 13:07:23 +00:00
start_index = is -> audio_stream ;
2005-08-14 01:29:34 +00:00
else
start_index = is -> subtitle_stream ;
if ( start_index < ( codec_type == CODEC_TYPE_SUBTITLE ? - 1 : 0 ))
2003-08-06 13:07:23 +00:00
return ;
stream_index = start_index ;
for (;;) {
if ( ++ stream_index >= is -> ic -> nb_streams )
2005-08-14 01:29:34 +00:00
{
if ( codec_type == CODEC_TYPE_SUBTITLE )
{
stream_index = - 1 ;
goto the_end ;
} else
stream_index = 0 ;
}
2003-08-06 13:07:23 +00:00
if ( stream_index == start_index )
return ;
st = ic -> streams [ stream_index ];
2005-07-17 22:24:36 +00:00
if ( st -> codec -> codec_type == codec_type ) {
2003-08-06 13:07:23 +00:00
/* check that parameters are OK */
switch ( codec_type ) {
case CODEC_TYPE_AUDIO :
2005-07-17 22:24:36 +00:00
if ( st -> codec -> sample_rate != 0 &&
st -> codec -> channels != 0 )
2003-08-06 13:07:23 +00:00
goto the_end ;
break ;
case CODEC_TYPE_VIDEO :
2005-08-14 01:29:34 +00:00
case CODEC_TYPE_SUBTITLE :
2003-08-06 13:07:23 +00:00
goto the_end ;
default :
break ;
}
}
}
the_end :
stream_component_close ( is , start_index );
stream_component_open ( is , stream_index );
}
2006-06-18 11:33:14 +00:00
static void toggle_full_screen ( void )
2003-06-07 18:34:02 +00:00
{
is_full_screen = ! is_full_screen ;
2006-06-14 21:02:55 +00:00
if ( ! fs_screen_width ) {
/* use default SDL method */
2006-12-31 20:04:08 +00:00
// SDL_WM_ToggleFullScreen(screen);
2003-06-07 18:34:02 +00:00
}
2006-12-31 20:04:08 +00:00
video_open ( cur_stream );
2003-06-07 18:34:02 +00:00
}
2006-06-18 11:33:14 +00:00
static void toggle_pause ( void )
2003-06-07 18:34:02 +00:00
{
if ( cur_stream )
stream_pause ( cur_stream );
2003-12-28 01:19:41 +00:00
step = 0 ;
}
2006-06-18 11:33:14 +00:00
static void step_to_next_frame ( void )
2003-12-28 01:19:41 +00:00
{
if ( cur_stream ) {
2007-08-27 08:06:48 +00:00
/* if the stream is paused unpause it, then step */
2003-12-28 01:19:41 +00:00
if ( cur_stream -> paused )
2007-08-27 08:06:48 +00:00
stream_pause ( cur_stream );
2003-12-28 01:19:41 +00:00
}
step = 1 ;
2003-06-07 18:34:02 +00:00
}
2006-06-18 11:33:14 +00:00
static void do_exit ( void )
2003-06-07 18:34:02 +00:00
{
2009-05-16 10:26:41 +00:00
int i ;
2003-06-07 18:34:02 +00:00
if ( cur_stream ) {
stream_close ( cur_stream );
cur_stream = NULL ;
}
2009-05-16 10:26:41 +00:00
for ( i = 0 ; i < CODEC_TYPE_NB ; i ++ )
av_free ( avcodec_opts [ i ]);
av_free ( avformat_opts );
av_free ( sws_opts );
2010-03-05 02:20:10 +00:00
#if CONFIG_AVFILTER
avfilter_uninit ();
#endif
2003-06-07 18:34:02 +00:00
if ( show_status )
printf ( " \n " );
SDL_Quit ();
exit ( 0 );
}
2006-06-18 11:33:14 +00:00
static void toggle_audio_display ( void )
2003-06-07 18:34:02 +00:00
{
if ( cur_stream ) {
2010-02-03 23:34:06 +00:00
int bgcolor = SDL_MapRGB ( screen -> format , 0x00 , 0x00 , 0x00 );
2010-02-05 02:06:38 +00:00
cur_stream -> show_audio = ( cur_stream -> show_audio + 1 ) % 3 ;
2010-02-03 23:34:06 +00:00
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 );
2003-06-07 18:34:02 +00:00
}
}
/* handle an event sent by the GUI */
2006-06-18 11:33:14 +00:00
static void event_loop ( void )
2003-06-07 18:34:02 +00:00
{
SDL_Event event ;
2003-11-28 19:19:01 +00:00
double incr , pos , frac ;
2003-06-07 18:34:02 +00:00
for (;;) {
2010-02-05 00:37:39 +00:00
double x ;
2003-06-07 18:34:02 +00:00
SDL_WaitEvent ( & event );
switch ( event . type ) {
case SDL_KEYDOWN :
switch ( event . key . keysym . sym ) {
case SDLK_ESCAPE :
case SDLK_q :
do_exit ();
break ;
case SDLK_f :
toggle_full_screen ();
break ;
case SDLK_p :
case SDLK_SPACE :
toggle_pause ();
break ;
2003-12-28 01:19:41 +00:00
case SDLK_s : //S: Step to next frame
step_to_next_frame ();
break ;
2003-06-07 18:34:02 +00:00
case SDLK_a :
2005-12-17 18:14:38 +00:00
if ( cur_stream )
2003-08-06 13:07:23 +00:00
stream_cycle_channel ( cur_stream , CODEC_TYPE_AUDIO );
break ;
case SDLK_v :
2005-12-17 18:14:38 +00:00
if ( cur_stream )
2003-08-06 13:07:23 +00:00
stream_cycle_channel ( cur_stream , CODEC_TYPE_VIDEO );
break ;
2005-08-14 01:29:34 +00:00
case SDLK_t :
2005-12-17 18:14:38 +00:00
if ( cur_stream )
2005-08-14 01:29:34 +00:00
stream_cycle_channel ( cur_stream , CODEC_TYPE_SUBTITLE );
break ;
2003-08-06 13:07:23 +00:00
case SDLK_w :
2003-06-07 18:34:02 +00:00
toggle_audio_display ();
break ;
2003-11-10 18:59:45 +00:00
case SDLK_LEFT :
incr = - 10.0 ;
goto do_seek ;
case SDLK_RIGHT :
incr = 10.0 ;
goto do_seek ;
case SDLK_UP :
incr = 60.0 ;
goto do_seek ;
case SDLK_DOWN :
incr = - 60.0 ;
do_seek :
if ( cur_stream ) {
2006-11-07 22:35:41 +00:00
if ( seek_by_bytes ) {
2010-02-02 16:51:02 +00:00
if ( cur_stream -> video_stream >= 0 && cur_stream -> video_current_pos >= 0 ){
pos = cur_stream -> video_current_pos ;
} else if ( cur_stream -> audio_stream >= 0 && cur_stream -> audio_pkt . pos >= 0 ){
pos = cur_stream -> audio_pkt . pos ;
} else
pos = url_ftell ( cur_stream -> ic -> pb );
2006-11-07 22:35:41 +00:00
if ( cur_stream -> ic -> bit_rate )
2010-02-03 15:19:19 +00:00
incr *= cur_stream -> ic -> bit_rate / 8.0 ;
2006-11-07 22:35:41 +00:00
else
incr *= 180000.0 ;
pos += incr ;
2010-01-30 23:19:59 +00:00
stream_seek ( cur_stream , pos , incr , 1 );
2006-11-07 22:35:41 +00:00
} else {
pos = get_master_clock ( cur_stream );
pos += incr ;
2010-01-30 23:19:59 +00:00
stream_seek ( cur_stream , ( int64_t )( pos * AV_TIME_BASE ), ( int64_t )( incr * AV_TIME_BASE ), 0 );
2006-11-07 22:35:41 +00:00
}
2003-11-10 18:59:45 +00:00
}
break ;
2003-06-07 18:34:02 +00:00
default :
break ;
}
break ;
2003-11-28 19:19:01 +00:00
case SDL_MOUSEBUTTONDOWN :
2010-02-05 00:37:39 +00:00
case SDL_MOUSEMOTION :
if ( event . type == SDL_MOUSEBUTTONDOWN ){
x = event . button . x ;
} else {
if ( event . motion . state != SDL_PRESSED )
break ;
x = event . motion . x ;
}
2005-12-22 01:10:11 +00:00
if ( cur_stream ) {
2010-01-30 23:19:59 +00:00
if ( seek_by_bytes || cur_stream -> ic -> duration <= 0 ){
uint64_t size = url_fsize ( cur_stream -> ic -> pb );
2010-02-05 00:37:39 +00:00
stream_seek ( cur_stream , size * x / cur_stream -> width , 0 , 1 );
2010-01-30 23:19:59 +00:00
} else {
2010-01-31 18:07:58 +00:00
int64_t ts ;
int ns , hh , mm , ss ;
int tns , thh , tmm , tss ;
tns = cur_stream -> ic -> duration / 1000000LL ;
thh = tns / 3600 ;
tmm = ( tns % 3600 ) / 60 ;
tss = ( tns % 60 );
2010-02-05 00:37:39 +00:00
frac = x / cur_stream -> width ;
2010-01-31 18:07:58 +00:00
ns = frac * tns ;
hh = ns / 3600 ;
mm = ( ns % 3600 ) / 60 ;
ss = ( ns % 60 );
fprintf ( stderr , "Seek to %2.0f%% (%2d:%02d:%02d) of total duration (%2d:%02d:%02d) \n " , frac * 100 ,
hh , mm , ss , thh , tmm , tss );
ts = frac * cur_stream -> ic -> duration ;
if ( cur_stream -> ic -> start_time != AV_NOPTS_VALUE )
ts += cur_stream -> ic -> start_time ;
stream_seek ( cur_stream , ts , 0 , 0 );
2010-01-30 23:19:59 +00:00
}
2005-12-22 01:10:11 +00:00
}
break ;
2003-06-07 18:34:02 +00:00
case SDL_VIDEORESIZE :
if ( cur_stream ) {
2005-12-17 18:14:38 +00:00
screen = SDL_SetVideoMode ( event . resize . w , event . resize . h , 0 ,
2003-06-07 18:34:02 +00:00
SDL_HWSURFACE | SDL_RESIZABLE | SDL_ASYNCBLIT | SDL_HWACCEL );
2006-12-31 20:09:10 +00:00
screen_width = cur_stream -> width = event . resize . w ;
screen_height = cur_stream -> height = event . resize . h ;
2003-06-07 18:34:02 +00:00
}
break ;
case SDL_QUIT :
2003-08-06 13:07:23 +00:00
case FF_QUIT_EVENT :
2003-06-07 18:34:02 +00:00
do_exit ();
break ;
case FF_ALLOC_EVENT :
2006-12-30 11:22:46 +00:00
video_open ( event . user . data1 );
2003-06-07 18:34:02 +00:00
alloc_picture ( event . user . data1 );
break ;
case FF_REFRESH_EVENT :
video_refresh_timer ( event . user . data1 );
2010-03-11 02:35:04 +00:00
cur_stream -> refresh = 0 ;
2003-06-07 18:34:02 +00:00
break ;
default :
break ;
}
}
}
2007-02-22 00:33:09 +00:00
static void opt_frame_size ( const char * arg )
{
2007-06-18 08:08:43 +00:00
if ( av_parse_video_frame_size ( & frame_width , & frame_height , arg ) < 0 ) {
2007-02-22 00:33:09 +00:00
fprintf ( stderr , "Incorrect frame size \n " );
exit ( 1 );
}
if (( frame_width % 2 ) != 0 || ( frame_height % 2 ) != 0 ) {
fprintf ( stderr , "Frame size must be a multiple of 2 \n " );
exit ( 1 );
}
}
2008-03-22 18:50:23 +00:00
static int opt_width ( const char * opt , const char * arg )
2003-06-07 18:34:02 +00:00
{
2008-03-22 18:50:23 +00:00
screen_width = parse_number_or_die ( opt , arg , OPT_INT64 , 1 , INT_MAX );
return 0 ;
2003-06-07 18:34:02 +00:00
}
2008-03-22 18:50:23 +00:00
static int opt_height ( const char * opt , const char * arg )
2003-06-07 18:34:02 +00:00
{
2008-03-22 18:50:23 +00:00
screen_height = parse_number_or_die ( opt , arg , OPT_INT64 , 1 , INT_MAX );
return 0 ;
2003-06-07 18:34:02 +00:00
}
static void opt_format ( const char * arg )
{
file_iformat = av_find_input_format ( arg );
if ( ! file_iformat ) {
fprintf ( stderr , "Unknown input format: %s \n " , arg );
exit ( 1 );
}
}
2003-09-16 19:37:27 +00:00
2007-02-22 00:33:09 +00:00
static void opt_frame_pix_fmt ( const char * arg )
{
2010-01-30 19:10:26 +00:00
frame_pix_fmt = av_get_pix_fmt ( arg );
2007-02-22 00:33:09 +00:00
}
2008-06-12 11:33:53 +00:00
static int opt_sync ( const char * opt , const char * arg )
2003-08-06 13:07:23 +00:00
{
if ( ! strcmp ( arg , "audio" ))
av_sync_type = AV_SYNC_AUDIO_MASTER ;
else if ( ! strcmp ( arg , "video" ))
av_sync_type = AV_SYNC_VIDEO_MASTER ;
else if ( ! strcmp ( arg , "ext" ))
av_sync_type = AV_SYNC_EXTERNAL_CLOCK ;
2007-08-09 12:13:29 +00:00
else {
2008-06-12 11:33:53 +00:00
fprintf ( stderr , "Unknown value for %s: %s \n " , opt , arg );
2007-08-09 12:13:29 +00:00
exit ( 1 );
}
2008-06-12 11:33:53 +00:00
return 0 ;
2003-08-06 13:07:23 +00:00
}
2008-04-01 12:22:33 +00:00
static int opt_seek ( const char * opt , const char * arg )
2003-11-10 18:59:45 +00:00
{
2008-04-01 12:22:33 +00:00
start_time = parse_time_or_die ( opt , arg , 1 );
return 0 ;
2003-11-10 18:59:45 +00:00
}
2008-03-22 18:50:23 +00:00
static int opt_debug ( const char * opt , const char * arg )
2003-11-28 15:14:24 +00:00
{
2007-12-12 21:48:50 +00:00
av_log_set_level ( 99 );
2008-03-22 18:50:23 +00:00
debug = parse_number_or_die ( opt , arg , OPT_INT64 , 0 , INT_MAX );
return 0 ;
2003-11-28 15:14:24 +00:00
}
2005-12-17 18:14:38 +00:00
2008-03-22 18:50:23 +00:00
static int opt_vismv ( const char * opt , const char * arg )
2003-12-30 02:12:12 +00:00
{
2008-03-22 18:50:23 +00:00
debug_mv = parse_number_or_die ( opt , arg , OPT_INT64 , INT_MIN , INT_MAX );
return 0 ;
2003-12-30 02:12:12 +00:00
}
2004-02-23 20:56:56 +00:00
2008-03-22 18:50:23 +00:00
static int opt_thread_count ( const char * opt , const char * arg )
2004-02-23 20:56:56 +00:00
{
2008-03-22 18:50:23 +00:00
thread_count = parse_number_or_die ( opt , arg , OPT_INT64 , 0 , INT_MAX );
2009-01-13 23:44:16 +00:00
#if !HAVE_THREADS
2004-02-23 20:56:56 +00:00
fprintf ( stderr , "Warning: not compiled with thread support, using thread emulation \n " );
#endif
2008-03-22 18:50:23 +00:00
return 0 ;
2004-02-23 20:56:56 +00:00
}
2005-12-17 18:14:38 +00:00
2008-05-01 15:10:44 +00:00
static const OptionDef options [] = {
2009-11-29 23:12:19 +00:00
#include "cmdutils_common_opts.h"
2008-03-22 18:50:23 +00:00
{ "x" , HAS_ARG | OPT_FUNC2 , {( void * ) opt_width }, "force displayed width" , "width" },
{ "y" , HAS_ARG | OPT_FUNC2 , {( void * ) opt_height }, "force displayed height" , "height" },
2007-02-22 00:33:09 +00:00
{ "s" , HAS_ARG | OPT_VIDEO , {( void * ) opt_frame_size }, "set frame size (WxH or abbreviation)" , "size" },
2003-08-06 13:07:23 +00:00
{ "fs" , OPT_BOOL , {( void * ) & is_full_screen }, "force full screen" },
2003-06-07 18:34:02 +00:00
{ "an" , OPT_BOOL , {( void * ) & audio_disable }, "disable audio" },
{ "vn" , OPT_BOOL , {( void * ) & video_disable }, "disable video" },
2010-02-23 16:56:23 +00:00
{ "ast" , OPT_INT | HAS_ARG | OPT_EXPERT , {( void * ) & wanted_stream [ CODEC_TYPE_AUDIO ]}, "select desired audio stream" , "stream_number" },
{ "vst" , OPT_INT | HAS_ARG | OPT_EXPERT , {( void * ) & wanted_stream [ CODEC_TYPE_VIDEO ]}, "select desired video stream" , "stream_number" },
{ "sst" , OPT_INT | HAS_ARG | OPT_EXPERT , {( void * ) & wanted_stream [ CODEC_TYPE_SUBTITLE ]}, "select desired subtitle stream" , "stream_number" },
2008-04-01 12:22:33 +00:00
{ "ss" , HAS_ARG | OPT_FUNC2 , {( void * ) & opt_seek }, "seek to a given position in seconds" , "pos" },
2010-02-05 04:42:20 +00:00
{ "bytes" , OPT_INT | HAS_ARG , {( void * ) & seek_by_bytes }, "seek by bytes 0=off 1=on -1=auto" , "val" },
2003-06-07 18:34:02 +00:00
{ "nodisp" , OPT_BOOL , {( void * ) & display_disable }, "disable graphical display" },
{ "f" , HAS_ARG , {( void * ) opt_format }, "force format" , "fmt" },
2007-02-22 00:33:09 +00:00
{ "pix_fmt" , HAS_ARG | OPT_EXPERT | OPT_VIDEO , {( void * ) opt_frame_pix_fmt }, "set pixel format" , "format" },
2009-08-04 09:26:33 +00:00
{ "stats" , OPT_BOOL | OPT_EXPERT , {( void * ) & show_status }, "show status" , "" },
2008-03-22 18:50:23 +00:00
{ "debug" , HAS_ARG | OPT_FUNC2 | OPT_EXPERT , {( void * ) opt_debug }, "print specific debug info" , "" },
2004-04-30 18:54:36 +00:00
{ "bug" , OPT_INT | HAS_ARG | OPT_EXPERT , {( void * ) & workaround_bugs }, "workaround bugs" , "" },
2008-03-22 18:50:23 +00:00
{ "vismv" , HAS_ARG | OPT_FUNC2 | OPT_EXPERT , {( void * ) opt_vismv }, "visualize motion vectors" , "" },
2004-09-02 15:30:46 +00:00
{ "fast" , OPT_BOOL | OPT_EXPERT , {( void * ) & fast }, "non spec compliant optimizations" , "" },
2005-08-15 14:22:43 +00:00
{ "genpts" , OPT_BOOL | OPT_EXPERT , {( void * ) & genpts }, "generate pts" , "" },
2010-02-01 03:12:01 +00:00
{ "drp" , OPT_INT | HAS_ARG | OPT_EXPERT , {( void * ) & decoder_reorder_pts }, "let decoder reorder pts 0=off 1=on -1=auto" , "" },
2004-09-25 23:18:58 +00:00
{ "lowres" , OPT_INT | HAS_ARG | OPT_EXPERT , {( void * ) & lowres }, "" , "" },
2005-07-14 21:39:36 +00:00
{ "skiploop" , OPT_INT | HAS_ARG | OPT_EXPERT , {( void * ) & skip_loop_filter }, "" , "" },
{ "skipframe" , OPT_INT | HAS_ARG | OPT_EXPERT , {( void * ) & skip_frame }, "" , "" },
{ "skipidct" , OPT_INT | HAS_ARG | OPT_EXPERT , {( void * ) & skip_idct }, "" , "" },
2004-09-25 23:18:58 +00:00
{ "idct" , OPT_INT | HAS_ARG | OPT_EXPERT , {( void * ) & idct }, "set idct algo" , "algo" },
2008-09-08 18:18:49 +00:00
{ "er" , OPT_INT | HAS_ARG | OPT_EXPERT , {( void * ) & error_recognition }, "set error detection threshold (0-4)" , "threshold" },
2005-07-17 10:18:10 +00:00
{ "ec" , OPT_INT | HAS_ARG | OPT_EXPERT , {( void * ) & error_concealment }, "set error concealment options" , "bit_mask" },
2008-06-12 11:33:53 +00:00
{ "sync" , HAS_ARG | OPT_FUNC2 | OPT_EXPERT , {( void * ) opt_sync }, "set audio-video sync. type (type=audio/video/ext)" , "type" },
2008-03-22 18:50:23 +00:00
{ "threads" , HAS_ARG | OPT_FUNC2 | OPT_EXPERT , {( void * ) opt_thread_count }, "thread count" , "count" },
2010-02-04 01:54:24 +00:00
{ "autoexit" , OPT_BOOL | OPT_EXPERT , {( void * ) & autoexit }, "exit at the end" , "" },
2010-03-23 17:39:51 +00:00
{ "loop" , OPT_INT | HAS_ARG | OPT_EXPERT , {( void * ) & loop }, "set number of times the playback shall be looped" , "loop count" },
2010-03-11 02:35:04 +00:00
{ "framedrop" , OPT_BOOL | OPT_EXPERT , {( void * ) & framedrop }, "drop frames when cpu is too slow" , "" },
2010-03-17 23:39:18 +00:00
{ "window_title" , OPT_STRING | HAS_ARG , {( void * ) & window_title }, "set window title" , "window title" },
2010-03-05 02:20:10 +00:00
#if CONFIG_AVFILTER
{ "vfilters" , OPT_STRING | HAS_ARG , {( void * ) & vfilters }, "video filters" , "filter list" },
#endif
2010-03-11 11:25:51 +00:00
{ "rdftspeed" , OPT_INT | HAS_ARG | OPT_AUDIO | OPT_EXPERT , {( void * ) & rdftspeed }, "rdft speed" , "msecs" },
2008-09-28 19:39:18 +00:00
{ "default" , OPT_FUNC2 | HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT , {( void * ) opt_default }, "generic catch all option" , "" },
2003-06-07 18:34:02 +00:00
{ NULL , },
};
2009-12-21 02:12:34 +00:00
static void show_usage ( void )
2003-06-07 18:34:02 +00:00
{
2009-12-21 02:09:17 +00:00
printf ( "Simple media player \n " );
printf ( "usage: ffplay [options] input_file \n " );
2003-06-07 18:34:02 +00:00
printf ( " \n " );
2009-12-21 02:12:34 +00:00
}
static void show_help ( void )
{
show_usage ();
2003-08-24 15:29:48 +00:00
show_help_options ( options , "Main options: \n " ,
OPT_EXPERT , 0 );
show_help_options ( options , " \n Advanced options: \n " ,
OPT_EXPERT , OPT_EXPERT );
2003-06-07 18:34:02 +00:00
printf ( " \n While playing: \n "
"q, ESC quit \n "
"f toggle full screen \n "
"p, SPC pause \n "
2003-08-06 13:07:23 +00:00
"a cycle audio channel \n "
"v cycle video channel \n "
2005-08-14 01:29:34 +00:00
"t cycle subtitle channel \n "
2003-08-06 13:07:23 +00:00
"w show audio waves \n "
2003-11-10 18:59:45 +00:00
"left/right seek backward/forward 10 seconds \n "
"down/up seek backward/forward 1 minute \n "
2003-11-28 19:19:01 +00:00
"mouse click seek to percentage in file corresponding to fraction of width \n "
2003-06-07 18:34:02 +00:00
);
}
2008-05-01 15:10:44 +00:00
static void opt_input_file ( const char * filename )
2003-06-07 18:34:02 +00:00
{
2010-03-01 23:10:27 +00:00
if ( input_filename ) {
fprintf ( stderr , "Argument '%s' provided as input filename, but '%s' was already specified. \n " ,
filename , input_filename );
exit ( 1 );
}
2003-08-29 20:22:16 +00:00
if ( ! strcmp ( filename , "-" ))
2007-08-14 09:14:31 +00:00
filename = "pipe:" ;
2003-06-07 18:34:02 +00:00
input_filename = filename ;
}
/* Called from the main */
int main ( int argc , char ** argv )
{
2008-09-28 19:39:18 +00:00
int flags , i ;
2005-12-17 18:14:38 +00:00
2003-06-07 18:34:02 +00:00
/* register all codecs, demux and protocols */
2007-11-22 16:10:02 +00:00
avcodec_register_all ();
avdevice_register_all ();
2010-03-05 02:20:10 +00:00
#if CONFIG_AVFILTER
avfilter_register_all ();
#endif
2003-06-07 18:34:02 +00:00
av_register_all ();
2008-09-28 19:39:18 +00:00
for ( i = 0 ; i < CODEC_TYPE_NB ; i ++ ){
2009-03-03 23:32:10 +00:00
avcodec_opts [ i ] = avcodec_alloc_context2 ( i );
2008-09-28 19:39:18 +00:00
}
2009-02-08 08:27:26 +00:00
avformat_opts = avformat_alloc_context ();
2010-03-05 02:20:10 +00:00
#if !CONFIG_AVFILTER
2008-09-28 19:39:18 +00:00
sws_opts = sws_getContext ( 16 , 16 , 0 , 16 , 16 , 0 , sws_flags , NULL , NULL , NULL );
2010-03-05 02:20:10 +00:00
#endif
2008-09-28 19:39:18 +00:00
2008-05-29 08:48:51 +00:00
show_banner ();
2007-09-27 13:52:33 +00:00
2007-08-14 13:58:28 +00:00
parse_options ( argc , argv , options , opt_input_file );
2003-06-07 18:34:02 +00:00
2007-08-09 12:13:29 +00:00
if ( ! input_filename ) {
2009-12-21 02:15:46 +00:00
show_usage ();
2008-11-17 23:15:29 +00:00
fprintf ( stderr , "An input file must be specified \n " );
2009-12-21 02:15:46 +00:00
fprintf ( stderr , "Use -h to get full help or, even better, run 'man ffplay' \n " );
2007-08-09 12:13:29 +00:00
exit ( 1 );
}
2003-06-07 18:34:02 +00:00
if ( display_disable ) {
video_disable = 1 ;
}
2003-06-14 11:09:57 +00:00
flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER ;
2007-08-27 09:17:03 +00:00
#if !defined(__MINGW32__) && !defined(__APPLE__)
flags |= SDL_INIT_EVENTTHREAD ; /* Not supported on Windows or Mac OS X */
2003-06-14 11:09:57 +00:00
#endif
2003-06-07 18:34:02 +00:00
if ( SDL_Init ( flags )) {
2003-09-29 00:14:13 +00:00
fprintf ( stderr , "Could not initialize SDL - %s \n " , SDL_GetError ());
2003-06-07 18:34:02 +00:00
exit ( 1 );
}
if ( ! display_disable ) {
2009-01-13 23:44:16 +00:00
#if HAVE_SDL_VIDEO_SIZE
2006-06-09 22:43:21 +00:00
const SDL_VideoInfo * vi = SDL_GetVideoInfo ();
fs_screen_width = vi -> current_w ;
fs_screen_height = vi -> current_h ;
2006-06-14 21:02:55 +00:00
#endif
2003-06-07 18:34:02 +00:00
}
SDL_EventState ( SDL_ACTIVEEVENT , SDL_IGNORE );
SDL_EventState ( SDL_SYSWMEVENT , SDL_IGNORE );
SDL_EventState ( SDL_USEREVENT , SDL_IGNORE );
2006-11-16 11:58:27 +00:00
av_init_packet ( & flush_pkt );
flush_pkt . data = "FLUSH" ;
2003-08-06 13:07:23 +00:00
cur_stream = stream_open ( input_filename , file_iformat );
2003-06-07 18:34:02 +00:00
event_loop ();
/* never returns */
return 0 ;
}