2003-06-07 20:34:02 +02:00
/*
2011-04-23 15:19:17 +02:00
* ffplay : Simple Media Player based on the Libav libraries
2003-06-07 20:34:02 +02:00
* Copyright ( c ) 2003 Fabrice Bellard
*
2011-03-18 18:35:10 +01:00
* This file is part of Libav .
2006-10-07 17:30:46 +02:00
*
2011-03-18 18:35:10 +01:00
* Libav is free software ; you can redistribute it and / or
2003-06-07 20:34:02 +02:00
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
2006-10-07 17:30:46 +02:00
* version 2.1 of the License , or ( at your option ) any later version .
2003-06-07 20:34:02 +02:00
*
2011-03-18 18:35:10 +01:00
* Libav is distributed in the hope that it will be useful ,
2003-06-07 20:34:02 +02: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
2011-03-18 18:35:10 +01:00
* License along with Libav ; if not , write to the Free Software
2006-01-12 23:43:26 +01:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2003-06-07 20:34:02 +02:00
*/
2007-06-10 16:34:56 +02:00
2009-11-12 00:46:11 +01:00
# include "config.h"
2010-03-11 03:32:03 +01:00
# include <inttypes.h>
2007-07-02 09:43:23 +02:00
# include <math.h>
# include <limits.h>
2008-05-09 13:56:36 +02:00
# include "libavutil/avstring.h"
2010-07-01 20:49:44 +02:00
# include "libavutil/colorspace.h"
2010-01-30 20:10:26 +01:00
# include "libavutil/pixdesc.h"
2011-02-07 14:37:08 +01:00
# include "libavutil/imgutils.h"
# include "libavutil/parseutils.h"
# include "libavutil/samplefmt.h"
2008-05-09 13:56:36 +02:00
# include "libavformat/avformat.h"
# include "libavdevice/avdevice.h"
# include "libswscale/swscale.h"
2008-08-02 07:01:30 +02:00
# include "libavcodec/audioconvert.h"
2011-02-03 14:58:59 +01:00
# include "libavutil/opt.h"
2010-03-07 22:56:48 +01:00
# include "libavcodec/avfft.h"
2003-06-07 20:34:02 +02:00
2010-03-05 03:20:10 +01:00
# if CONFIG_AVFILTER
# include "libavfilter / avfilter.h"
# include "libavfilter / avfiltergraph.h"
# endif
2003-06-07 20:34:02 +02:00
# include "cmdutils.h"
# include <SDL.h>
# include <SDL_thread.h>
2006-07-11 23:45:45 +02:00
# ifdef __MINGW32__
2003-06-14 13:09:57 +02:00
# undef main /* We don't want SDL to override our main() */
# endif
2010-03-11 03:35:04 +01:00
# include <unistd.h>
# include <assert.h>
2011-04-23 15:19:17 +02:00
const char program_name [ ] = " ffplay " ;
2008-05-29 10:48:51 +02:00
const int program_birth_year = 2003 ;
2007-09-27 15:52:33 +02:00
2011-01-22 15:35:00 +01:00
//#define DEBUG
2003-08-06 15:07:23 +02:00
//#define DEBUG_SYNC
2010-01-30 22:27:17 +01:00
# define MAX_QUEUE_SIZE (15 * 1024 * 1024)
# define MIN_AUDIOQ_SIZE (20 * 16 * 1024)
# define MIN_FRAMES 5
2003-06-07 20:34:02 +02:00
2003-08-06 15:07:23 +02: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 02:38:09 +02:00
# define AV_SYNC_THRESHOLD 0.01
2003-08-06 15:07:23 +02:00
/* no AV correction is done if too big error */
# define AV_NOSYNC_THRESHOLD 10.0
2010-03-11 03:35:04 +01:00
# define FRAME_SKIP_FACTOR 0.05
2003-08-06 15:07:23 +02: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 20:34:02 +02:00
/* NOTE: the size must be big enough to compensate the hardware audio buffersize size */
# define SAMPLE_ARRAY_SIZE (2*65536)
2006-08-03 18:55:36 +02:00
static int sws_flags = SWS_BICUBIC ;
2003-06-07 20:34:02 +02: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 16:45:46 +01:00
# define VIDEO_PICTURE_QUEUE_SIZE 2
2005-08-14 03:29:34 +02:00
# define SUBPICTURE_QUEUE_SIZE 4
2003-06-07 20:34:02 +02:00
typedef struct VideoPicture {
2005-09-20 20:47:08 +02:00
double pts ; ///<presentation time stamp for this picture
2010-03-11 03:35:04 +01:00
double target_clock ; ///<av_gettime() time at which this should be displayed ideally
2010-02-02 17:51:02 +01:00
int64_t pos ; ///<byte position in file
2003-06-07 20:34:02 +02:00
SDL_Overlay * bmp ;
int width , height ; /* source height & width */
int allocated ;
2010-03-05 03:20:10 +01:00
enum PixelFormat pix_fmt ;
# if CONFIG_AVFILTER
2010-08-07 03:15:19 +02:00
AVFilterBufferRef * picref ;
2010-03-05 03:20:10 +01:00
# endif
2003-06-07 20:34:02 +02:00
} VideoPicture ;
2005-08-14 03:29:34 +02:00
typedef struct SubPicture {
double pts ; /* presentation time stamp for this picture */
AVSubtitle sub ;
} SubPicture ;
2003-06-07 20:34:02 +02:00
enum {
AV_SYNC_AUDIO_MASTER , /* default choice */
AV_SYNC_VIDEO_MASTER ,
2003-08-06 15:07:23 +02:00
AV_SYNC_EXTERNAL_CLOCK , /* synchronize to an external clock */
2003-06-07 20:34:02 +02:00
} ;
typedef struct VideoState {
SDL_Thread * parse_tid ;
SDL_Thread * video_tid ;
2010-03-11 03:35:04 +01:00
SDL_Thread * refresh_tid ;
2003-08-06 15:07:23 +02:00
AVInputFormat * iformat ;
2003-06-07 20:34:02 +02:00
int no_background ;
int abort_request ;
int paused ;
2003-07-17 12:29:50 +02:00
int last_paused ;
2003-11-10 19:59:45 +01:00
int seek_req ;
2004-10-11 00:05:43 +02:00
int seek_flags ;
2003-11-10 19:59:45 +01:00
int64_t seek_pos ;
2009-03-14 17:24:30 +01:00
int64_t seek_rel ;
2010-02-01 11:32:17 +01:00
int read_pause_return ;
2003-06-07 20:34:02 +02:00
AVFormatContext * ic ;
int dtg_active_format ;
int audio_stream ;
2005-12-17 19:14:38 +01:00
2003-06-07 20:34:02 +02:00
int av_sync_type ;
2003-08-06 15:07:23 +02:00
double external_clock ; /* external clock base */
int64_t external_clock_time ;
2005-12-17 19:14:38 +01:00
2003-08-06 15:07:23 +02: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 20:34:02 +02: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 04:25:11 +01: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 07:01:30 +02:00
uint8_t * audio_buf ;
2004-10-06 10:50:46 +02:00
unsigned int audio_buf_size ; /* in bytes */
2003-06-07 20:34:02 +02:00
int audio_buf_index ; /* in bytes */
2009-04-10 14:07:06 +02:00
AVPacket audio_pkt_temp ;
2003-06-07 20:34:02 +02:00
AVPacket audio_pkt ;
2010-11-12 12:04:40 +01:00
enum AVSampleFormat audio_src_fmt ;
2008-08-02 07:01:30 +02:00
AVAudioConvert * reformat_ctx ;
2005-12-17 19:14:38 +01:00
2003-06-07 20:34:02 +02:00
int show_audio ; /* if true, display audio samples */
int16_t sample_array [ SAMPLE_ARRAY_SIZE ] ;
int sample_array_index ;
2003-06-09 22:48:06 +02:00
int last_i_start ;
2010-03-07 22:56:48 +01:00
RDFTContext * rdft ;
2010-02-05 03:06:38 +01:00
int rdft_bits ;
2010-06-30 00:41:20 +02:00
FFTSample * rdft_data ;
2010-02-05 03:06:38 +01:00
int xpos ;
2005-12-17 19:14:38 +01:00
2005-08-14 03:29:34 +02: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 19:14:38 +01:00
2003-08-06 15:07:23 +02:00
double frame_timer ;
double frame_last_pts ;
double frame_last_delay ;
2005-12-17 19:14:38 +01:00
double video_clock ; ///<pts of last decoded frame / predicted pts of next decoded frame
2003-06-07 20:34:02 +02:00
int video_stream ;
AVStream * video_st ;
PacketQueue videoq ;
2005-09-20 20:47:08 +02:00
double video_current_pts ; ///<current displayed pts (different from video_clock if frame fifos are used)
2010-02-01 11:55:51 +01: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 17:51:02 +01:00
int64_t video_current_pos ; ///<current displayed file pos
2003-06-07 20:34:02 +02: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 03:20:10 +01:00
# if !CONFIG_AVFILTER
2009-05-16 12:29:55 +02:00
struct SwsContext * img_convert_ctx ;
2010-03-05 03:20:10 +01:00
# endif
2005-12-17 19:14:38 +01:00
2003-06-07 20:34:02 +02:00
// QETimer *video_timer;
char filename [ 1024 ] ;
int width , height , xleft , ytop ;
2010-01-31 19:54:32 +01:00
2010-09-28 04:05:12 +02:00
PtsCorrectionContext pts_ctx ;
2010-01-31 19:54:32 +01:00
2010-03-05 03:20:10 +01:00
# if CONFIG_AVFILTER
AVFilterContext * out_video_filter ; ///<the last filter in the video chain
# endif
2010-03-11 03:35:04 +01:00
float skip_frames ;
float skip_frames_index ;
int refresh ;
2003-06-07 20:34:02 +02:00
} VideoState ;
2008-05-01 17:10:44 +02:00
static void show_help ( void ) ;
2003-06-07 20:34:02 +02:00
/* options specified by the user */
static AVInputFormat * file_iformat ;
static const char * input_filename ;
2010-03-18 00:39:18 +01:00
static const char * window_title ;
2003-06-07 20:34:02 +02:00
static int fs_screen_width ;
static int fs_screen_height ;
2006-12-30 12:22:46 +01:00
static int screen_width = 0 ;
static int screen_height = 0 ;
2007-02-22 01:33:09 +01:00
static int frame_width = 0 ;
static int frame_height = 0 ;
static enum PixelFormat frame_pix_fmt = PIX_FMT_NONE ;
2003-06-07 20:34:02 +02:00
static int audio_disable ;
static int video_disable ;
2010-03-31 01:30:55 +02:00
static int wanted_stream [ AVMEDIA_TYPE_NB ] = {
[ AVMEDIA_TYPE_AUDIO ] = - 1 ,
[ AVMEDIA_TYPE_VIDEO ] = - 1 ,
[ AVMEDIA_TYPE_SUBTITLE ] = - 1 ,
2010-02-23 17:56:23 +01:00
} ;
2010-02-02 19:02:16 +01:00
static int seek_by_bytes = - 1 ;
2003-06-07 20:34:02 +02:00
static int display_disable ;
2009-07-30 23:54:50 +02:00
static int show_status = 1 ;
2003-08-06 15:07:23 +02:00
static int av_sync_type = AV_SYNC_AUDIO_MASTER ;
2003-11-10 19:59:45 +01:00
static int64_t start_time = AV_NOPTS_VALUE ;
2010-04-01 22:56:23 +02:00
static int64_t duration = AV_NOPTS_VALUE ;
2003-11-28 16:14:24 +01:00
static int debug = 0 ;
2003-12-30 03:12:12 +01:00
static int debug_mv = 0 ;
2003-12-28 02:19:41 +01:00
static int step = 0 ;
2004-02-23 21:56:56 +01:00
static int thread_count = 1 ;
2004-04-30 20:54:36 +02:00
static int workaround_bugs = 1 ;
2004-09-02 17:30:46 +02:00
static int fast = 0 ;
2005-08-15 16:22:43 +02:00
static int genpts = 0 ;
2004-09-26 01:18:58 +02:00
static int lowres = 0 ;
static int idct = FF_IDCT_AUTO ;
2005-07-14 23:39:36 +02: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 20:18:49 +02:00
static int error_recognition = FF_ER_CAREFUL ;
2005-07-17 12:18:10 +02:00
static int error_concealment = 3 ;
2010-01-31 19:54:32 +01:00
static int decoder_reorder_pts = - 1 ;
2010-02-04 02:54:24 +01:00
static int autoexit ;
2010-07-04 14:43:12 +02:00
static int exit_on_keydown ;
static int exit_on_mousedown ;
2010-03-23 18:39:51 +01:00
static int loop = 1 ;
2010-03-11 03:35:04 +01:00
static int framedrop = 1 ;
2010-03-11 12:25:51 +01:00
static int rdftspeed = 20 ;
2010-03-05 03:20:10 +01:00
# if CONFIG_AVFILTER
static char * vfilters = NULL ;
# endif
2003-06-07 20:34:02 +02:00
/* current context */
static int is_full_screen ;
static VideoState * cur_stream ;
2003-06-09 22:48:06 +02:00
static int64_t audio_callback_time ;
2003-06-07 20:34:02 +02:00
2008-10-04 11:23:18 +02:00
static AVPacket flush_pkt ;
2006-11-16 12:58:27 +01:00
2003-06-07 20:34:02 +02:00
# define FF_ALLOC_EVENT (SDL_USEREVENT)
# define FF_REFRESH_EVENT (SDL_USEREVENT + 1)
2003-08-06 15:07:23 +02:00
# define FF_QUIT_EVENT (SDL_USEREVENT + 2)
2003-06-07 20:34:02 +02:00
2008-10-04 11:23:18 +02:00
static SDL_Surface * screen ;
2003-06-07 20:34:02 +02:00
2010-02-01 13:22:12 +01:00
static int packet_queue_put ( PacketQueue * q , AVPacket * pkt ) ;
2003-06-07 20:34:02 +02: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 13:22:12 +01:00
packet_queue_put ( q , & flush_pkt ) ;
2003-06-07 20:34:02 +02:00
}
2003-11-10 19:59:45 +01:00
static void packet_queue_flush ( PacketQueue * q )
2003-06-07 20:34:02 +02:00
{
AVPacketList * pkt , * pkt1 ;
2005-06-24 10:32:55 +02:00
SDL_LockMutex ( q - > mutex ) ;
2003-06-07 20:34:02 +02:00
for ( pkt = q - > first_pkt ; pkt ! = NULL ; pkt = pkt1 ) {
pkt1 = pkt - > next ;
av_free_packet ( & pkt - > pkt ) ;
2004-08-17 01:17:32 +02:00
av_freep ( & pkt ) ;
2003-06-07 20:34:02 +02:00
}
2003-11-10 19:59:45 +01:00
q - > last_pkt = NULL ;
q - > first_pkt = NULL ;
q - > nb_packets = 0 ;
q - > size = 0 ;
2005-06-24 10:32:55 +02:00
SDL_UnlockMutex ( q - > mutex ) ;
2003-11-10 19:59:45 +01:00
}
static void packet_queue_end ( PacketQueue * q )
{
packet_queue_flush ( q ) ;
2003-06-07 20:34:02 +02:00
SDL_DestroyMutex ( q - > mutex ) ;
SDL_DestroyCond ( q - > cond ) ;
}
static int packet_queue_put ( PacketQueue * q , AVPacket * pkt )
{
AVPacketList * pkt1 ;
2003-11-10 19:59:45 +01:00
/* duplicate the packet */
2006-11-16 12:58:27 +01:00
if ( pkt ! = & flush_pkt & & av_dup_packet ( pkt ) < 0 )
2003-11-10 19:59:45 +01:00
return - 1 ;
2005-12-17 19:14:38 +01:00
2003-06-07 20:34:02 +02:00
pkt1 = av_malloc ( sizeof ( AVPacketList ) ) ;
if ( ! pkt1 )
return - 1 ;
pkt1 - > pkt = * pkt ;
pkt1 - > next = NULL ;
2003-11-10 19:59:45 +01:00
2003-06-07 20:34:02 +02: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 17:01:52 +01:00
q - > size + = pkt1 - > pkt . size + sizeof ( * pkt1 ) ;
2003-06-07 20:34:02 +02: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 19:14:38 +01:00
2003-06-07 20:34:02 +02: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 19:14:38 +01:00
2003-06-07 20:34:02 +02: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 17:01:52 +01:00
q - > size - = pkt1 - > pkt . size + sizeof ( * pkt1 ) ;
2003-06-07 20:34:02 +02: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 19:14:38 +01:00
static inline void fill_rectangle ( SDL_Surface * screen ,
2003-06-07 20:34:02 +02: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 ) ;
}
2005-08-14 03:29:34 +02: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 20:58:35 +02:00
unsigned int val = ( ( const uint32_t * ) ( pal ) ) [ * ( const uint8_t * ) ( s ) ] ; \
2005-08-14 03:29:34 +02: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 20:52:49 +02:00
static void blend_subrect ( AVPicture * dst , const AVSubtitleRect * rect , int imgw , int imgh )
2005-08-14 03:29:34 +02: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 18:15:59 +02:00
int dstx , dsty , dstw , dsth ;
2009-01-03 18:50:00 +01: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 18:15:59 +02: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 ] ;
2009-02-09 01:27:04 +01:00
width2 = ( ( dstw + 1 ) > > 1 ) + ( dstx & ~ dstw & 1 ) ;
2007-08-09 18:15:59 +02:00
skip2 = dstx > > 1 ;
2005-08-14 03:29:34 +02:00
wrap = dst - > linesize [ 0 ] ;
2009-01-03 20:17:18 +01: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 19:14:38 +01:00
2007-08-09 18:15:59 +02:00
if ( dsty & 1 ) {
lum + = dstx ;
2005-08-14 03:29:34 +02:00
cb + = skip2 ;
cr + = skip2 ;
2005-12-17 19:14:38 +01:00
2007-08-09 18:15:59 +02:00
if ( dstx & 1 ) {
2005-08-14 03:29:34 +02: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 18:15:59 +02:00
for ( w = dstw - ( dstx & 1 ) ; w > = 2 ; w - = 2 ) {
2005-08-14 03:29:34 +02: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-06 00:07:24 +01:00
p + + ;
lum + + ;
2005-08-14 03:29:34 +02:00
}
2009-02-06 00:10:05 +01:00
p + = wrap3 - dstw * BPP ;
lum + = wrap - dstw - dstx ;
2005-08-14 03:29:34 +02:00
cb + = dst - > linesize [ 1 ] - width2 - skip2 ;
cr + = dst - > linesize [ 2 ] - width2 - skip2 ;
}
2007-08-09 18:15:59 +02:00
for ( h = dsth - ( dsty & 1 ) ; h > = 2 ; h - = 2 ) {
lum + = dstx ;
2005-08-14 03:29:34 +02:00
cb + = skip2 ;
cr + = skip2 ;
2005-12-17 19:14:38 +01:00
2007-08-09 18:15:59 +02:00
if ( dstx & 1 ) {
2005-08-14 03:29:34 +02: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 18:15:59 +02:00
for ( w = dstw - ( dstx & 1 ) ; w > = 2 ; w - = 2 ) {
2005-08-14 03:29:34 +02: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-03 00:24:28 +01:00
YUVA_IN ( y , u , v , a , p + BPP , pal ) ;
2005-08-14 03:29:34 +02: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-03 00:24:28 +01:00
YUVA_IN ( y , u , v , a , p + BPP , pal ) ;
2005-08-14 03:29:34 +02: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 18:15:59 +02:00
p + = wrap3 + ( wrap3 - dstw * BPP ) ;
lum + = wrap + ( wrap - dstw - dstx ) ;
2005-08-14 03:29:34 +02:00
cb + = dst - > linesize [ 1 ] - width2 - skip2 ;
cr + = dst - > linesize [ 2 ] - width2 - skip2 ;
}
/* handle odd height */
if ( h ) {
2007-08-09 18:15:59 +02:00
lum + = dstx ;
2005-08-14 03:29:34 +02:00
cb + = skip2 ;
cr + = skip2 ;
2005-12-17 19:14:38 +01:00
2007-08-09 18:15:59 +02:00
if ( dstx & 1 ) {
2005-08-14 03:29:34 +02: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 18:15:59 +02:00
for ( w = dstw - ( dstx & 1 ) ; w > = 2 ; w - = 2 ) {
2005-08-14 03:29:34 +02: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 )
{
2010-07-11 09:35:00 +02:00
avsubtitle_free ( & sp - > sub ) ;
2005-08-14 03:29:34 +02:00
}
2003-06-07 20:34:02 +02:00
static void video_image_display ( VideoState * is )
{
VideoPicture * vp ;
2005-08-14 03:29:34 +02:00
SubPicture * sp ;
AVPicture pict ;
2003-06-07 20:34:02 +02:00
float aspect_ratio ;
int width , height , x , y ;
SDL_Rect rect ;
2005-08-14 03:29:34 +02:00
int i ;
2003-06-07 20:34:02 +02:00
vp = & is - > pictq [ is - > pictq_rindex ] ;
if ( vp - > bmp ) {
2010-03-05 03:20:10 +01:00
# if CONFIG_AVFILTER
2010-08-11 13:06:04 +02:00
if ( vp - > picref - > video - > pixel_aspect . num = = 0 )
2010-03-05 03:20:10 +01:00
aspect_ratio = 0 ;
else
2010-08-11 13:06:04 +02:00
aspect_ratio = av_q2d ( vp - > picref - > video - > pixel_aspect ) ;
2010-03-05 03:20:10 +01:00
# else
2003-06-07 20:34:02 +02:00
/* XXX: use variable in the frame */
2008-08-24 01:13:58 +02: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 19:59:45 +01:00
else
2008-08-24 01:13:58 +02:00
aspect_ratio = 0 ;
2010-03-05 03:20:10 +01:00
# endif
2003-06-07 20:34:02 +02:00
if ( aspect_ratio < = 0.0 )
2008-08-24 01:13:58 +02:00
aspect_ratio = 1.0 ;
2010-03-05 03:20:10 +01:00
aspect_ratio * = ( float ) vp - > width / ( float ) vp - > height ;
2003-06-07 20:34:02 +02:00
2005-08-14 03:29:34 +02: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 18:54:48 +01:00
blend_subrect ( & pict , sp - > sub . rects [ i ] ,
2007-08-09 20:52:49 +02:00
vp - > bmp - > w , vp - > bmp - > h ) ;
2005-08-14 03:29:34 +02:00
SDL_UnlockYUVOverlay ( vp - > bmp ) ;
}
}
}
2003-06-07 20:34:02 +02:00
/* XXX: we suppose the screen has a 1.0 pixel ratio */
height = is - > height ;
2008-09-03 13:16:29 +02:00
width = ( ( int ) rint ( height * aspect_ratio ) ) & ~ 1 ;
2003-06-07 20:34:02 +02:00
if ( width > is - > width ) {
width = is - > width ;
2008-09-03 13:16:29 +02:00
height = ( ( int ) rint ( width / aspect_ratio ) ) & ~ 1 ;
2003-06-07 20:34:02 +02:00
}
x = ( is - > width - width ) / 2 ;
y = ( is - > height - height ) / 2 ;
2011-04-26 14:29:14 +02:00
is - > no_background = 0 ;
2003-06-07 20:34:02 +02:00
rect . x = is - > xleft + x ;
2007-03-02 11:40:24 +01:00
rect . y = is - > ytop + y ;
2003-06-07 20:34:02 +02:00
rect . w = width ;
rect . h = height ;
SDL_DisplayYUVOverlay ( vp - > bmp , & rect ) ;
}
}
2011-04-12 12:06:49 +02: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 )
{
return is - > audio_buf_size - is - > audio_buf_index ;
}
2003-06-07 20:34:02 +02:00
static inline int compute_mod ( int a , int b )
{
a = a % b ;
2005-12-17 19:14:38 +01:00
if ( a > = 0 )
2003-06-07 20:34:02 +02: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 16:16:38 +01: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 19:14:38 +01:00
2003-06-07 20:34:02 +02:00
/* compute display index : center on currently output samples */
2005-07-18 00:24:36 +02:00
channels = s - > audio_st - > codec - > channels ;
2003-06-07 20:34:02 +02:00
nb_display_channels = channels ;
2003-06-09 22:48:06 +02:00
if ( ! s - > paused ) {
2010-02-05 16:16:38 +01:00
int data_used = s - > show_audio = = 1 ? s - > width : ( 2 * nb_freq ) ;
2003-06-09 22:48:06 +02:00
n = 2 * channels ;
delay = audio_write_get_buf_size ( s ) ;
delay / = n ;
2005-12-17 19:14:38 +01:00
2003-06-09 22:48:06 +02: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 23:43:23 +01:00
delay - = ( time_diff * s - > audio_st - > codec - > sample_rate ) / 1000000 ;
2003-06-09 22:48:06 +02:00
}
2005-12-17 19:14:38 +01:00
2010-03-10 23:43:23 +01:00
delay + = 2 * data_used ;
2010-02-05 16:16:38 +01:00
if ( delay < data_used )
delay = data_used ;
2007-01-29 23:06:55 +01:00
i_start = x = compute_mod ( s - > sample_array_index - delay * channels , SAMPLE_ARRAY_SIZE ) ;
2010-02-05 03:06:38 +01:00
if ( s - > show_audio = = 1 ) {
2010-02-06 14:15:15 +01: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 23:06:55 +01:00
}
}
2003-06-09 22:48:06 +02:00
s - > last_i_start = i_start ;
} else {
i_start = s - > last_i_start ;
2003-06-07 20:34:02 +02:00
}
bgcolor = SDL_MapRGB ( screen - > format , 0x00 , 0x00 , 0x00 ) ;
2010-02-05 03:06:38 +01:00
if ( s - > show_audio = = 1 ) {
2010-02-06 14:15:15 +01:00
fill_rectangle ( screen ,
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 ;
2003-06-07 20:34:02 +02:00
}
}
2010-02-06 14:15:15 +01:00
fgcolor = SDL_MapRGB ( screen - > format , 0x00 , 0x00 , 0xff ) ;
2003-06-07 20:34:02 +02:00
2010-02-06 14:15:15 +01:00
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 03:06:38 +01:00
} else {
nb_display_channels = FFMIN ( nb_display_channels , 2 ) ;
if ( rdft_bits ! = s - > rdft_bits ) {
2010-03-07 22:56:48 +01:00
av_rdft_end ( s - > rdft ) ;
2010-06-30 00:41:20 +02:00
av_free ( s - > rdft_data ) ;
2010-03-07 22:56:48 +01:00
s - > rdft = av_rdft_init ( rdft_bits , DFT_R2C ) ;
2010-02-05 03:06:38 +01:00
s - > rdft_bits = rdft_bits ;
2010-06-30 00:41:20 +02:00
s - > rdft_data = av_malloc ( 4 * nb_freq * sizeof ( * s - > rdft_data ) ) ;
2010-02-05 03:06:38 +01:00
}
{
2010-06-30 00:41:20 +02:00
FFTSample * data [ 2 ] ;
2010-02-05 03:06:38 +01:00
for ( ch = 0 ; ch < nb_display_channels ; ch + + ) {
2010-06-30 00:41:20 +02:00
data [ ch ] = s - > rdft_data + 2 * nb_freq * ch ;
2010-02-05 03:06:38 +01:00
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 22:56:48 +01:00
av_rdft_calc ( s - > rdft , data [ ch ] ) ;
2010-02-05 03:06:38 +01:00
}
//least efficient way to do this, we should of course directly access it but its more than fast enough
2010-02-05 12:25:58 +01:00
for ( y = 0 ; y < s - > height ; y + + ) {
2010-02-05 03:06:38 +01: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 ] ) ) ;
2010-07-07 20:14:58 +02:00
int b = ( nb_display_channels = = 2 ) ? sqrt ( w * sqrt ( data [ 1 ] [ 2 * y + 0 ] * data [ 1 ] [ 2 * y + 0 ]
+ data [ 1 ] [ 2 * y + 1 ] * data [ 1 ] [ 2 * y + 1 ] ) ) : a ;
2010-02-05 03:06:38 +01:00
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 20:34:02 +02:00
}
2006-12-31 19:01:13 +01:00
static int video_open ( VideoState * is ) {
int flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL ;
int w , h ;
2006-12-31 21:04:08 +01:00
if ( is_full_screen ) flags | = SDL_FULLSCREEN ;
else flags | = SDL_RESIZABLE ;
2006-12-31 19:01:13 +01:00
if ( is_full_screen & & fs_screen_width ) {
w = fs_screen_width ;
h = fs_screen_height ;
2006-12-31 21:04:08 +01:00
} else if ( ! is_full_screen & & screen_width ) {
w = screen_width ;
h = screen_height ;
2010-03-05 03:20:10 +01: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 21:04:08 +01: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 03:20:10 +01:00
# endif
2006-12-31 19:01:13 +01:00
} else {
2006-12-31 21:04:08 +01:00
w = 640 ;
h = 480 ;
2006-12-31 19:01:13 +01:00
}
2010-03-10 16:39:31 +01:00
if ( screen & & is - > width = = screen - > w & & screen - > w = = w
& & is - > height = = screen - > h & & screen - > h = = h )
return 0 ;
2007-08-27 11:17:03 +02:00
# ifndef __APPLE__
2006-12-31 19:01:13 +01: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-18 00:39:18 +01:00
if ( ! window_title )
window_title = input_filename ;
SDL_WM_SetCaption ( window_title , window_title ) ;
2006-12-31 19:01:13 +01:00
is - > width = screen - > w ;
is - > height = screen - > h ;
return 0 ;
}
2006-12-31 18:59:10 +01:00
2003-06-07 20:34:02 +02:00
/* display the current picture, if any */
static void video_display ( VideoState * is )
{
2006-12-31 18:59:10 +01:00
if ( ! screen )
video_open ( cur_stream ) ;
2005-12-17 19:14:38 +01:00
if ( is - > audio_st & & is - > show_audio )
2003-06-07 20:34:02 +02:00
video_audio_display ( is ) ;
else if ( is - > video_st )
video_image_display ( is ) ;
}
2010-03-11 03:35:04 +01:00
static int refresh_thread ( void * opaque )
2003-06-07 20:34:02 +02:00
{
2010-03-11 03:35:04 +01:00
VideoState * is = opaque ;
while ( ! is - > abort_request ) {
2011-01-15 02:28:00 +01:00
SDL_Event event ;
event . type = FF_REFRESH_EVENT ;
event . user . data1 = opaque ;
2010-03-11 03:35:04 +01:00
if ( ! is - > refresh ) {
is - > refresh = 1 ;
2011-01-15 02:28:00 +01:00
SDL_PushEvent ( & event ) ;
2010-03-11 03:35:04 +01:00
}
2010-03-11 12:25:51 +01: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 03:35:04 +01:00
}
return 0 ;
2003-06-07 20:34:02 +02:00
}
2003-08-06 15:07:23 +02: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 19:14:38 +01:00
bytes_per_sec = is - > audio_st - > codec - > sample_rate *
2005-07-18 00:24:36 +02:00
2 * is - > audio_st - > codec - > channels ;
2003-08-06 15:07:23 +02: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 01:22:03 +01:00
if ( is - > paused ) {
2010-02-01 10:26:30 +01:00
return is - > video_current_pts ;
2003-11-10 19:59:45 +01:00
} else {
2010-02-01 11:55:51 +01:00
return is - > video_current_pts_drift + av_gettime ( ) / 1000000.0 ;
2003-11-10 19:59:45 +01:00
}
2003-08-06 15:07:23 +02: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 19:59:45 +01: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 15:07:23 +02:00
val = get_external_clock ( is ) ;
2003-11-10 19:59:45 +01:00
}
2003-08-06 15:07:23 +02:00
return val ;
}
2003-11-10 19:59:45 +01:00
/* seek in the stream */
2010-01-31 00:19:59 +01:00
static void stream_seek ( VideoState * is , int64_t pos , int64_t rel , int seek_by_bytes )
2003-11-10 19:59:45 +01:00
{
2005-06-24 10:32:55 +02:00
if ( ! is - > seek_req ) {
is - > seek_pos = pos ;
2009-03-14 17:24:30 +01:00
is - > seek_rel = rel ;
2010-02-02 17:01:22 +01:00
is - > seek_flags & = ~ AVSEEK_FLAG_BYTE ;
2006-11-07 23:35:41 +01:00
if ( seek_by_bytes )
is - > seek_flags | = AVSEEK_FLAG_BYTE ;
2005-06-24 10:32:55 +02:00
is - > seek_req = 1 ;
}
2003-11-10 19:59:45 +01:00
}
/* pause or resume the video */
static void stream_pause ( VideoState * is )
{
2010-02-01 11:55:51 +01:00
if ( is - > paused ) {
is - > frame_timer + = av_gettime ( ) / 1000000.0 + is - > video_current_pts_drift - is - > video_current_pts ;
2010-02-01 11:32:17 +01:00
if ( is - > read_pause_return ! = AVERROR ( ENOSYS ) ) {
2010-02-01 11:55:51 +01:00
is - > video_current_pts = is - > video_current_pts_drift + av_gettime ( ) / 1000000.0 ;
2010-02-01 11:32:17 +01:00
}
2010-02-01 11:55:51 +01:00
is - > video_current_pts_drift = is - > video_current_pts - av_gettime ( ) / 1000000.0 ;
2003-11-10 19:59:45 +01:00
}
2010-02-01 11:55:51 +01:00
is - > paused = ! is - > paused ;
2003-11-10 19:59:45 +01:00
}
2010-03-11 03:35:04 +01:00
static double compute_target_time ( double frame_current_pts , VideoState * is )
2009-02-18 16:17:39 +01:00
{
2010-03-11 03:35:04 +01:00
double delay , sync_threshold , diff ;
2009-02-18 16:17:39 +01: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 16:23:05 +01:00
} else {
2009-02-18 16:23:30 +01:00
is - > frame_last_delay = delay ;
2009-02-18 16:23:05 +01:00
}
2009-02-18 16:17:39 +01: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-03 00:22:35 +01:00
diff = get_video_clock ( is ) - get_master_clock ( is ) ;
2009-02-18 16:17:39 +01: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 16:25:57 +01: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 03:35:04 +01:00
return is - > frame_timer ;
2009-02-18 16:17:39 +01:00
}
2003-06-07 20:34:02 +02:00
/* called to display each frame */
static void video_refresh_timer ( void * opaque )
{
VideoState * is = opaque ;
VideoPicture * vp ;
2003-08-06 15:07:23 +02:00
2005-08-14 03:29:34 +02:00
SubPicture * sp , * sp2 ;
2003-06-07 20:34:02 +02:00
if ( is - > video_st ) {
2010-03-11 03:35:04 +01:00
retry :
2003-06-07 20:34:02 +02:00
if ( is - > pictq_size = = 0 ) {
2010-03-11 03:35:04 +01:00
//nothing to do, no picture to display in the que
2003-06-07 20:34:02 +02:00
} else {
2010-03-11 03:35:04 +01:00
double time = av_gettime ( ) / 1000000.0 ;
double next_target ;
2003-08-06 15:07:23 +02:00
/* dequeue the picture */
2003-06-07 20:34:02 +02:00
vp = & is - > pictq [ is - > pictq_rindex ] ;
2003-08-06 15:07:23 +02:00
2010-03-11 03:35:04 +01:00
if ( time < vp - > target_clock )
return ;
2003-08-06 15:07:23 +02:00
/* update current video pts */
is - > video_current_pts = vp - > pts ;
2010-03-11 03:35:04 +01:00
is - > video_current_pts_drift = is - > video_current_pts - time ;
2010-02-02 22:31:20 +01:00
is - > video_current_pos = vp - > pos ;
2010-03-11 03:35:04 +01: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 15:07:23 +02:00
2005-08-14 03:29:34 +02:00
if ( is - > subtitle_st ) {
if ( is - > subtitle_stream_changed ) {
SDL_LockMutex ( is - > subpq_mutex ) ;
2005-12-17 19:14:38 +01:00
2005-08-14 03:29:34 +02:00
while ( is - > subpq_size ) {
free_subpicture ( & is - > subpq [ is - > subpq_rindex ] ) ;
2005-12-17 19:14:38 +01:00
2005-08-14 03:29:34 +02:00
/* update queue size and signal for next picture */
if ( + + is - > subpq_rindex = = SUBPICTURE_QUEUE_SIZE )
is - > subpq_rindex = 0 ;
2005-12-17 19:14:38 +01:00
2005-08-14 03:29:34 +02: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 20:34:02 +02:00
/* display picture */
2011-01-15 18:21:09 +01:00
if ( ! display_disable )
2011-01-15 18:21:12 +01:00
video_display ( is ) ;
2005-12-17 19:14:38 +01:00
2003-06-07 20:34:02 +02:00
/* update queue size and signal for next picture */
if ( + + is - > pictq_rindex = = VIDEO_PICTURE_QUEUE_SIZE )
is - > pictq_rindex = 0 ;
2005-12-17 19:14:38 +01:00
2003-06-07 20:34:02 +02: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 19:14:38 +01:00
2003-06-07 20:34:02 +02:00
/* display picture */
2011-01-15 18:21:09 +01:00
if ( ! display_disable )
2011-01-15 18:21:12 +01:00
video_display ( is ) ;
2003-06-07 20:34:02 +02:00
}
if ( show_status ) {
static int64_t last_time ;
int64_t cur_time ;
2005-08-14 03:29:34 +02:00
int aqsize , vqsize , sqsize ;
2003-08-06 15:07:23 +02:00
double av_diff ;
2005-12-17 19:14:38 +01:00
2003-06-07 20:34:02 +02:00
cur_time = av_gettime ( ) ;
2009-07-30 23:54:50 +02:00
if ( ! last_time | | ( cur_time - last_time ) > = 30000 ) {
2003-06-07 20:34:02 +02:00
aqsize = 0 ;
vqsize = 0 ;
2005-08-14 03:29:34 +02:00
sqsize = 0 ;
2003-06-07 20:34:02 +02:00
if ( is - > audio_st )
aqsize = is - > audioq . size ;
if ( is - > video_st )
vqsize = is - > videoq . size ;
2005-08-14 03:29:34 +02:00
if ( is - > subtitle_st )
sqsize = is - > subtitleq . size ;
2003-08-06 15:07:23 +02:00
av_diff = 0 ;
if ( is - > audio_st & & is - > video_st )
av_diff = get_audio_clock ( is ) - get_video_clock ( is ) ;
2010-03-12 16:12:05 +01:00
printf ( " %7.2f A-V:%7.3f s:%3.1f aq=%5dKB vq=%5dKB sq=%5dB f=% " PRId64 " /% " PRId64 " \r " ,
2010-09-28 04:05:12 +02:00
get_master_clock ( is ) , av_diff , FFMAX ( is - > skip_frames - 1 , 0 ) , aqsize / 1024 , vqsize / 1024 , sqsize , is - > pts_ctx . num_faulty_dts , is - > pts_ctx . num_faulty_pts ) ;
2003-06-07 20:34:02 +02:00
fflush ( stdout ) ;
last_time = cur_time ;
}
}
}
2010-08-17 09:46:09 +02:00
static void stream_close ( VideoState * is )
{
VideoPicture * vp ;
int i ;
/* XXX: use a special url_shutdown call to abort parse cleanly */
is - > abort_request = 1 ;
SDL_WaitThread ( is - > parse_tid , NULL ) ;
SDL_WaitThread ( is - > refresh_tid , NULL ) ;
/* free all pictures */
for ( i = 0 ; i < VIDEO_PICTURE_QUEUE_SIZE ; i + + ) {
vp = & is - > pictq [ i ] ;
# if CONFIG_AVFILTER
if ( vp - > picref ) {
avfilter_unref_buffer ( vp - > picref ) ;
vp - > picref = NULL ;
}
# endif
if ( vp - > bmp ) {
SDL_FreeYUVOverlay ( vp - > bmp ) ;
vp - > bmp = NULL ;
}
}
SDL_DestroyMutex ( is - > pictq_mutex ) ;
SDL_DestroyCond ( is - > pictq_cond ) ;
SDL_DestroyMutex ( is - > subpq_mutex ) ;
SDL_DestroyCond ( is - > subpq_cond ) ;
# if !CONFIG_AVFILTER
if ( is - > img_convert_ctx )
sws_freeContext ( is - > img_convert_ctx ) ;
# endif
av_free ( is ) ;
}
static void do_exit ( void )
{
if ( cur_stream ) {
stream_close ( cur_stream ) ;
cur_stream = NULL ;
}
2010-10-02 10:44:33 +02:00
uninit_opts ( ) ;
2010-08-17 09:46:09 +02:00
# if CONFIG_AVFILTER
avfilter_uninit ( ) ;
# endif
if ( show_status )
printf ( " \n " ) ;
SDL_Quit ( ) ;
2010-09-24 17:39:10 +02:00
av_log ( NULL , AV_LOG_QUIET , " " ) ;
2010-08-17 09:46:09 +02:00
exit ( 0 ) ;
}
2003-06-07 20:34:02 +02:00
/* 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 03:20:10 +01:00
# if CONFIG_AVFILTER
if ( vp - > picref )
2010-08-07 03:15:27 +02:00
avfilter_unref_buffer ( vp - > picref ) ;
2010-03-05 03:20:10 +01:00
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 19:14:38 +01:00
SDL_YV12_OVERLAY ,
2003-09-16 21:37:27 +02:00
screen ) ;
2010-08-17 09:47:44 +02:00
if ( ! vp - > bmp | | vp - > bmp - > pitches [ 0 ] < vp - > width ) {
/* SDL allocates a buffer smaller than requested if the video
* overlay hardware is unable to support the requested size . */
fprintf ( stderr , " Error: the video system does not support an image \n "
2010-08-17 10:13:14 +02:00
" size of %dx%d pixels. Try using -lowres or -vf \" scale=w:h \" \n "
2010-08-17 09:47:44 +02:00
" to reduce the image size. \n " , vp - > width , vp - > height ) ;
do_exit ( ) ;
}
2003-06-07 20:34:02 +02:00
SDL_LockMutex ( is - > pictq_mutex ) ;
vp - > allocated = 1 ;
SDL_CondSignal ( is - > pictq_cond ) ;
SDL_UnlockMutex ( is - > pictq_mutex ) ;
}
2005-09-20 20:47:08 +02:00
/**
*
* @ param pts the dts of the pkt / pts of the frame and guessed if not known
*/
2010-02-02 17:51:02 +01:00
static int queue_picture ( VideoState * is , AVFrame * src_frame , double pts , int64_t pos )
2003-06-07 20:34:02 +02:00
{
VideoPicture * vp ;
int dst_pix_fmt ;
2010-03-05 03:20:10 +01:00
# if CONFIG_AVFILTER
AVPicture pict_src ;
# endif
2003-06-07 20:34:02 +02:00
/* wait until we have space to put a new picture */
SDL_LockMutex ( is - > pictq_mutex ) ;
2010-03-11 03:35:04 +01: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 20:34:02 +02: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 19:14:38 +01:00
2003-06-07 20:34:02 +02:00
if ( is - > videoq . abort_request )
return - 1 ;
vp = & is - > pictq [ is - > pictq_windex ] ;
/* alloc or resize hardware picture buffer */
2005-12-17 19:14:38 +01:00
if ( ! vp - > bmp | |
2010-03-05 03:20:10 +01: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-18 00:24:36 +02:00
vp - > width ! = is - > video_st - > codec - > width | |
vp - > height ! = is - > video_st - > codec - > height ) {
2010-03-05 03:20:10 +01:00
# endif
2003-06-07 20:34:02 +02: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 19:14:38 +01:00
2003-06-07 20:34:02 +02: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 15:07:23 +02:00
/* if the frame is not skipped, then display it */
2003-06-07 20:34:02 +02:00
if ( vp - > bmp ) {
2009-03-23 17:43:06 +01:00
AVPicture pict ;
2010-03-05 03:20:10 +01:00
# if CONFIG_AVFILTER
if ( vp - > picref )
2010-08-07 03:15:27 +02:00
avfilter_unref_buffer ( vp - > picref ) ;
2010-03-05 03:20:10 +01:00
vp - > picref = src_frame - > opaque ;
# endif
2009-03-23 17:43:06 +01:00
2003-06-07 20:34:02 +02:00
/* get a pointer on the bitmap */
SDL_LockYUVOverlay ( vp - > bmp ) ;
dst_pix_fmt = PIX_FMT_YUV420P ;
2009-03-23 17:43:06 +01:00
memset ( & pict , 0 , sizeof ( AVPicture ) ) ;
2003-06-07 20:34:02 +02: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 03:20:10 +01: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 21:39:18 +02:00
sws_flags = av_get_int ( sws_opts , " sws_flags " , NULL ) ;
2009-05-16 12:29:55 +02:00
is - > img_convert_ctx = sws_getCachedContext ( is - > img_convert_ctx ,
2010-03-05 03:20:10 +01:00
vp - > width , vp - > height , vp - > pix_fmt , vp - > width , vp - > height ,
2007-08-03 20:46:59 +02:00
dst_pix_fmt , sws_flags , NULL , NULL , NULL ) ;
2009-05-16 12:29:55 +02:00
if ( is - > img_convert_ctx = = NULL ) {
2007-08-03 20:45:44 +02:00
fprintf ( stderr , " Cannot initialize the conversion context \n " ) ;
exit ( 1 ) ;
}
2009-05-16 12:29:55 +02:00
sws_scale ( is - > img_convert_ctx , src_frame - > data , src_frame - > linesize ,
2010-03-05 03:20:10 +01:00
0 , vp - > height , pict . data , pict . linesize ) ;
# endif
2003-06-07 20:34:02 +02:00
/* update the bitmap content */
SDL_UnlockYUVOverlay ( vp - > bmp ) ;
2003-08-06 15:07:23 +02:00
vp - > pts = pts ;
2010-02-02 17:51:02 +01:00
vp - > pos = pos ;
2003-06-07 20:34:02 +02: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 03:35:04 +01:00
vp - > target_clock = compute_target_time ( vp - > pts , is ) ;
2003-06-07 20:34:02 +02:00
is - > pictq_size + + ;
SDL_UnlockMutex ( is - > pictq_mutex ) ;
}
2003-08-06 15:07:23 +02:00
return 0 ;
}
2005-12-17 19:14:38 +01:00
/**
* compute the exact PTS for the picture if it is omitted in the stream
2005-09-20 20:47:08 +02:00
* @ param pts1 the dts of the pkt / pts of the frame
*/
2010-02-02 17:51:02 +01:00
static int output_picture2 ( VideoState * is , AVFrame * src_frame , double pts1 , int64_t pos )
2003-08-06 15:07:23 +02:00
{
double frame_delay , pts ;
2005-12-17 19:14:38 +01:00
2003-08-06 15:07:23 +02:00
pts = pts1 ;
2003-06-07 20:34:02 +02:00
if ( pts ! = 0 ) {
2003-08-06 15:07:23 +02:00
/* update video clock with pts, if present */
2003-06-07 20:34:02 +02:00
is - > video_clock = pts ;
} else {
2003-11-10 19:59:45 +01:00
pts = is - > video_clock ;
}
/* update video clock for next frame */
2005-07-18 00:24:36 +02:00
frame_delay = av_q2d ( is - > video_st - > codec - > time_base ) ;
2003-11-10 19:59:45 +01:00
/* for MPEG2, the frame can be repeated, so we update the
clock accordingly */
2005-09-20 20:47:08 +02:00
frame_delay + = src_frame - > repeat_pict * ( frame_delay * 0.5 ) ;
2003-11-10 19:59:45 +01:00
is - > video_clock + = frame_delay ;
2003-08-06 15:07:23 +02:00
2010-02-02 17:51:02 +01:00
return queue_picture ( is , src_frame , pts , pos ) ;
2003-06-07 20:34:02 +02:00
}
2010-03-05 04:54:39 +01:00
static int get_video_frame ( VideoState * is , AVFrame * frame , int64_t * pts , AVPacket * pkt )
2003-06-07 20:34:02 +02:00
{
2010-02-01 13:19:28 +01:00
int len1 , got_picture , i ;
2003-06-07 20:34:02 +02:00
2011-01-16 18:26:00 +01:00
if ( packet_queue_get ( & is - > videoq , pkt , 1 ) < 0 )
return - 1 ;
2010-02-01 13:19:28 +01:00
2011-01-16 18:26:00 +01:00
if ( pkt - > data = = flush_pkt . data ) {
avcodec_flush_buffers ( is - > video_st - > codec ) ;
2010-02-01 13:19:28 +01:00
2011-01-16 18:26:00 +01: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 + + ) {
is - > pictq [ i ] . target_clock = 0 ;
}
while ( is - > pictq_size & & ! is - > videoq . abort_request ) {
SDL_CondWait ( is - > pictq_cond , is - > pictq_mutex ) ;
2006-11-16 12:58:27 +01:00
}
2011-01-16 18:26:00 +01:00
is - > video_current_pos = - 1 ;
SDL_UnlockMutex ( is - > pictq_mutex ) ;
2006-11-16 12:58:27 +01:00
2011-01-16 18:26:00 +01:00
init_pts_correction ( & is - > pts_ctx ) ;
is - > frame_last_pts = AV_NOPTS_VALUE ;
is - > frame_last_delay = 0 ;
is - > frame_timer = ( double ) av_gettime ( ) / 1000000.0 ;
is - > skip_frames = 1 ;
is - > skip_frames_index = 0 ;
return 0 ;
}
2010-09-28 04:05:12 +02:00
2011-01-16 18:26:00 +01:00
len1 = avcodec_decode_video2 ( is - > video_st - > codec ,
frame , & got_picture ,
pkt ) ;
if ( got_picture ) {
if ( decoder_reorder_pts = = - 1 ) {
2011-02-05 06:28:24 +01:00
* pts = guess_correct_pts ( & is - > pts_ctx , frame - > pkt_pts , frame - > pkt_dts ) ;
2011-01-16 18:26:00 +01:00
} else if ( decoder_reorder_pts ) {
2011-01-16 18:39:34 +01:00
* pts = frame - > pkt_pts ;
2011-01-16 18:26:00 +01:00
} else {
2011-02-05 06:28:24 +01:00
* pts = frame - > pkt_dts ;
2011-01-16 18:26:00 +01:00
}
if ( * pts = = AV_NOPTS_VALUE ) {
* pts = 0 ;
2010-02-01 14:03:46 +01:00
}
2010-01-31 19:54:32 +01:00
2010-03-11 03:35:04 +01:00
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 03:20:10 +01:00
return 0 ;
}
# if CONFIG_AVFILTER
typedef struct {
VideoState * is ;
AVFrame * frame ;
2010-04-01 08:41:21 +02:00
int use_dr1 ;
2010-03-05 03:20:10 +01:00
} FilterPriv ;
2010-04-01 08:41:21 +02:00
static int input_get_buffer ( AVCodecContext * codec , AVFrame * pic )
{
AVFilterContext * ctx = codec - > opaque ;
2010-08-07 03:15:19 +02:00
AVFilterBufferRef * ref ;
2010-04-01 08:41:21 +02:00
int perms = AV_PERM_WRITE ;
2010-05-15 19:34:45 +02:00
int i , w , h , stride [ 4 ] ;
2010-04-01 08:41:21 +02:00
unsigned edge ;
2011-03-29 17:48:57 +02:00
int pixel_size ;
2010-04-01 08:41:21 +02:00
2010-12-27 16:10:21 +01:00
if ( codec - > codec - > capabilities & CODEC_CAP_NEG_LINESIZES )
perms | = AV_PERM_NEG_LINESIZES ;
2010-04-01 08:41:21 +02:00
if ( pic - > buffer_hints & FF_BUFFER_HINTS_VALID ) {
if ( pic - > buffer_hints & FF_BUFFER_HINTS_READABLE ) perms | = AV_PERM_READ ;
if ( pic - > buffer_hints & FF_BUFFER_HINTS_PRESERVE ) perms | = AV_PERM_PRESERVE ;
if ( pic - > buffer_hints & FF_BUFFER_HINTS_REUSABLE ) perms | = AV_PERM_REUSE2 ;
}
if ( pic - > reference ) perms | = AV_PERM_READ | AV_PERM_PRESERVE ;
w = codec - > width ;
h = codec - > height ;
avcodec_align_dimensions2 ( codec , & w , & h , stride ) ;
edge = codec - > flags & CODEC_FLAG_EMU_EDGE ? 0 : avcodec_get_edge_width ( ) ;
w + = edge < < 1 ;
h + = edge < < 1 ;
if ( ! ( ref = avfilter_get_video_buffer ( ctx - > outputs [ 0 ] , perms , w , h ) ) )
return - 1 ;
2011-03-29 17:48:57 +02:00
pixel_size = av_pix_fmt_descriptors [ ref - > format ] . comp [ 0 ] . step_minus1 + 1 ;
2010-08-11 13:06:04 +02:00
ref - > video - > w = codec - > width ;
ref - > video - > h = codec - > height ;
2010-07-03 23:20:32 +02:00
for ( i = 0 ; i < 4 ; i + + ) {
2010-08-07 02:02:26 +02:00
unsigned hshift = ( i = = 1 | | i = = 2 ) ? av_pix_fmt_descriptors [ ref - > format ] . log2_chroma_w : 0 ;
unsigned vshift = ( i = = 1 | | i = = 2 ) ? av_pix_fmt_descriptors [ ref - > format ] . log2_chroma_h : 0 ;
2010-04-01 08:41:21 +02:00
2010-05-23 21:13:17 +02:00
if ( ref - > data [ i ] ) {
2011-03-29 17:48:57 +02:00
ref - > data [ i ] + = ( ( edge * pixel_size ) > > hshift ) + ( ( edge * ref - > linesize [ i ] ) > > vshift ) ;
2010-05-23 21:13:17 +02:00
}
2010-04-01 08:41:21 +02:00
pic - > data [ i ] = ref - > data [ i ] ;
pic - > linesize [ i ] = ref - > linesize [ i ] ;
}
pic - > opaque = ref ;
pic - > age = INT_MAX ;
pic - > type = FF_BUFFER_TYPE_USER ;
2010-05-27 02:31:45 +02:00
pic - > reordered_opaque = codec - > reordered_opaque ;
2011-01-08 00:07:24 +01:00
if ( codec - > pkt ) pic - > pkt_pts = codec - > pkt - > pts ;
else pic - > pkt_pts = AV_NOPTS_VALUE ;
2010-04-01 08:41:21 +02:00
return 0 ;
}
static void input_release_buffer ( AVCodecContext * codec , AVFrame * pic )
{
memset ( pic - > data , 0 , sizeof ( pic - > data ) ) ;
2010-08-07 03:15:27 +02:00
avfilter_unref_buffer ( pic - > opaque ) ;
2010-04-01 08:41:21 +02:00
}
2010-05-24 16:19:44 +02:00
static int input_reget_buffer ( AVCodecContext * codec , AVFrame * pic )
{
2010-08-07 03:15:19 +02:00
AVFilterBufferRef * ref = pic - > opaque ;
2010-05-24 16:19:44 +02:00
if ( pic - > data [ 0 ] = = NULL ) {
pic - > buffer_hints | = FF_BUFFER_HINTS_READABLE ;
return codec - > get_buffer ( codec , pic ) ;
}
2010-08-11 13:06:04 +02:00
if ( ( codec - > width ! = ref - > video - > w ) | | ( codec - > height ! = ref - > video - > h ) | |
2010-08-07 02:02:26 +02:00
( codec - > pix_fmt ! = ref - > format ) ) {
2010-05-24 16:19:44 +02:00
av_log ( codec , AV_LOG_ERROR , " Picture properties changed. \n " ) ;
return - 1 ;
}
pic - > reordered_opaque = codec - > reordered_opaque ;
2011-01-08 00:07:24 +01:00
if ( codec - > pkt ) pic - > pkt_pts = codec - > pkt - > pts ;
else pic - > pkt_pts = AV_NOPTS_VALUE ;
2010-05-24 16:19:44 +02:00
return 0 ;
}
2010-03-05 03:20:10 +01:00
static int input_init ( AVFilterContext * ctx , const char * args , void * opaque )
{
FilterPriv * priv = ctx - > priv ;
2010-04-01 08:41:21 +02:00
AVCodecContext * codec ;
2010-03-05 03:20:10 +01:00
if ( ! opaque ) return - 1 ;
priv - > is = opaque ;
2010-04-01 08:41:21 +02:00
codec = priv - > is - > video_st - > codec ;
codec - > opaque = ctx ;
if ( codec - > codec - > capabilities & CODEC_CAP_DR1 ) {
priv - > use_dr1 = 1 ;
codec - > get_buffer = input_get_buffer ;
codec - > release_buffer = input_release_buffer ;
2010-05-24 16:19:44 +02:00
codec - > reget_buffer = input_reget_buffer ;
2011-02-08 03:15:44 +01:00
codec - > thread_safe_callbacks = 1 ;
2010-04-01 08:41:21 +02:00
}
2010-03-05 03:20:10 +01:00
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 ;
2010-08-07 03:15:19 +02:00
AVFilterBufferRef * picref ;
2010-03-05 04:54:39 +01:00
int64_t pts = 0 ;
2010-03-05 03:20:10 +01: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 ;
2010-04-01 08:41:21 +02:00
if ( priv - > use_dr1 ) {
2010-08-07 03:15:27 +02:00
picref = avfilter_ref_buffer ( priv - > frame - > opaque , ~ 0 ) ;
2010-04-01 08:41:21 +02:00
} else {
2010-04-01 08:41:27 +02:00
picref = avfilter_get_video_buffer ( link , AV_PERM_WRITE , link - > w , link - > h ) ;
2010-09-07 23:23:55 +02:00
av_image_copy ( picref - > data , picref - > linesize ,
2010-09-07 23:23:59 +02:00
priv - > frame - > data , priv - > frame - > linesize ,
picref - > format , link - > w , link - > h ) ;
2010-04-01 08:41:21 +02:00
}
2010-03-05 03:20:10 +01:00
av_free_packet ( & pkt ) ;
picref - > pts = pts ;
2010-03-13 12:27:07 +01:00
picref - > pos = pkt . pos ;
2010-08-11 13:06:04 +02:00
picref - > video - > pixel_aspect = priv - > is - > video_st - > codec - > sample_aspect_ratio ;
2010-04-01 08:41:25 +02:00
avfilter_start_frame ( link , picref ) ;
2010-03-05 03:20:10 +01:00
avfilter_draw_slice ( link , 0 , link - > h , 1 ) ;
avfilter_end_frame ( link ) ;
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 ;
2010-10-12 20:40:16 +02:00
link - > time_base = priv - > is - > video_st - > time_base ;
2010-03-05 03:20:10 +01:00
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 " ,
2010-03-31 01:30:55 +02:00
. type = AVMEDIA_TYPE_VIDEO ,
2010-03-05 03:20:10 +01:00
. request_frame = input_request_frame ,
. config_props = input_config_props , } ,
{ . name = NULL } } ,
} ;
2011-02-01 19:28:09 +01:00
static int configure_video_filters ( AVFilterGraph * graph , VideoState * is , const char * vfilters )
2010-03-05 03:20:10 +01:00
{
2010-05-08 23:39:57 +02:00
char sws_flags_str [ 128 ] ;
2011-02-01 19:28:09 +01:00
int ret ;
2010-10-12 20:40:26 +02:00
FFSinkContext ffsink_ctx = { . pix_fmt = PIX_FMT_YUV420P } ;
2010-03-05 03:20:10 +01:00
AVFilterContext * filt_src = NULL , * filt_out = NULL ;
2010-05-08 23:39:57 +02:00
snprintf ( sws_flags_str , sizeof ( sws_flags_str ) , " flags=%d " , sws_flags ) ;
graph - > scale_sws_opts = av_strdup ( sws_flags_str ) ;
2010-03-05 03:20:10 +01:00
2011-02-01 19:28:09 +01:00
if ( ( ret = avfilter_graph_create_filter ( & filt_src , & input_filter , " src " ,
NULL , is , graph ) ) < 0 )
2010-12-02 21:12:27 +01:00
goto the_end ;
2011-02-01 19:28:09 +01:00
if ( ( ret = avfilter_graph_create_filter ( & filt_out , & ffsink , " out " ,
NULL , & ffsink_ctx , graph ) ) < 0 )
2010-12-02 21:12:27 +01:00
goto the_end ;
2010-03-05 03:20:10 +01:00
if ( vfilters ) {
AVFilterInOut * outputs = av_malloc ( sizeof ( AVFilterInOut ) ) ;
AVFilterInOut * inputs = av_malloc ( sizeof ( AVFilterInOut ) ) ;
outputs - > name = av_strdup ( " in " ) ;
2010-11-07 19:40:18 +01:00
outputs - > filter_ctx = filt_src ;
2010-03-05 03:20:10 +01:00
outputs - > pad_idx = 0 ;
outputs - > next = NULL ;
inputs - > name = av_strdup ( " out " ) ;
2010-11-07 19:40:18 +01:00
inputs - > filter_ctx = filt_out ;
2010-03-05 03:20:10 +01:00
inputs - > pad_idx = 0 ;
inputs - > next = NULL ;
2011-02-01 19:28:09 +01:00
if ( ( ret = avfilter_graph_parse ( graph , vfilters , inputs , outputs , NULL ) ) < 0 )
2010-03-05 03:20:10 +01:00
goto the_end ;
av_freep ( & vfilters ) ;
} else {
2011-02-01 19:28:09 +01:00
if ( ( ret = avfilter_link ( filt_src , 0 , filt_out , 0 ) ) < 0 )
goto the_end ;
2010-03-05 03:20:10 +01:00
}
2011-02-01 19:28:09 +01:00
if ( ( ret = avfilter_graph_config ( graph , NULL ) ) < 0 )
2010-10-16 12:20:53 +02:00
goto the_end ;
2010-03-05 03:20:10 +01:00
is - > out_video_filter = filt_out ;
2011-02-01 19:28:09 +01:00
the_end :
return ret ;
}
# endif /* CONFIG_AVFILTER */
static int video_thread ( void * arg )
{
VideoState * is = arg ;
AVFrame * frame = avcodec_alloc_frame ( ) ;
int64_t pts_int ;
double pts ;
int ret ;
# if CONFIG_AVFILTER
AVFilterGraph * graph = avfilter_graph_alloc ( ) ;
AVFilterContext * filt_out = NULL ;
int64_t pos ;
if ( ( ret = configure_video_filters ( graph , is , vfilters ) ) < 0 )
goto the_end ;
filt_out = is - > out_video_filter ;
2010-03-05 03:20:10 +01:00
# endif
for ( ; ; ) {
# if !CONFIG_AVFILTER
AVPacket pkt ;
2010-10-12 20:40:16 +02:00
# else
2010-10-18 15:57:11 +02:00
AVFilterBufferRef * picref ;
2010-10-12 20:40:16 +02:00
AVRational tb ;
2010-03-05 03:20:10 +01:00
# endif
while ( is - > paused & & ! is - > videoq . abort_request )
SDL_Delay ( 10 ) ;
# if CONFIG_AVFILTER
2010-10-18 15:57:11 +02:00
ret = get_filtered_video_frame ( filt_out , frame , & picref , & tb ) ;
if ( picref ) {
pts_int = picref - > pts ;
pos = picref - > pos ;
frame - > opaque = picref ;
}
2010-10-12 20:40:16 +02:00
if ( av_cmp_q ( tb , is - > video_st - > time_base ) ) {
2011-01-22 15:35:00 +01:00
av_unused int64_t pts1 = pts_int ;
2010-10-12 20:40:16 +02:00
pts_int = av_rescale_q ( pts_int , tb , is - > video_st - > time_base ) ;
2011-01-22 15:35:00 +01:00
av_dlog ( NULL , " video_thread(): "
" tb:%d/%d pts:% " PRId64 " -> tb:%d/%d pts:% " PRId64 " \n " ,
tb . num , tb . den , pts1 ,
is - > video_st - > time_base . num , is - > video_st - > time_base . den , pts_int ) ;
2010-10-12 20:40:16 +02:00
}
2010-03-05 03:20:10 +01:00
# else
ret = get_video_frame ( is , frame , & pts_int , & pkt ) ;
# endif
if ( ret < 0 ) goto the_end ;
if ( ! ret )
continue ;
2010-03-05 04:54:39 +01:00
pts = pts_int * av_q2d ( is - > video_st - > time_base ) ;
2010-03-05 03:20:10 +01:00
# if CONFIG_AVFILTER
2010-03-13 12:27:07 +01:00
ret = output_picture2 ( is , frame , pts , pos ) ;
2010-03-05 03:20:10 +01:00
# else
2010-03-05 04:34:47 +01:00
ret = output_picture2 ( is , frame , pts , pkt . pos ) ;
2010-03-05 03:20:10 +01:00
av_free_packet ( & pkt ) ;
# endif
if ( ret < 0 )
goto the_end ;
2005-12-17 19:14:38 +01:00
if ( step )
2003-12-28 02:19:41 +01:00
if ( cur_stream )
stream_pause ( cur_stream ) ;
2003-06-07 20:34:02 +02:00
}
the_end :
2010-03-05 03:20:10 +01:00
# if CONFIG_AVFILTER
2011-02-01 20:02:17 +01:00
avfilter_graph_free ( & graph ) ;
2010-03-05 03:20:10 +01:00
# endif
2003-09-28 22:47:07 +02:00
av_free ( frame ) ;
2003-06-07 20:34:02 +02:00
return 0 ;
}
2005-08-14 03:29:34 +02: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 19:14:38 +01:00
2006-11-16 12:58:27 +01:00
if ( pkt - > data = = flush_pkt . data ) {
avcodec_flush_buffers ( is - > subtitle_st - > codec ) ;
continue ;
}
2005-08-14 03:29:34 +02: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 19:14:38 +01:00
2005-08-14 03:29:34 +02:00
if ( is - > subtitleq . abort_request )
goto the_end ;
2005-12-17 19:14:38 +01:00
2005-08-14 03:29:34 +02: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 14:07:06 +02:00
len1 = avcodec_decode_subtitle2 ( is - > subtitle_st - > codec ,
2005-12-17 19:14:38 +01:00
& sp - > sub , & got_subtitle ,
2009-04-10 14:07:06 +02:00
pkt ) ;
2005-08-14 03:29:34 +02:00
if ( got_subtitle & & sp - > sub . format = = 0 ) {
sp - > pts = pts ;
2005-12-17 19:14:38 +01:00
2005-08-14 03:29:34 +02:00
for ( i = 0 ; i < sp - > sub . num_rects ; i + + )
{
2009-01-03 18:54:48 +01:00
for ( j = 0 ; j < sp - > sub . rects [ i ] - > nb_colors ; j + + )
2005-08-14 03:29:34 +02:00
{
2009-01-03 20:17:18 +01:00
RGBA_IN ( r , g , b , a , ( uint32_t * ) sp - > sub . rects [ i ] - > pict . data [ 1 ] + j ) ;
2005-08-14 03:29:34 +02: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 20:17:18 +01:00
YUVA_OUT ( ( uint32_t * ) sp - > sub . rects [ i ] - > pict . data [ 1 ] + j , y , u , v , a ) ;
2005-08-14 03:29:34 +02: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 ) ;
}
the_end :
return 0 ;
}
2003-06-07 20:34:02 +02: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-18 00:24:36 +02:00
channels = is - > audio_st - > codec - > channels ;
2003-06-07 20:34:02 +02: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 19:14:38 +01:00
static int synchronize_audio ( VideoState * is , short * samples ,
2003-08-06 15:07:23 +02:00
int samples_size1 , double pts )
2003-06-07 20:34:02 +02:00
{
2003-08-06 15:07:23 +02:00
int n , samples_size ;
2003-06-07 20:34:02 +02:00
double ref_clock ;
2005-12-17 19:14:38 +01:00
2005-07-18 00:24:36 +02:00
n = 2 * is - > audio_st - > codec - > channels ;
2003-08-06 15:07:23 +02:00
samples_size = samples_size1 ;
2003-06-07 20:34:02 +02: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 15:07:23 +02:00
is - > av_sync_type = = AV_SYNC_EXTERNAL_CLOCK ) ) {
double diff , avg_diff ;
2003-06-07 20:34:02 +02:00
int wanted_size , min_size , max_size , nb_samples ;
2005-12-17 19:14:38 +01:00
2003-08-06 15:07:23 +02:00
ref_clock = get_master_clock ( is ) ;
diff = get_audio_clock ( is ) - ref_clock ;
2005-12-17 19:14:38 +01:00
2003-08-06 15:07:23 +02: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 ) ;
if ( fabs ( avg_diff ) > = is - > audio_diff_threshold ) {
2005-07-18 00:24:36 +02:00
wanted_size = samples_size + ( ( int ) ( diff * is - > audio_st - > codec - > sample_rate ) * n ) ;
2003-08-06 15:07:23 +02:00
nb_samples = samples_size / n ;
2005-12-17 19:14:38 +01:00
2003-08-06 15:07:23 +02: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 19:14:38 +01:00
2003-08-06 15:07:23 +02: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 19:14:38 +01:00
2003-08-06 15:07:23 +02: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 ;
}
}
2011-04-29 17:27:01 +02:00
av_dlog ( NULL , " diff=%f adiff=%f sample_diff=%d apts=%0.3f vpts=%0.3f %f \n " ,
diff , avg_diff , samples_size - samples_size1 ,
is - > audio_clock , is - > video_clock , is - > audio_diff_threshold ) ;
2003-06-07 20:34:02 +02:00
}
2003-08-06 15:07:23 +02: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 20:34:02 +02:00
}
}
return samples_size ;
}
/* decode one audio frame and returns its uncompressed size */
2008-08-02 07:01:30 +02:00
static int audio_decode_frame ( VideoState * is , double * pts_ptr )
2003-06-07 20:34:02 +02:00
{
2009-04-10 14:07:06 +02:00
AVPacket * pkt_temp = & is - > audio_pkt_temp ;
2003-06-07 20:34:02 +02:00
AVPacket * pkt = & is - > audio_pkt ;
2008-08-02 03:26:38 +02:00
AVCodecContext * dec = is - > audio_st - > codec ;
2003-11-10 19:59:45 +01:00
int n , len1 , data_size ;
2003-06-07 20:34:02 +02:00
double pts ;
for ( ; ; ) {
2003-11-10 19:59:45 +01:00
/* NOTE: the audio packet can contain several frames */
2009-04-10 14:07:06 +02:00
while ( pkt_temp - > size > 0 ) {
2008-08-02 07:01:30 +02:00
data_size = sizeof ( is - > audio_buf1 ) ;
2009-04-10 14:07:06 +02:00
len1 = avcodec_decode_audio3 ( dec ,
2008-08-02 07:01:30 +02:00
( int16_t * ) is - > audio_buf1 , & data_size ,
2009-04-10 14:07:06 +02:00
pkt_temp ) ;
2003-11-10 19:59:45 +01:00
if ( len1 < 0 ) {
/* if error, we skip the frame */
2009-04-10 14:07:06 +02:00
pkt_temp - > size = 0 ;
2003-06-07 20:34:02 +02:00
break ;
2003-11-10 19:59:45 +01:00
}
2005-12-17 19:14:38 +01:00
2009-04-10 14:07:06 +02:00
pkt_temp - > data + = len1 ;
pkt_temp - > size - = len1 ;
2003-11-10 19:59:45 +01:00
if ( data_size < = 0 )
continue ;
2008-08-02 07:01:30 +02:00
if ( dec - > sample_fmt ! = is - > audio_src_fmt ) {
if ( is - > reformat_ctx )
av_audio_convert_free ( is - > reformat_ctx ) ;
2010-11-12 12:04:40 +01:00
is - > reformat_ctx = av_audio_convert_alloc ( AV_SAMPLE_FMT_S16 , 1 ,
2008-08-02 07:01:30 +02:00
dec - > sample_fmt , 1 , NULL , 0 ) ;
if ( ! is - > reformat_ctx ) {
fprintf ( stderr , " Cannot convert %s sample format to %s sample format \n " ,
2010-11-03 21:19:34 +01:00
av_get_sample_fmt_name ( dec - > sample_fmt ) ,
2010-11-12 12:04:40 +01:00
av_get_sample_fmt_name ( AV_SAMPLE_FMT_S16 ) ) ;
2008-08-02 07:01:30 +02:00
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 } ;
2010-11-03 21:19:34 +01:00
int istride [ 6 ] = { av_get_bits_per_sample_fmt ( dec - > sample_fmt ) / 8 } ;
2008-08-02 07:01:30 +02:00
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 19:59:45 +01:00
/* if no pts, then compute it */
pts = is - > audio_clock ;
* pts_ptr = pts ;
2008-08-02 03:26:38 +02:00
n = 2 * dec - > channels ;
2005-12-17 19:14:38 +01:00
is - > audio_clock + = ( double ) data_size /
2008-08-02 03:26:38 +02:00
( double ) ( n * dec - > sample_rate ) ;
2003-08-06 15:07:23 +02:00
# if defined(DEBUG_SYNC)
2003-11-10 19:59:45 +01: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 20:34:02 +02:00
}
2003-11-10 19:59:45 +01:00
# endif
return data_size ;
2003-06-07 20:34:02 +02:00
}
2003-11-10 19:59:45 +01:00
/* free the current packet */
if ( pkt - > data )
2003-06-07 20:34:02 +02:00
av_free_packet ( pkt ) ;
2005-12-17 19:14:38 +01:00
2003-11-10 19:59:45 +01:00
if ( is - > paused | | is - > audioq . abort_request ) {
return - 1 ;
}
2005-12-17 19:14:38 +01:00
2003-06-07 20:34:02 +02:00
/* read next packet */
if ( packet_queue_get ( & is - > audioq , pkt , 1 ) < 0 )
return - 1 ;
2006-11-16 12:58:27 +01:00
if ( pkt - > data = = flush_pkt . data ) {
2008-08-02 03:26:38 +02:00
avcodec_flush_buffers ( dec ) ;
2006-11-16 12:58:27 +01:00
continue ;
}
2009-04-10 14:07:06 +02:00
pkt_temp - > data = pkt - > data ;
pkt_temp - > size = pkt - > size ;
2005-12-17 19:14:38 +01:00
2003-11-10 19:59:45 +01:00
/* if update the audio clock with the pts */
if ( pkt - > pts ! = AV_NOPTS_VALUE ) {
2005-04-30 23:43:59 +02:00
is - > audio_clock = av_q2d ( is - > audio_st - > time_base ) * pkt - > pts ;
2003-11-10 19:59:45 +01:00
}
2003-06-07 20:34:02 +02:00
}
}
/* prepare a new audio buffer */
2008-05-01 17:10:44 +02:00
static void sdl_audio_callback ( void * opaque , Uint8 * stream , int len )
2003-06-07 20:34:02 +02:00
{
VideoState * is = opaque ;
int audio_size , len1 ;
double pts ;
audio_callback_time = av_gettime ( ) ;
2005-12-17 19:14:38 +01:00
2003-06-07 20:34:02 +02:00
while ( len > 0 ) {
if ( is - > audio_buf_index > = is - > audio_buf_size ) {
2008-08-02 07:01:30 +02:00
audio_size = audio_decode_frame ( is , & pts ) ;
2003-06-07 20:34:02 +02:00
if ( audio_size < 0 ) {
/* if error, just output silence */
2009-01-10 00:54:57 +01:00
is - > audio_buf = is - > audio_buf1 ;
2003-06-07 20:34:02 +02: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 19:14:38 +01:00
audio_size = synchronize_audio ( is , ( int16_t * ) is - > audio_buf , audio_size ,
2003-06-07 20:34:02 +02: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 23:38:43 +01:00
AVCodecContext * avctx ;
2003-06-07 20:34:02 +02:00
AVCodec * codec ;
SDL_AudioSpec wanted_spec , spec ;
if ( stream_index < 0 | | stream_index > = ic - > nb_streams )
return - 1 ;
2010-02-16 23:38:43 +01:00
avctx = ic - > streams [ stream_index ] - > codec ;
2005-12-17 19:14:38 +01:00
2003-06-07 20:34:02 +02:00
/* prepare audio output */
2010-03-31 01:30:55 +02:00
if ( avctx - > codec_type = = AVMEDIA_TYPE_AUDIO ) {
2010-02-16 23:38:43 +01:00
if ( avctx - > channels > 0 ) {
avctx - > request_channels = FFMIN ( 2 , avctx - > channels ) ;
2007-12-15 08:05:14 +01:00
} else {
2010-02-16 23:38:43 +01:00
avctx - > request_channels = 2 ;
2003-08-06 15:07:23 +02:00
}
2003-06-07 20:34:02 +02:00
}
2010-02-16 23:38:43 +01: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 ;
2011-02-08 03:15:45 +01:00
avctx - > thread_count = thread_count ;
2010-02-16 23:38:43 +01:00
2010-09-30 01:06:51 +02:00
set_context_opts ( avctx , avcodec_opts [ avctx - > codec_type ] , 0 , codec ) ;
2008-09-28 21:39:18 +02:00
2003-06-07 20:34:02 +02:00
if ( ! codec | |
2010-02-16 23:38:43 +01:00
avcodec_open ( avctx , codec ) < 0 )
2003-06-07 20:34:02 +02:00
return - 1 ;
2008-01-06 03:44:05 +01:00
/* prepare audio output */
2010-03-31 01:30:55 +02:00
if ( avctx - > codec_type = = AVMEDIA_TYPE_AUDIO ) {
2010-02-16 23:38:43 +01:00
wanted_spec . freq = avctx - > sample_rate ;
2008-01-06 03:44:05 +01:00
wanted_spec . format = AUDIO_S16SYS ;
2010-02-16 23:38:43 +01:00
wanted_spec . channels = avctx - > channels ;
2008-01-06 03:44:05 +01: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 ;
2010-11-12 12:04:40 +01:00
is - > audio_src_fmt = AV_SAMPLE_FMT_S16 ;
2008-01-06 03:44:05 +01:00
}
2008-08-18 19:09:34 +02:00
ic - > streams [ stream_index ] - > discard = AVDISCARD_DEFAULT ;
2010-02-16 23:38:43 +01:00
switch ( avctx - > codec_type ) {
2010-03-31 01:30:55 +02:00
case AVMEDIA_TYPE_AUDIO :
2003-06-07 20:34:02 +02:00
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 15:07:23 +02: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 23:38:43 +01:00
is - > audio_diff_threshold = 2.0 * SDL_AUDIO_BUFFER_SIZE / avctx - > sample_rate ;
2003-08-06 15:07:23 +02:00
2003-06-07 20:34:02 +02:00
memset ( & is - > audio_pkt , 0 , sizeof ( is - > audio_pkt ) ) ;
packet_queue_init ( & is - > audioq ) ;
2005-12-22 02:10:11 +01:00
SDL_PauseAudio ( 0 ) ;
2003-06-07 20:34:02 +02:00
break ;
2010-03-31 01:30:55 +02:00
case AVMEDIA_TYPE_VIDEO :
2003-06-07 20:34:02 +02:00
is - > video_stream = stream_index ;
is - > video_st = ic - > streams [ stream_index ] ;
packet_queue_init ( & is - > videoq ) ;
is - > video_tid = SDL_CreateThread ( video_thread , is ) ;
break ;
2010-03-31 01:30:55 +02:00
case AVMEDIA_TYPE_SUBTITLE :
2005-08-14 03:29:34 +02:00
is - > subtitle_stream = stream_index ;
is - > subtitle_st = ic - > streams [ stream_index ] ;
packet_queue_init ( & is - > subtitleq ) ;
2005-12-17 19:14:38 +01:00
2005-08-14 03:29:34 +02:00
is - > subtitle_tid = SDL_CreateThread ( subtitle_thread , is ) ;
break ;
2003-06-07 20:34:02 +02:00
default :
break ;
}
return 0 ;
}
static void stream_component_close ( VideoState * is , int stream_index )
{
AVFormatContext * ic = is - > ic ;
2010-02-16 23:38:43 +01:00
AVCodecContext * avctx ;
2005-12-17 19:14:38 +01:00
2005-08-14 03:29:34 +02:00
if ( stream_index < 0 | | stream_index > = ic - > nb_streams )
return ;
2010-02-16 23:38:43 +01:00
avctx = ic - > streams [ stream_index ] - > codec ;
2003-06-07 20:34:02 +02:00
2010-02-16 23:38:43 +01:00
switch ( avctx - > codec_type ) {
2010-03-31 01:30:55 +02:00
case AVMEDIA_TYPE_AUDIO :
2003-06-07 20:34:02 +02:00
packet_queue_abort ( & is - > audioq ) ;
SDL_CloseAudio ( ) ;
packet_queue_end ( & is - > audioq ) ;
2008-08-02 07:01:30 +02:00
if ( is - > reformat_ctx )
av_audio_convert_free ( is - > reformat_ctx ) ;
2010-02-24 15:45:18 +01:00
is - > reformat_ctx = NULL ;
2003-06-07 20:34:02 +02:00
break ;
2010-03-31 01:30:55 +02:00
case AVMEDIA_TYPE_VIDEO :
2003-06-07 20:34:02 +02:00
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 ;
2010-03-31 01:30:55 +02:00
case AVMEDIA_TYPE_SUBTITLE :
2005-08-14 03:29:34 +02:00
packet_queue_abort ( & is - > subtitleq ) ;
2005-12-17 19:14:38 +01:00
2005-08-14 03:29:34 +02: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 19:14:38 +01:00
2005-08-14 03:29:34 +02: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 20:34:02 +02:00
default :
break ;
}
2008-08-18 19:09:34 +02:00
ic - > streams [ stream_index ] - > discard = AVDISCARD_ALL ;
2010-02-16 23:38:43 +01:00
avcodec_close ( avctx ) ;
switch ( avctx - > codec_type ) {
2010-03-31 01:30:55 +02:00
case AVMEDIA_TYPE_AUDIO :
2003-06-07 20:34:02 +02:00
is - > audio_st = NULL ;
is - > audio_stream = - 1 ;
break ;
2010-03-31 01:30:55 +02:00
case AVMEDIA_TYPE_VIDEO :
2003-06-07 20:34:02 +02:00
is - > video_st = NULL ;
is - > video_stream = - 1 ;
break ;
2010-03-31 01:30:55 +02:00
case AVMEDIA_TYPE_SUBTITLE :
2005-08-14 03:29:34 +02:00
is - > subtitle_st = NULL ;
is - > subtitle_stream = - 1 ;
break ;
2003-06-07 20:34:02 +02:00
default :
break ;
}
}
2003-07-17 12:29:50 +02: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 20:34:02 +02: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 17:46:40 +01:00
int err , i , ret ;
2010-03-31 01:30:55 +02:00
int st_index [ AVMEDIA_TYPE_NB ] ;
2003-06-07 20:34:02 +02:00
AVPacket pkt1 , * pkt = & pkt1 ;
2003-09-16 21:37:27 +02:00
AVFormatParameters params , * ap = & params ;
2009-04-19 22:24:44 +02:00
int eof = 0 ;
2010-04-01 22:56:23 +02:00
int pkt_in_play_range = 0 ;
2003-06-07 20:34:02 +02:00
2010-01-30 23:47:08 +01:00
ic = avformat_alloc_context ( ) ;
2010-02-23 17:46:40 +01:00
memset ( st_index , - 1 , sizeof ( st_index ) ) ;
2003-06-07 20:34:02 +02:00
is - > video_stream = - 1 ;
is - > audio_stream = - 1 ;
2005-08-14 03:29:34 +02:00
is - > subtitle_stream = - 1 ;
2003-06-07 20:34:02 +02:00
2003-07-17 12:29:50 +02:00
global_video_state = is ;
2011-04-04 20:15:44 +02:00
avio_set_interrupt_cb ( decode_interrupt_cb ) ;
2003-07-17 12:29:50 +02:00
2003-09-16 21:37:27 +02:00
memset ( ap , 0 , sizeof ( * ap ) ) ;
2005-12-17 19:14:38 +01:00
2010-01-30 23:47:08 +01:00
ap - > prealloced_context = 1 ;
2007-02-22 01:33:09 +01:00
ap - > width = frame_width ;
ap - > height = frame_height ;
2007-02-09 14:24:08 +01:00
ap - > time_base = ( AVRational ) { 1 , 25 } ;
2007-02-22 01:33:09 +01:00
ap - > pix_fmt = frame_pix_fmt ;
2007-02-09 14:24:08 +01:00
2010-09-30 01:06:51 +02:00
set_context_opts ( ic , avformat_opts , AV_OPT_FLAG_DECODING_PARAM , NULL ) ;
2010-01-30 23:47:08 +01:00
2003-09-16 21:37:27 +02:00
err = av_open_input_file ( & ic , is - > filename , is - > iformat , 0 , ap ) ;
2003-08-06 15:07:23 +02:00
if ( err < 0 ) {
print_error ( is - > filename , err ) ;
ret = - 1 ;
goto fail ;
}
2003-06-07 20:34:02 +02:00
is - > ic = ic ;
2005-08-15 16:22:43 +02:00
if ( genpts )
ic - > flags | = AVFMT_FLAG_GENPTS ;
2011-04-24 22:48:42 +02:00
/* Set AVCodecContext options so they will be seen by av_find_stream_info() */
for ( i = 0 ; i < ic - > nb_streams ; i + + ) {
AVCodecContext * dec = ic - > streams [ i ] - > codec ;
switch ( dec - > codec_type ) {
case AVMEDIA_TYPE_AUDIO :
set_context_opts ( dec , avcodec_opts [ AVMEDIA_TYPE_AUDIO ] ,
AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM ,
NULL ) ;
break ;
case AVMEDIA_TYPE_VIDEO :
set_context_opts ( dec , avcodec_opts [ AVMEDIA_TYPE_VIDEO ] ,
AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM ,
NULL ) ;
break ;
}
}
2007-08-24 09:20:28 +02: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 08:41:00 +01: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 19:59:45 +01:00
2010-02-02 19:02:16 +01:00
if ( seek_by_bytes < 0 )
seek_by_bytes = ! ! ( ic - > iformat - > flags & AVFMT_TS_DISCONT ) ;
2003-11-10 19:59:45 +01: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 17:24:30 +01:00
ret = avformat_seek_file ( ic , - 1 , INT64_MIN , timestamp , INT64_MAX , 0 ) ;
2003-11-10 19:59:45 +01:00
if ( ret < 0 ) {
2005-12-17 19:14:38 +01:00
fprintf ( stderr , " %s: could not seek to position %0.3f \n " ,
2003-11-10 19:59:45 +01:00
is - > filename , ( double ) timestamp / AV_TIME_BASE ) ;
}
}
2010-12-27 10:08:36 +01:00
for ( i = 0 ; i < ic - > nb_streams ; i + + )
2008-08-18 19:09:34 +02:00
ic - > streams [ i ] - > discard = AVDISCARD_ALL ;
2010-12-28 10:52:31 +01:00
if ( ! video_disable )
2010-12-27 10:08:36 +01:00
st_index [ AVMEDIA_TYPE_VIDEO ] =
av_find_best_stream ( ic , AVMEDIA_TYPE_VIDEO ,
wanted_stream [ AVMEDIA_TYPE_VIDEO ] , - 1 , NULL , 0 ) ;
2010-12-28 10:52:31 +01:00
if ( ! audio_disable )
2010-12-27 10:08:36 +01:00
st_index [ AVMEDIA_TYPE_AUDIO ] =
av_find_best_stream ( ic , AVMEDIA_TYPE_AUDIO ,
wanted_stream [ AVMEDIA_TYPE_AUDIO ] ,
st_index [ AVMEDIA_TYPE_VIDEO ] ,
NULL , 0 ) ;
2010-12-28 10:52:31 +01:00
if ( ! video_disable )
2010-12-27 10:08:36 +01:00
st_index [ AVMEDIA_TYPE_SUBTITLE ] =
av_find_best_stream ( ic , AVMEDIA_TYPE_SUBTITLE ,
wanted_stream [ AVMEDIA_TYPE_SUBTITLE ] ,
( st_index [ AVMEDIA_TYPE_AUDIO ] > = 0 ?
st_index [ AVMEDIA_TYPE_AUDIO ] :
st_index [ AVMEDIA_TYPE_VIDEO ] ) ,
NULL , 0 ) ;
2003-06-07 20:34:02 +02:00
if ( show_status ) {
2011-02-16 09:52:35 +01:00
av_dump_format ( ic , 0 , is - > filename , 0 ) ;
2003-06-07 20:34:02 +02:00
}
/* open the streams */
2010-03-31 01:30:55 +02:00
if ( st_index [ AVMEDIA_TYPE_AUDIO ] > = 0 ) {
stream_component_open ( is , st_index [ AVMEDIA_TYPE_AUDIO ] ) ;
2003-06-07 20:34:02 +02:00
}
2010-02-18 01:19:50 +01:00
ret = - 1 ;
2010-03-31 01:30:55 +02:00
if ( st_index [ AVMEDIA_TYPE_VIDEO ] > = 0 ) {
ret = stream_component_open ( is , st_index [ AVMEDIA_TYPE_VIDEO ] ) ;
2010-02-18 01:19:50 +01:00
}
2010-03-11 03:35:04 +01:00
is - > refresh_tid = SDL_CreateThread ( refresh_thread , is ) ;
2010-02-18 01:19:50 +01:00
if ( ret < 0 ) {
2003-06-07 20:34:02 +02:00
if ( ! display_disable )
2010-02-05 16:26:42 +01:00
is - > show_audio = 2 ;
2003-06-07 20:34:02 +02:00
}
2010-03-31 01:30:55 +02:00
if ( st_index [ AVMEDIA_TYPE_SUBTITLE ] > = 0 ) {
stream_component_open ( is , st_index [ AVMEDIA_TYPE_SUBTITLE ] ) ;
2009-02-09 02:38:12 +01:00
}
2003-06-07 20:34:02 +02:00
if ( is - > video_stream < 0 & & is - > audio_stream < 0 ) {
2003-08-06 15:07:23 +02:00
fprintf ( stderr , " %s: could not open codecs \n " , is - > filename ) ;
ret = - 1 ;
2003-06-07 20:34:02 +02:00
goto fail ;
}
for ( ; ; ) {
if ( is - > abort_request )
break ;
2003-07-17 12:29:50 +02:00
if ( is - > paused ! = is - > last_paused ) {
is - > last_paused = is - > paused ;
2003-11-10 19:59:45 +01:00
if ( is - > paused )
2010-02-01 11:32:17 +01:00
is - > read_pause_return = av_read_pause ( ic ) ;
2003-11-10 19:59:45 +01:00
else
av_read_play ( ic ) ;
2003-07-17 12:29:50 +02:00
}
2009-01-19 22:39:07 +01:00
# if CONFIG_RTSP_DEMUXER
if ( is - > paused & & ! strcmp ( ic - > iformat - > name , " rtsp " ) ) {
2003-07-17 12:29:50 +02:00
/* wait 10 ms to avoid trying to get another packet */
/* XXX: horrible */
SDL_Delay ( 10 ) ;
continue ;
}
2003-08-29 01:11:23 +02:00
# endif
2003-11-10 19:59:45 +01:00
if ( is - > seek_req ) {
2007-01-23 15:22:25 +01:00
int64_t seek_target = is - > seek_pos ;
2009-03-14 17:24:30 +01: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 15:22:25 +01:00
2009-03-14 17:24:30 +01:00
ret = avformat_seek_file ( is - > ic , - 1 , seek_min , seek_target , seek_max , is - > seek_flags ) ;
2003-11-10 19:59:45 +01:00
if ( ret < 0 ) {
fprintf ( stderr , " %s: error while seeking \n " , is - > ic - > filename ) ;
2004-01-10 23:36:35 +01:00
} else {
if ( is - > audio_stream > = 0 ) {
packet_queue_flush ( & is - > audioq ) ;
2006-11-16 12:58:27 +01:00
packet_queue_put ( & is - > audioq , & flush_pkt ) ;
2004-01-10 23:36:35 +01:00
}
2005-08-14 03:29:34 +02:00
if ( is - > subtitle_stream > = 0 ) {
packet_queue_flush ( & is - > subtitleq ) ;
2006-11-16 12:58:27 +01:00
packet_queue_put ( & is - > subtitleq , & flush_pkt ) ;
2005-08-14 03:29:34 +02:00
}
2004-01-10 23:36:35 +01:00
if ( is - > video_stream > = 0 ) {
packet_queue_flush ( & is - > videoq ) ;
2006-11-16 12:58:27 +01:00
packet_queue_put ( & is - > videoq , & flush_pkt ) ;
2004-01-10 23:36:35 +01:00
}
2003-11-10 19:59:45 +01:00
}
is - > seek_req = 0 ;
2009-04-28 20:02:21 +02:00
eof = 0 ;
2003-11-10 19:59:45 +01:00
}
2003-07-17 12:29:50 +02:00
2003-06-07 20:34:02 +02:00
/* if the queue are full, no need to read more */
2010-01-30 22:27:17 +01: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 20:34:02 +02:00
/* wait 10 ms */
SDL_Delay ( 10 ) ;
continue ;
}
2010-10-09 19:50:17 +02:00
if ( eof ) {
2009-04-19 23:44:03 +02:00
if ( is - > video_stream > = 0 ) {
2009-04-19 23:44:25 +02: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 23:44:03 +02:00
}
2009-04-19 22:25:20 +02:00
SDL_Delay ( 10 ) ;
2010-03-23 18:39:51 +01: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 02:54:24 +01:00
}
2009-01-07 21:45:39 +01:00
continue ;
}
2003-11-10 19:59:45 +01:00
ret = av_read_frame ( ic , pkt ) ;
2003-06-07 20:34:02 +02:00
if ( ret < 0 ) {
2011-03-21 14:52:54 +01:00
if ( ret = = AVERROR_EOF | | ( ic - > pb & & ic - > pb - > eof_reached ) )
2009-04-19 22:24:44 +02:00
eof = 1 ;
2011-03-21 14:52:54 +01:00
if ( ic - > pb & & ic - > pb - > error )
2005-12-22 02:10:11 +01:00
break ;
2009-04-19 22:24:44 +02:00
SDL_Delay ( 100 ) ; /* wait for user event */
continue ;
2003-06-07 20:34:02 +02:00
}
2010-04-01 22:56:23 +02:00
/* check if packet is in play range specified by user, then queue, otherwise discard */
pkt_in_play_range = duration = = AV_NOPTS_VALUE | |
( pkt - > pts - ic - > streams [ pkt - > stream_index ] - > start_time ) *
av_q2d ( ic - > streams [ pkt - > stream_index ] - > time_base ) -
( double ) ( start_time ! = AV_NOPTS_VALUE ? start_time : 0 ) / 1000000
< = ( ( double ) duration / 1000000 ) ;
if ( pkt - > stream_index = = is - > audio_stream & & pkt_in_play_range ) {
2003-06-07 20:34:02 +02:00
packet_queue_put ( & is - > audioq , pkt ) ;
2010-04-01 22:56:23 +02:00
} else if ( pkt - > stream_index = = is - > video_stream & & pkt_in_play_range ) {
2003-06-07 20:34:02 +02:00
packet_queue_put ( & is - > videoq , pkt ) ;
2010-04-01 22:56:23 +02:00
} else if ( pkt - > stream_index = = is - > subtitle_stream & & pkt_in_play_range ) {
2005-08-14 03:29:34 +02:00
packet_queue_put ( & is - > subtitleq , pkt ) ;
2003-06-07 20:34:02 +02:00
} else {
av_free_packet ( pkt ) ;
}
}
/* wait until the end */
while ( ! is - > abort_request ) {
SDL_Delay ( 100 ) ;
}
2003-08-06 15:07:23 +02:00
ret = 0 ;
2003-06-07 20:34:02 +02:00
fail :
2003-07-17 12:29:50 +02:00
/* disable interrupting */
global_video_state = NULL ;
2003-06-07 20:34:02 +02: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 03:29:34 +02:00
if ( is - > subtitle_stream > = 0 )
stream_component_close ( is , is - > subtitle_stream ) ;
2003-08-06 15:07:23 +02:00
if ( is - > ic ) {
av_close_input_file ( is - > ic ) ;
is - > ic = NULL ; /* safety */
}
2011-04-04 20:15:44 +02:00
avio_set_interrupt_cb ( NULL ) ;
2003-07-17 12:29:50 +02:00
2003-08-06 15:07:23 +02:00
if ( ret ! = 0 ) {
SDL_Event event ;
2005-12-17 19:14:38 +01:00
2003-08-06 15:07:23 +02:00
event . type = FF_QUIT_EVENT ;
event . user . data1 = is ;
SDL_PushEvent ( & event ) ;
}
2003-06-07 20:34:02 +02:00
return 0 ;
}
2003-08-06 15:07:23 +02:00
static VideoState * stream_open ( const char * filename , AVInputFormat * iformat )
2003-06-07 20:34:02 +02:00
{
VideoState * is ;
is = av_mallocz ( sizeof ( VideoState ) ) ;
if ( ! is )
return NULL ;
2007-06-24 13:27:12 +02:00
av_strlcpy ( is - > filename , filename , sizeof ( is - > filename ) ) ;
2003-08-06 15:07:23 +02:00
is - > iformat = iformat ;
2003-06-07 20:34:02 +02:00
is - > ytop = 0 ;
is - > xleft = 0 ;
/* start video display */
is - > pictq_mutex = SDL_CreateMutex ( ) ;
is - > pictq_cond = SDL_CreateCond ( ) ;
2005-12-17 19:14:38 +01:00
2005-08-14 03:29:34 +02:00
is - > subpq_mutex = SDL_CreateMutex ( ) ;
is - > subpq_cond = SDL_CreateCond ( ) ;
2005-12-17 19:14:38 +01:00
2003-08-06 15:07:23 +02:00
is - > av_sync_type = av_sync_type ;
2003-06-07 20:34:02 +02:00
is - > parse_tid = SDL_CreateThread ( decode_thread , is ) ;
if ( ! is - > parse_tid ) {
av_free ( is ) ;
return NULL ;
}
return is ;
}
2006-06-18 13:33:14 +02:00
static void stream_cycle_channel ( VideoState * is , int codec_type )
2003-08-06 15:07:23 +02:00
{
AVFormatContext * ic = is - > ic ;
int start_index , stream_index ;
AVStream * st ;
2010-03-31 01:30:55 +02:00
if ( codec_type = = AVMEDIA_TYPE_VIDEO )
2003-08-06 15:07:23 +02:00
start_index = is - > video_stream ;
2010-03-31 01:30:55 +02:00
else if ( codec_type = = AVMEDIA_TYPE_AUDIO )
2003-08-06 15:07:23 +02:00
start_index = is - > audio_stream ;
2005-08-14 03:29:34 +02:00
else
start_index = is - > subtitle_stream ;
2010-03-31 01:30:55 +02:00
if ( start_index < ( codec_type = = AVMEDIA_TYPE_SUBTITLE ? - 1 : 0 ) )
2003-08-06 15:07:23 +02:00
return ;
stream_index = start_index ;
for ( ; ; ) {
if ( + + stream_index > = is - > ic - > nb_streams )
2005-08-14 03:29:34 +02:00
{
2010-03-31 01:30:55 +02:00
if ( codec_type = = AVMEDIA_TYPE_SUBTITLE )
2005-08-14 03:29:34 +02:00
{
stream_index = - 1 ;
goto the_end ;
} else
stream_index = 0 ;
}
2003-08-06 15:07:23 +02:00
if ( stream_index = = start_index )
return ;
st = ic - > streams [ stream_index ] ;
2005-07-18 00:24:36 +02:00
if ( st - > codec - > codec_type = = codec_type ) {
2003-08-06 15:07:23 +02:00
/* check that parameters are OK */
switch ( codec_type ) {
2010-03-31 01:30:55 +02:00
case AVMEDIA_TYPE_AUDIO :
2005-07-18 00:24:36 +02:00
if ( st - > codec - > sample_rate ! = 0 & &
st - > codec - > channels ! = 0 )
2003-08-06 15:07:23 +02:00
goto the_end ;
break ;
2010-03-31 01:30:55 +02:00
case AVMEDIA_TYPE_VIDEO :
case AVMEDIA_TYPE_SUBTITLE :
2003-08-06 15:07:23 +02:00
goto the_end ;
default :
break ;
}
}
}
the_end :
stream_component_close ( is , start_index ) ;
stream_component_open ( is , stream_index ) ;
}
2006-06-18 13:33:14 +02:00
static void toggle_full_screen ( void )
2003-06-07 20:34:02 +02:00
{
is_full_screen = ! is_full_screen ;
2006-12-31 21:04:08 +01:00
video_open ( cur_stream ) ;
2003-06-07 20:34:02 +02:00
}
2006-06-18 13:33:14 +02:00
static void toggle_pause ( void )
2003-06-07 20:34:02 +02:00
{
if ( cur_stream )
stream_pause ( cur_stream ) ;
2003-12-28 02:19:41 +01:00
step = 0 ;
}
2006-06-18 13:33:14 +02:00
static void step_to_next_frame ( void )
2003-12-28 02:19:41 +01:00
{
if ( cur_stream ) {
2007-08-27 10:06:48 +02:00
/* if the stream is paused unpause it, then step */
2003-12-28 02:19:41 +01:00
if ( cur_stream - > paused )
2007-08-27 10:06:48 +02:00
stream_pause ( cur_stream ) ;
2003-12-28 02:19:41 +01:00
}
step = 1 ;
2003-06-07 20:34:02 +02:00
}
2006-06-18 13:33:14 +02:00
static void toggle_audio_display ( void )
2003-06-07 20:34:02 +02:00
{
if ( cur_stream ) {
2010-02-04 00:34:06 +01:00
int bgcolor = SDL_MapRGB ( screen - > format , 0x00 , 0x00 , 0x00 ) ;
2010-02-05 03:06:38 +01:00
cur_stream - > show_audio = ( cur_stream - > show_audio + 1 ) % 3 ;
2010-02-04 00:34:06 +01: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 20:34:02 +02:00
}
}
/* handle an event sent by the GUI */
2006-06-18 13:33:14 +02:00
static void event_loop ( void )
2003-06-07 20:34:02 +02:00
{
SDL_Event event ;
2003-11-28 20:19:01 +01:00
double incr , pos , frac ;
2003-06-07 20:34:02 +02:00
for ( ; ; ) {
2010-02-05 01:37:39 +01:00
double x ;
2003-06-07 20:34:02 +02:00
SDL_WaitEvent ( & event ) ;
switch ( event . type ) {
case SDL_KEYDOWN :
2010-07-04 14:43:12 +02:00
if ( exit_on_keydown ) {
do_exit ( ) ;
break ;
}
2003-06-07 20:34:02 +02:00
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 02:19:41 +01:00
case SDLK_s : //S: Step to next frame
step_to_next_frame ( ) ;
break ;
2003-06-07 20:34:02 +02:00
case SDLK_a :
2005-12-17 19:14:38 +01:00
if ( cur_stream )
2010-03-31 01:30:55 +02:00
stream_cycle_channel ( cur_stream , AVMEDIA_TYPE_AUDIO ) ;
2003-08-06 15:07:23 +02:00
break ;
case SDLK_v :
2005-12-17 19:14:38 +01:00
if ( cur_stream )
2010-03-31 01:30:55 +02:00
stream_cycle_channel ( cur_stream , AVMEDIA_TYPE_VIDEO ) ;
2003-08-06 15:07:23 +02:00
break ;
2005-08-14 03:29:34 +02:00
case SDLK_t :
2005-12-17 19:14:38 +01:00
if ( cur_stream )
2010-03-31 01:30:55 +02:00
stream_cycle_channel ( cur_stream , AVMEDIA_TYPE_SUBTITLE ) ;
2005-08-14 03:29:34 +02:00
break ;
2003-08-06 15:07:23 +02:00
case SDLK_w :
2003-06-07 20:34:02 +02:00
toggle_audio_display ( ) ;
break ;
2003-11-10 19:59:45 +01: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 23:35:41 +01:00
if ( seek_by_bytes ) {
2010-02-02 17:51:02 +01: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
2011-03-03 20:11:45 +01:00
pos = avio_tell ( cur_stream - > ic - > pb ) ;
2006-11-07 23:35:41 +01:00
if ( cur_stream - > ic - > bit_rate )
2010-02-03 16:19:19 +01:00
incr * = cur_stream - > ic - > bit_rate / 8.0 ;
2006-11-07 23:35:41 +01:00
else
incr * = 180000.0 ;
pos + = incr ;
2010-01-31 00:19:59 +01:00
stream_seek ( cur_stream , pos , incr , 1 ) ;
2006-11-07 23:35:41 +01:00
} else {
pos = get_master_clock ( cur_stream ) ;
pos + = incr ;
2010-01-31 00:19:59 +01:00
stream_seek ( cur_stream , ( int64_t ) ( pos * AV_TIME_BASE ) , ( int64_t ) ( incr * AV_TIME_BASE ) , 0 ) ;
2006-11-07 23:35:41 +01:00
}
2003-11-10 19:59:45 +01:00
}
break ;
2003-06-07 20:34:02 +02:00
default :
break ;
}
break ;
2003-11-28 20:19:01 +01:00
case SDL_MOUSEBUTTONDOWN :
2010-07-04 14:43:12 +02:00
if ( exit_on_mousedown ) {
do_exit ( ) ;
break ;
}
2010-02-05 01:37:39 +01: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 02:10:11 +01:00
if ( cur_stream ) {
2010-01-31 00:19:59 +01:00
if ( seek_by_bytes | | cur_stream - > ic - > duration < = 0 ) {
2011-03-04 19:57:36 +01:00
uint64_t size = avio_size ( cur_stream - > ic - > pb ) ;
2010-02-05 01:37:39 +01:00
stream_seek ( cur_stream , size * x / cur_stream - > width , 0 , 1 ) ;
2010-01-31 00:19:59 +01:00
} else {
2010-01-31 19:07:58 +01: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 01:37:39 +01:00
frac = x / cur_stream - > width ;
2010-01-31 19:07:58 +01: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-31 00:19:59 +01:00
}
2005-12-22 02:10:11 +01:00
}
break ;
2003-06-07 20:34:02 +02:00
case SDL_VIDEORESIZE :
if ( cur_stream ) {
2005-12-17 19:14:38 +01:00
screen = SDL_SetVideoMode ( event . resize . w , event . resize . h , 0 ,
2003-06-07 20:34:02 +02:00
SDL_HWSURFACE | SDL_RESIZABLE | SDL_ASYNCBLIT | SDL_HWACCEL ) ;
2006-12-31 21:09:10 +01:00
screen_width = cur_stream - > width = event . resize . w ;
screen_height = cur_stream - > height = event . resize . h ;
2003-06-07 20:34:02 +02:00
}
break ;
case SDL_QUIT :
2003-08-06 15:07:23 +02:00
case FF_QUIT_EVENT :
2003-06-07 20:34:02 +02:00
do_exit ( ) ;
break ;
case FF_ALLOC_EVENT :
2006-12-30 12:22:46 +01:00
video_open ( event . user . data1 ) ;
2003-06-07 20:34:02 +02:00
alloc_picture ( event . user . data1 ) ;
break ;
case FF_REFRESH_EVENT :
video_refresh_timer ( event . user . data1 ) ;
2010-03-11 03:35:04 +01:00
cur_stream - > refresh = 0 ;
2003-06-07 20:34:02 +02:00
break ;
default :
break ;
}
}
}
2011-05-28 01:53:00 +02:00
static int opt_frame_size ( const char * opt , const char * arg )
2007-02-22 01:33:09 +01:00
{
2010-07-27 01:12:28 +02:00
if ( av_parse_video_size ( & frame_width , & frame_height , arg ) < 0 ) {
2007-02-22 01:33:09 +01:00
fprintf ( stderr , " Incorrect frame size \n " ) ;
2011-05-28 01:53:00 +02:00
return AVERROR ( EINVAL ) ;
2007-02-22 01:33:09 +01:00
}
if ( ( frame_width % 2 ) ! = 0 | | ( frame_height % 2 ) ! = 0 ) {
fprintf ( stderr , " Frame size must be a multiple of 2 \n " ) ;
2011-05-28 01:53:00 +02:00
return AVERROR ( EINVAL ) ;
2007-02-22 01:33:09 +01:00
}
2011-05-28 01:53:00 +02:00
return 0 ;
2007-02-22 01:33:09 +01:00
}
2008-03-22 19:50:23 +01:00
static int opt_width ( const char * opt , const char * arg )
2003-06-07 20:34:02 +02:00
{
2008-03-22 19:50:23 +01:00
screen_width = parse_number_or_die ( opt , arg , OPT_INT64 , 1 , INT_MAX ) ;
return 0 ;
2003-06-07 20:34:02 +02:00
}
2008-03-22 19:50:23 +01:00
static int opt_height ( const char * opt , const char * arg )
2003-06-07 20:34:02 +02:00
{
2008-03-22 19:50:23 +01:00
screen_height = parse_number_or_die ( opt , arg , OPT_INT64 , 1 , INT_MAX ) ;
return 0 ;
2003-06-07 20:34:02 +02:00
}
2011-05-28 01:53:00 +02:00
static int opt_format ( const char * opt , const char * arg )
2003-06-07 20:34:02 +02:00
{
file_iformat = av_find_input_format ( arg ) ;
if ( ! file_iformat ) {
fprintf ( stderr , " Unknown input format: %s \n " , arg ) ;
2011-05-28 01:53:00 +02:00
return AVERROR ( EINVAL ) ;
2003-06-07 20:34:02 +02:00
}
2011-05-28 01:53:00 +02:00
return 0 ;
2003-06-07 20:34:02 +02:00
}
2003-09-16 21:37:27 +02:00
2011-05-28 01:53:00 +02:00
static int opt_frame_pix_fmt ( const char * opt , const char * arg )
2007-02-22 01:33:09 +01:00
{
2010-01-30 20:10:26 +01:00
frame_pix_fmt = av_get_pix_fmt ( arg ) ;
2011-05-28 01:53:00 +02:00
return 0 ;
2007-02-22 01:33:09 +01:00
}
2008-06-12 13:33:53 +02:00
static int opt_sync ( const char * opt , const char * arg )
2003-08-06 15:07:23 +02: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 14:13:29 +02:00
else {
2008-06-12 13:33:53 +02:00
fprintf ( stderr , " Unknown value for %s: %s \n " , opt , arg ) ;
2007-08-09 14:13:29 +02:00
exit ( 1 ) ;
}
2008-06-12 13:33:53 +02:00
return 0 ;
2003-08-06 15:07:23 +02:00
}
2008-04-01 14:22:33 +02:00
static int opt_seek ( const char * opt , const char * arg )
2003-11-10 19:59:45 +01:00
{
2008-04-01 14:22:33 +02:00
start_time = parse_time_or_die ( opt , arg , 1 ) ;
return 0 ;
2003-11-10 19:59:45 +01:00
}
2010-04-01 22:56:23 +02:00
static int opt_duration ( const char * opt , const char * arg )
{
duration = parse_time_or_die ( opt , arg , 1 ) ;
return 0 ;
}
2008-03-22 19:50:23 +01:00
static int opt_debug ( const char * opt , const char * arg )
2003-11-28 16:14:24 +01:00
{
2007-12-12 22:48:50 +01:00
av_log_set_level ( 99 ) ;
2008-03-22 19:50:23 +01:00
debug = parse_number_or_die ( opt , arg , OPT_INT64 , 0 , INT_MAX ) ;
return 0 ;
2003-11-28 16:14:24 +01:00
}
2005-12-17 19:14:38 +01:00
2008-03-22 19:50:23 +01:00
static int opt_vismv ( const char * opt , const char * arg )
2003-12-30 03:12:12 +01:00
{
2008-03-22 19:50:23 +01:00
debug_mv = parse_number_or_die ( opt , arg , OPT_INT64 , INT_MIN , INT_MAX ) ;
return 0 ;
2003-12-30 03:12:12 +01:00
}
2004-02-23 21:56:56 +01:00
2008-03-22 19:50:23 +01:00
static int opt_thread_count ( const char * opt , const char * arg )
2004-02-23 21:56:56 +01:00
{
2008-03-22 19:50:23 +01:00
thread_count = parse_number_or_die ( opt , arg , OPT_INT64 , 0 , INT_MAX ) ;
2009-01-14 00:44:16 +01:00
# if !HAVE_THREADS
2004-02-23 21:56:56 +01:00
fprintf ( stderr , " Warning: not compiled with thread support, using thread emulation \n " ) ;
# endif
2008-03-22 19:50:23 +01:00
return 0 ;
2004-02-23 21:56:56 +01:00
}
2005-12-17 19:14:38 +01:00
2008-05-01 17:10:44 +02:00
static const OptionDef options [ ] = {
2009-11-30 00:12:19 +01:00
# include "cmdutils_common_opts.h"
2011-05-28 01:53:00 +02:00
{ " x " , HAS_ARG , { ( void * ) opt_width } , " force displayed width " , " width " } ,
{ " y " , HAS_ARG , { ( void * ) opt_height } , " force displayed height " , " height " } ,
2007-02-22 01:33:09 +01:00
{ " s " , HAS_ARG | OPT_VIDEO , { ( void * ) opt_frame_size } , " set frame size (WxH or abbreviation) " , " size " } ,
2003-08-06 15:07:23 +02:00
{ " fs " , OPT_BOOL , { ( void * ) & is_full_screen } , " force full screen " } ,
2003-06-07 20:34:02 +02:00
{ " an " , OPT_BOOL , { ( void * ) & audio_disable } , " disable audio " } ,
{ " vn " , OPT_BOOL , { ( void * ) & video_disable } , " disable video " } ,
2010-03-31 01:30:55 +02:00
{ " ast " , OPT_INT | HAS_ARG | OPT_EXPERT , { ( void * ) & wanted_stream [ AVMEDIA_TYPE_AUDIO ] } , " select desired audio stream " , " stream_number " } ,
{ " vst " , OPT_INT | HAS_ARG | OPT_EXPERT , { ( void * ) & wanted_stream [ AVMEDIA_TYPE_VIDEO ] } , " select desired video stream " , " stream_number " } ,
{ " sst " , OPT_INT | HAS_ARG | OPT_EXPERT , { ( void * ) & wanted_stream [ AVMEDIA_TYPE_SUBTITLE ] } , " select desired subtitle stream " , " stream_number " } ,
2011-05-28 01:53:00 +02:00
{ " ss " , HAS_ARG , { ( void * ) & opt_seek } , " seek to a given position in seconds " , " pos " } ,
{ " t " , HAS_ARG , { ( void * ) & opt_duration } , " play \" duration \" seconds of audio/video " , " duration " } ,
2010-02-05 05:42:20 +01:00
{ " bytes " , OPT_INT | HAS_ARG , { ( void * ) & seek_by_bytes } , " seek by bytes 0=off 1=on -1=auto " , " val " } ,
2003-06-07 20:34:02 +02:00
{ " nodisp " , OPT_BOOL , { ( void * ) & display_disable } , " disable graphical display " } ,
{ " f " , HAS_ARG , { ( void * ) opt_format } , " force format " , " fmt " } ,
2007-02-22 01:33:09 +01:00
{ " pix_fmt " , HAS_ARG | OPT_EXPERT | OPT_VIDEO , { ( void * ) opt_frame_pix_fmt } , " set pixel format " , " format " } ,
2009-08-04 11:26:33 +02:00
{ " stats " , OPT_BOOL | OPT_EXPERT , { ( void * ) & show_status } , " show status " , " " } ,
2011-05-28 01:53:00 +02:00
{ " debug " , HAS_ARG | OPT_EXPERT , { ( void * ) opt_debug } , " print specific debug info " , " " } ,
2004-04-30 20:54:36 +02:00
{ " bug " , OPT_INT | HAS_ARG | OPT_EXPERT , { ( void * ) & workaround_bugs } , " workaround bugs " , " " } ,
2011-05-28 01:53:00 +02:00
{ " vismv " , HAS_ARG | OPT_EXPERT , { ( void * ) opt_vismv } , " visualize motion vectors " , " " } ,
2004-09-02 17:30:46 +02:00
{ " fast " , OPT_BOOL | OPT_EXPERT , { ( void * ) & fast } , " non spec compliant optimizations " , " " } ,
2005-08-15 16:22:43 +02:00
{ " genpts " , OPT_BOOL | OPT_EXPERT , { ( void * ) & genpts } , " generate pts " , " " } ,
2010-02-01 04:12:01 +01:00
{ " drp " , OPT_INT | HAS_ARG | OPT_EXPERT , { ( void * ) & decoder_reorder_pts } , " let decoder reorder pts 0=off 1=on -1=auto " , " " } ,
2004-09-26 01:18:58 +02:00
{ " lowres " , OPT_INT | HAS_ARG | OPT_EXPERT , { ( void * ) & lowres } , " " , " " } ,
2005-07-14 23:39:36 +02: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-26 01:18:58 +02:00
{ " idct " , OPT_INT | HAS_ARG | OPT_EXPERT , { ( void * ) & idct } , " set idct algo " , " algo " } ,
2008-09-08 20:18:49 +02:00
{ " er " , OPT_INT | HAS_ARG | OPT_EXPERT , { ( void * ) & error_recognition } , " set error detection threshold (0-4) " , " threshold " } ,
2005-07-17 12:18:10 +02:00
{ " ec " , OPT_INT | HAS_ARG | OPT_EXPERT , { ( void * ) & error_concealment } , " set error concealment options " , " bit_mask " } ,
2011-05-28 01:53:00 +02:00
{ " sync " , HAS_ARG | OPT_EXPERT , { ( void * ) opt_sync } , " set audio-video sync. type (type=audio/video/ext) " , " type " } ,
{ " threads " , HAS_ARG | OPT_EXPERT , { ( void * ) opt_thread_count } , " thread count " , " count " } ,
2010-02-04 02:54:24 +01:00
{ " autoexit " , OPT_BOOL | OPT_EXPERT , { ( void * ) & autoexit } , " exit at the end " , " " } ,
2010-07-04 14:43:12 +02:00
{ " exitonkeydown " , OPT_BOOL | OPT_EXPERT , { ( void * ) & exit_on_keydown } , " exit on key down " , " " } ,
{ " exitonmousedown " , OPT_BOOL | OPT_EXPERT , { ( void * ) & exit_on_mousedown } , " exit on mouse down " , " " } ,
2010-03-23 18:39:51 +01:00
{ " loop " , OPT_INT | HAS_ARG | OPT_EXPERT , { ( void * ) & loop } , " set number of times the playback shall be looped " , " loop count " } ,
2010-03-11 03:35:04 +01:00
{ " framedrop " , OPT_BOOL | OPT_EXPERT , { ( void * ) & framedrop } , " drop frames when cpu is too slow " , " " } ,
2010-03-18 00:39:18 +01:00
{ " window_title " , OPT_STRING | HAS_ARG , { ( void * ) & window_title } , " set window title " , " window title " } ,
2010-03-05 03:20:10 +01:00
# if CONFIG_AVFILTER
2010-05-13 01:18:12 +02:00
{ " vf " , OPT_STRING | HAS_ARG , { ( void * ) & vfilters } , " video filters " , " filter list " } ,
2010-03-05 03:20:10 +01:00
# endif
2010-03-11 12:25:51 +01:00
{ " rdftspeed " , OPT_INT | HAS_ARG | OPT_AUDIO | OPT_EXPERT , { ( void * ) & rdftspeed } , " rdft speed " , " msecs " } ,
2011-05-28 01:53:00 +02:00
{ " default " , HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT , { ( void * ) opt_default } , " generic catch all option " , " " } ,
2011-03-08 15:29:46 +01:00
{ " i " , 0 , { NULL } , " ffmpeg compatibility dummy option " , " " } ,
2003-06-07 20:34:02 +02:00
{ NULL , } ,
} ;
2009-12-21 03:12:34 +01:00
static void show_usage ( void )
2003-06-07 20:34:02 +02:00
{
2009-12-21 03:09:17 +01:00
printf ( " Simple media player \n " ) ;
printf ( " usage: ffplay [options] input_file \n " ) ;
2003-06-07 20:34:02 +02:00
printf ( " \n " ) ;
2009-12-21 03:12:34 +01:00
}
static void show_help ( void )
{
2010-09-24 02:51:43 +02:00
av_log_set_callback ( log_callback_help ) ;
2009-12-21 03:12:34 +01:00
show_usage ( ) ;
2003-08-24 17:29:48 +02:00
show_help_options ( options , " Main options: \n " ,
OPT_EXPERT , 0 ) ;
show_help_options ( options , " \n Advanced options: \n " ,
OPT_EXPERT , OPT_EXPERT ) ;
2010-09-24 02:51:43 +02:00
printf ( " \n " ) ;
av_opt_show2 ( avcodec_opts [ 0 ] , NULL ,
AV_OPT_FLAG_DECODING_PARAM , 0 ) ;
printf ( " \n " ) ;
av_opt_show2 ( avformat_opts , NULL ,
AV_OPT_FLAG_DECODING_PARAM , 0 ) ;
# if !CONFIG_AVFILTER
printf ( " \n " ) ;
av_opt_show2 ( sws_opts , NULL ,
AV_OPT_FLAG_ENCODING_PARAM , 0 ) ;
# endif
2003-06-07 20:34:02 +02:00
printf ( " \n While playing: \n "
" q, ESC quit \n "
" f toggle full screen \n "
" p, SPC pause \n "
2003-08-06 15:07:23 +02:00
" a cycle audio channel \n "
" v cycle video channel \n "
2005-08-14 03:29:34 +02:00
" t cycle subtitle channel \n "
2003-08-06 15:07:23 +02:00
" w show audio waves \n "
2010-04-09 19:52:34 +02:00
" s activate frame-step mode \n "
2003-11-10 19:59:45 +01:00
" left/right seek backward/forward 10 seconds \n "
" down/up seek backward/forward 1 minute \n "
2003-11-28 20:19:01 +01:00
" mouse click seek to percentage in file corresponding to fraction of width \n "
2003-06-07 20:34:02 +02:00
) ;
}
2008-05-01 17:10:44 +02:00
static void opt_input_file ( const char * filename )
2003-06-07 20:34:02 +02:00
{
2010-03-02 00:10:27 +01: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 22:22:16 +02:00
if ( ! strcmp ( filename , " - " ) )
2007-08-14 11:14:31 +02:00
filename = " pipe: " ;
2003-06-07 20:34:02 +02:00
input_filename = filename ;
}
/* Called from the main */
int main ( int argc , char * * argv )
{
2010-10-02 10:44:33 +02:00
int flags ;
2005-12-17 19:14:38 +01:00
2010-09-24 17:39:10 +02:00
av_log_set_flags ( AV_LOG_SKIP_REPEATED ) ;
2003-06-07 20:34:02 +02:00
/* register all codecs, demux and protocols */
2007-11-22 17:10:02 +01:00
avcodec_register_all ( ) ;
2010-04-22 10:55:23 +02:00
# if CONFIG_AVDEVICE
2007-11-22 17:10:02 +01:00
avdevice_register_all ( ) ;
2010-04-22 10:55:23 +02:00
# endif
2010-03-05 03:20:10 +01:00
# if CONFIG_AVFILTER
avfilter_register_all ( ) ;
# endif
2003-06-07 20:34:02 +02:00
av_register_all ( ) ;
2010-10-02 10:44:33 +02:00
init_opts ( ) ;
2008-09-28 21:39:18 +02:00
2008-05-29 10:48:51 +02:00
show_banner ( ) ;
2007-09-27 15:52:33 +02:00
2007-08-14 15:58:28 +02:00
parse_options ( argc , argv , options , opt_input_file ) ;
2003-06-07 20:34:02 +02:00
2007-08-09 14:13:29 +02:00
if ( ! input_filename ) {
2009-12-21 03:15:46 +01:00
show_usage ( ) ;
2008-11-18 00:15:29 +01:00
fprintf ( stderr , " An input file must be specified \n " ) ;
2009-12-21 03:15:46 +01:00
fprintf ( stderr , " Use -h to get full help or, even better, run 'man ffplay' \n " ) ;
2007-08-09 14:13:29 +02:00
exit ( 1 ) ;
}
2003-06-07 20:34:02 +02:00
if ( display_disable ) {
video_disable = 1 ;
}
2003-06-14 13:09:57 +02:00
flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER ;
2007-08-27 11:17:03 +02:00
# if !defined(__MINGW32__) && !defined(__APPLE__)
flags | = SDL_INIT_EVENTTHREAD ; /* Not supported on Windows or Mac OS X */
2003-06-14 13:09:57 +02:00
# endif
2003-06-07 20:34:02 +02:00
if ( SDL_Init ( flags ) ) {
2003-09-29 02:14:13 +02:00
fprintf ( stderr , " Could not initialize SDL - %s \n " , SDL_GetError ( ) ) ;
2003-06-07 20:34:02 +02:00
exit ( 1 ) ;
}
if ( ! display_disable ) {
2009-01-14 00:44:16 +01:00
# if HAVE_SDL_VIDEO_SIZE
2006-06-10 00:43:21 +02:00
const SDL_VideoInfo * vi = SDL_GetVideoInfo ( ) ;
fs_screen_width = vi - > current_w ;
fs_screen_height = vi - > current_h ;
2006-06-14 23:02:55 +02:00
# endif
2003-06-07 20:34:02 +02:00
}
SDL_EventState ( SDL_ACTIVEEVENT , SDL_IGNORE ) ;
SDL_EventState ( SDL_SYSWMEVENT , SDL_IGNORE ) ;
SDL_EventState ( SDL_USEREVENT , SDL_IGNORE ) ;
2006-11-16 12:58:27 +01:00
av_init_packet ( & flush_pkt ) ;
flush_pkt . data = " FLUSH " ;
2003-08-06 15:07:23 +02:00
cur_stream = stream_open ( input_filename , file_iformat ) ;
2003-06-07 20:34:02 +02:00
event_loop ( ) ;
/* never returns */
return 0 ;
}