#include <math.h>
#include <limits.h>
#include "libavutil/avstring.h"
+#include "libavutil/pixdesc.h"
#include "libavformat/avformat.h"
#include "libavdevice/avdevice.h"
#include "libswscale/swscale.h"
#include "libavcodec/audioconvert.h"
#include "libavcodec/colorspace.h"
#include "libavcodec/opt.h"
+#include "libavcodec/dsputil.h"
#include "cmdutils.h"
#endif
#undef exit
+#undef printf
+#undef fprintf
const char program_name[] = "FFplay";
const int program_birth_year = 2003;
//#define DEBUG_SYNC
-#define MAX_VIDEOQ_SIZE (5 * 256 * 1024)
-#define MAX_AUDIOQ_SIZE (20 * 16 * 1024)
-#define MAX_SUBTITLEQ_SIZE (5 * 16 * 1024)
+#define MAX_QUEUE_SIZE (15 * 1024 * 1024)
+#define MIN_AUDIOQ_SIZE (20 * 16 * 1024)
+#define MIN_FRAMES 5
/* SDL audio buffer size, in samples. Should be small to have precise
A/V sync as SDL does not have hardware buffer fullness info. */
typedef struct VideoPicture {
double pts; ///<presentation time stamp for this picture
+ int64_t pos; ///<byte position in file
SDL_Overlay *bmp;
int width, height; /* source height & width */
int allocated;
+ SDL_TimerID timer_id;
} VideoPicture;
typedef struct SubPicture {
int seek_flags;
int64_t seek_pos;
int64_t seek_rel;
+ int read_pause_return;
AVFormatContext *ic;
int dtg_active_format;
int audio_hw_buf_size;
/* samples output by the codec. we reserve more space for avsync
compensation */
- 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]);
+ 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];
uint8_t *audio_buf;
unsigned int audio_buf_size; /* in bytes */
int audio_buf_index; /* in bytes */
int16_t sample_array[SAMPLE_ARRAY_SIZE];
int sample_array_index;
int last_i_start;
+ RDFTContext rdft;
+ int rdft_bits;
+ int xpos;
SDL_Thread *subtitle_tid;
int subtitle_stream;
AVStream *video_st;
PacketQueue videoq;
double video_current_pts; ///<current displayed pts (different from video_clock if frame fifos are used)
- int64_t video_current_pts_time; ///<time (av_gettime) at which we updated video_current_pts - used to have running video pts
+ double video_current_pts_drift; ///<video_current_pts - time (av_gettime) at which we updated video_current_pts - used to have running video pts
+ int64_t video_current_pos; ///<current displayed file pos
VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE];
int pictq_size, pictq_rindex, pictq_windex;
SDL_mutex *pictq_mutex;
// QETimer *video_timer;
char filename[1024];
int width, height, xleft, ytop;
+
+ int64_t faulty_pts;
+ int64_t faulty_dts;
+ int64_t last_dts_for_fault_detection;
+ int64_t last_pts_for_fault_detection;
+
} VideoState;
static void show_help(void);
static int wanted_audio_stream= 0;
static int wanted_video_stream= 0;
static int wanted_subtitle_stream= -1;
-static int seek_by_bytes;
+static int seek_by_bytes=-1;
static int display_disable;
static int show_status = 1;
static int av_sync_type = AV_SYNC_AUDIO_MASTER;
static enum AVDiscard skip_loop_filter= AVDISCARD_DEFAULT;
static int error_recognition = FF_ER_CAREFUL;
static int error_concealment = 3;
-static int decoder_reorder_pts= 0;
+static int decoder_reorder_pts= -1;
+static int autoexit;
/* current context */
static int is_full_screen;
static SDL_Surface *screen;
+static int packet_queue_put(PacketQueue *q, AVPacket *pkt);
+
/* packet queue handling */
static void packet_queue_init(PacketQueue *q)
{
memset(q, 0, sizeof(PacketQueue));
q->mutex = SDL_CreateMutex();
q->cond = SDL_CreateCond();
+ packet_queue_put(q, &flush_pkt);
}
static void packet_queue_flush(PacketQueue *q)
int i, i_start, x, y1, y, ys, delay, n, nb_display_channels;
int ch, channels, h, h2, bgcolor, fgcolor;
int16_t time_diff;
+ int rdft_bits, nb_freq;
+
+ for(rdft_bits=1; (1<<rdft_bits)<2*s->height; rdft_bits++)
+ ;
+ nb_freq= 1<<(rdft_bits-1);
/* compute display index : center on currently output samples */
channels = s->audio_st->codec->channels;
nb_display_channels = channels;
if (!s->paused) {
+ int data_used= s->show_audio==1 ? s->width : (2*nb_freq);
n = 2 * channels;
delay = audio_write_get_buf_size(s);
delay /= n;
delay += (time_diff * s->audio_st->codec->sample_rate) / 1000000;
}
- delay -= s->width / 2;
- if (delay < s->width)
- delay = s->width;
+ delay -= data_used / 2;
+ if (delay < data_used)
+ delay = data_used;
i_start= x = compute_mod(s->sample_array_index - delay * channels, SAMPLE_ARRAY_SIZE);
-
- 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;
+ if(s->show_audio==1){
+ 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;
+ }
}
}
}
bgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0x00);
- 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;
+ if(s->show_audio==1){
+ 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;
}
- fill_rectangle(screen,
- s->xleft + x, ys, 1, y,
- fgcolor);
- i += channels;
- if (i >= SAMPLE_ARRAY_SIZE)
- i -= SAMPLE_ARRAY_SIZE;
}
- }
- fgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0xff);
+ fgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0xff);
- for(ch = 1;ch < nb_display_channels; ch++) {
- y = s->ytop + ch * h;
- fill_rectangle(screen,
- s->xleft, y, s->width, 1,
- fgcolor);
+ 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);
+ }else{
+ nb_display_channels= FFMIN(nb_display_channels, 2);
+ if(rdft_bits != s->rdft_bits){
+ ff_rdft_end(&s->rdft);
+ ff_rdft_init(&s->rdft, rdft_bits, RDFT);
+ s->rdft_bits= rdft_bits;
+ }
+ {
+ FFTSample data[2][2*nb_freq];
+ for(ch = 0;ch < nb_display_channels; ch++) {
+ i = i_start + ch;
+ for(x = 0; x < 2*nb_freq; x++) {
+ double w= (x-nb_freq)*(1.0/nb_freq);
+ data[ch][x]= s->sample_array[i]*(1.0-w*w);
+ i += channels;
+ if (i >= SAMPLE_ARRAY_SIZE)
+ i -= SAMPLE_ARRAY_SIZE;
+ }
+ ff_rdft_calc(&s->rdft, data[ch]);
+ }
+ //least efficient way to do this, we should of course directly access it but its more than fast enough
+ for(y=0; y<s->height; y++){
+ double w= 1/sqrt(nb_freq);
+ int a= sqrt(w*sqrt(data[0][2*y+0]*data[0][2*y+0] + data[0][2*y+1]*data[0][2*y+1]));
+ int b= sqrt(w*sqrt(data[1][2*y+0]*data[1][2*y+0] + data[1][2*y+1]*data[1][2*y+1]));
+ a= FFMIN(a,255);
+ b= FFMIN(b,255);
+ fgcolor = SDL_MapRGB(screen->format, a, b, (a+b)/2);
+
+ fill_rectangle(screen,
+ s->xpos, s->height-y, 1, 1,
+ fgcolor);
+ }
+ }
+ SDL_UpdateRect(screen, s->xpos, s->ytop, 1, s->height);
+ s->xpos++;
+ if(s->xpos >= s->width)
+ s->xpos= s->xleft;
}
- SDL_UpdateRect(screen, s->xleft, s->ytop, s->width, s->height);
}
static int video_open(VideoState *is){
}
/* schedule a video refresh in 'delay' ms */
-static void schedule_refresh(VideoState *is, int delay)
+static SDL_TimerID schedule_refresh(VideoState *is, int delay)
{
if(!delay) delay=1; //SDL seems to be buggy when the delay is 0
- SDL_AddTimer(delay, sdl_refresh_timer_cb, is);
+ return SDL_AddTimer(delay, sdl_refresh_timer_cb, is);
}
/* get the current audio clock value */
/* get the current video clock value */
static double get_video_clock(VideoState *is)
{
- double delta;
if (is->paused) {
- delta = 0;
+ return is->video_current_pts;
} else {
- delta = (av_gettime() - is->video_current_pts_time) / 1000000.0;
+ return is->video_current_pts_drift + av_gettime() / 1000000.0;
}
- return is->video_current_pts + delta;
}
/* get the current external clock value */
}
/* seek in the stream */
-static void stream_seek(VideoState *is, int64_t pos, int64_t rel)
+static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int seek_by_bytes)
{
if (!is->seek_req) {
is->seek_pos = pos;
is->seek_rel = rel;
+ is->seek_flags &= ~AVSEEK_FLAG_BYTE;
if (seek_by_bytes)
is->seek_flags |= AVSEEK_FLAG_BYTE;
is->seek_req = 1;
/* pause or resume the video */
static void stream_pause(VideoState *is)
{
- is->paused = !is->paused;
- if (!is->paused) {
- is->video_current_pts = get_video_clock(is);
- is->frame_timer += (av_gettime() - is->video_current_pts_time) / 1000000.0;
+ if (is->paused) {
+ is->frame_timer += av_gettime() / 1000000.0 + is->video_current_pts_drift - is->video_current_pts;
+ if(is->read_pause_return != AVERROR(ENOSYS)){
+ is->video_current_pts = is->video_current_pts_drift + av_gettime() / 1000000.0;
+ }
+ is->video_current_pts_drift = is->video_current_pts - av_gettime() / 1000000.0;
}
+ is->paused = !is->paused;
}
static double compute_frame_delay(double frame_current_pts, VideoState *is)
{
- double actual_delay, delay, sync_threshold, ref_clock, diff;
+ double actual_delay, delay, sync_threshold, diff;
/* compute nominal delay */
delay = frame_current_pts - is->frame_last_pts;
is->av_sync_type == AV_SYNC_EXTERNAL_CLOCK)) {
/* if video is slave, we try to correct big delays by
duplicating or deleting a frame */
- ref_clock = get_master_clock(is);
- diff = frame_current_pts - ref_clock;
+ diff = get_video_clock(is) - get_master_clock(is);
/* skip or repeat frame. We take into account the
delay to compute the threshold. I still don't know
if (is->video_st) {
if (is->pictq_size == 0) {
- /* if no picture, need to wait */
- schedule_refresh(is, 1);
+ fprintf(stderr, "Internal error detected in the SDL timer\n");
} else {
/* dequeue the picture */
vp = &is->pictq[is->pictq_rindex];
/* update current video pts */
is->video_current_pts = vp->pts;
- is->video_current_pts_time = av_gettime();
-
- /* launch timer for next picture */
- schedule_refresh(is, (int)(compute_frame_delay(vp->pts, is) * 1000 + 0.5));
+ is->video_current_pts_drift = is->video_current_pts - av_gettime() / 1000000.0;
+ is->video_current_pos = vp->pos;
if(is->subtitle_st) {
if (is->subtitle_stream_changed) {
is->pictq_rindex = 0;
SDL_LockMutex(is->pictq_mutex);
+ vp->timer_id= 0;
is->pictq_size--;
SDL_CondSignal(is->pictq_cond);
SDL_UnlockMutex(is->pictq_mutex);
av_diff = 0;
if (is->audio_st && is->video_st)
av_diff = get_audio_clock(is) - get_video_clock(is);
- printf("%7.2f A-V:%7.3f aq=%5dKB vq=%5dKB sq=%5dB \r",
- get_master_clock(is), av_diff, aqsize / 1024, vqsize / 1024, sqsize);
+ printf("%7.2f A-V:%7.3f aq=%5dKB vq=%5dKB sq=%5dB f=%Ld/%Ld \r",
+ get_master_clock(is), av_diff, aqsize / 1024, vqsize / 1024, sqsize, is->faulty_dts, is->faulty_pts);
fflush(stdout);
last_time = cur_time;
}
*
* @param pts the dts of the pkt / pts of the frame and guessed if not known
*/
-static int queue_picture(VideoState *is, AVFrame *src_frame, double pts)
+static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, int64_t pos)
{
VideoPicture *vp;
int dst_pix_fmt;
SDL_UnlockYUVOverlay(vp->bmp);
vp->pts = pts;
+ vp->pos = pos;
/* now we can update the picture count */
if (++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE)
is->pictq_windex = 0;
SDL_LockMutex(is->pictq_mutex);
is->pictq_size++;
+ //We must schedule in a mutex as we must store the timer id before the timer dies or might end up freeing a alraedy freed id
+ vp->timer_id= schedule_refresh(is, (int)(compute_frame_delay(vp->pts, is) * 1000 + 0.5));
SDL_UnlockMutex(is->pictq_mutex);
}
return 0;
* compute the exact PTS for the picture if it is omitted in the stream
* @param pts1 the dts of the pkt / pts of the frame
*/
-static int output_picture2(VideoState *is, AVFrame *src_frame, double pts1)
+static int output_picture2(VideoState *is, AVFrame *src_frame, double pts1, int64_t pos)
{
double frame_delay, pts;
ftype, pts, pts1);
}
#endif
- return queue_picture(is, src_frame, pts);
+ return queue_picture(is, src_frame, pts, pos);
}
static int video_thread(void *arg)
{
VideoState *is = arg;
AVPacket pkt1, *pkt = &pkt1;
- int len1, got_picture;
+ int len1, got_picture, i;
AVFrame *frame= avcodec_alloc_frame();
double pts;
if(pkt->data == flush_pkt.data){
avcodec_flush_buffers(is->video_st->codec);
+
+ SDL_LockMutex(is->pictq_mutex);
+ //Make sure there are no long delay timers (ideally we should just flush the que but thats harder)
+ for(i=0; i<VIDEO_PICTURE_QUEUE_SIZE; i++){
+ if(is->pictq[i].timer_id){
+ SDL_RemoveTimer(is->pictq[i].timer_id);
+ is->pictq[i].timer_id=0;
+ schedule_refresh(is, 1);
+ }
+ }
+ while (is->pictq_size && !is->videoq.abort_request) {
+ SDL_CondWait(is->pictq_cond, is->pictq_mutex);
+ }
+ is->video_current_pos= -1;
+ SDL_UnlockMutex(is->pictq_mutex);
+
+ is->last_dts_for_fault_detection=
+ is->last_pts_for_fault_detection= INT64_MIN;
+ is->frame_last_pts= AV_NOPTS_VALUE;
+ is->frame_last_delay = 0;
+ is->frame_timer = (double)av_gettime() / 1000000.0;
+
continue;
}
frame, &got_picture,
pkt);
- if( (decoder_reorder_pts || pkt->dts == AV_NOPTS_VALUE)
+ if (got_picture) {
+ if(pkt->dts != AV_NOPTS_VALUE){
+ is->faulty_dts += pkt->dts <= is->last_dts_for_fault_detection;
+ is->last_dts_for_fault_detection= pkt->dts;
+ }
+ if(frame->reordered_opaque != AV_NOPTS_VALUE){
+ is->faulty_pts += frame->reordered_opaque <= is->last_pts_for_fault_detection;
+ is->last_pts_for_fault_detection= frame->reordered_opaque;
+ }
+ }
+
+ if( ( decoder_reorder_pts==1
+ || (decoder_reorder_pts && is->faulty_pts<is->faulty_dts)
+ || pkt->dts == AV_NOPTS_VALUE)
&& frame->reordered_opaque != AV_NOPTS_VALUE)
pts= frame->reordered_opaque;
else if(pkt->dts != AV_NOPTS_VALUE)
// if (len1 < 0)
// break;
if (got_picture) {
- if (output_picture2(is, frame, pts) < 0)
+ if (output_picture2(is, frame, pts, pkt->pos) < 0)
goto the_end;
}
av_free_packet(pkt);
enc->skip_loop_filter= skip_loop_filter;
enc->error_recognition= error_recognition;
enc->error_concealment= error_concealment;
+ avcodec_thread_init(enc, thread_count);
set_context_opts(enc, avcodec_opts[enc->codec_type], 0);
is->audio_src_fmt= SAMPLE_FMT_S16;
}
- if(thread_count>1)
- avcodec_thread_init(enc, thread_count);
- enc->thread_count= thread_count;
ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;
switch(enc->codec_type) {
case CODEC_TYPE_AUDIO:
is->video_stream = stream_index;
is->video_st = ic->streams[stream_index];
- is->frame_last_delay = 40e-3;
- is->frame_timer = (double)av_gettime() / 1000000.0;
- is->video_current_pts_time = av_gettime();
+// is->video_current_pts_time = av_gettime();
packet_queue_init(&is->videoq);
is->video_tid = SDL_CreateThread(video_thread, is);
AVFormatParameters params, *ap = ¶ms;
int eof=0;
+ ic = avformat_alloc_context();
+
video_index = -1;
audio_index = -1;
subtitle_index = -1;
memset(ap, 0, sizeof(*ap));
+ ap->prealloced_context = 1;
ap->width = frame_width;
ap->height= frame_height;
ap->time_base= (AVRational){1, 25};
ap->pix_fmt = frame_pix_fmt;
+ set_context_opts(ic, avformat_opts, AV_OPT_FLAG_DECODING_PARAM);
+
err = av_open_input_file(&ic, is->filename, is->iformat, 0, ap);
if (err < 0) {
print_error(is->filename, err);
if(ic->pb)
ic->pb->eof_reached= 0; //FIXME hack, ffplay maybe should not use url_feof() to test for the end
+ if(seek_by_bytes<0)
+ seek_by_bytes= !!(ic->iformat->flags & AVFMT_TS_DISCONT);
+
/* if seeking requested, we execute it */
if (start_time != AV_NOPTS_VALUE) {
int64_t timestamp;
if (video_index >= 0) {
stream_component_open(is, video_index);
} else {
+ /* add the refresh timer to draw the picture */
+ schedule_refresh(is, 40);
+
if (!display_disable)
- is->show_audio = 1;
+ is->show_audio = 2;
}
if (subtitle_index >= 0) {
if (is->paused != is->last_paused) {
is->last_paused = is->paused;
if (is->paused)
- av_read_pause(ic);
+ is->read_pause_return= av_read_pause(ic);
else
av_read_play(ic);
}
}
/* if the queue are full, no need to read more */
- if (is->audioq.size > MAX_AUDIOQ_SIZE ||
- is->videoq.size > MAX_VIDEOQ_SIZE ||
- is->subtitleq.size > MAX_SUBTITLEQ_SIZE) {
+ 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))) {
/* wait 10 ms */
SDL_Delay(10);
continue;
packet_queue_put(&is->videoq, pkt);
}
SDL_Delay(10);
+ if(autoexit && is->audioq.size + is->videoq.size + is->subtitleq.size ==0){
+ ret=AVERROR_EOF;
+ goto fail;
+ }
continue;
}
ret = av_read_frame(ic, pkt);
is->subpq_mutex = SDL_CreateMutex();
is->subpq_cond = SDL_CreateCond();
- /* add the refresh timer to draw the picture */
- schedule_refresh(is, 40);
-
is->av_sync_type = av_sync_type;
is->parse_tid = SDL_CreateThread(decode_thread, is);
if (!is->parse_tid) {
static void toggle_audio_display(void)
{
if (cur_stream) {
- cur_stream->show_audio = !cur_stream->show_audio;
+ int bgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0x00);
+ cur_stream->show_audio = (cur_stream->show_audio + 1) % 3;
+ fill_rectangle(screen,
+ cur_stream->xleft, cur_stream->ytop, cur_stream->width, cur_stream->height,
+ bgcolor);
+ SDL_UpdateRect(screen, cur_stream->xleft, cur_stream->ytop, cur_stream->width, cur_stream->height);
}
}
double incr, pos, frac;
for(;;) {
+ double x;
SDL_WaitEvent(&event);
switch(event.type) {
case SDL_KEYDOWN:
do_seek:
if (cur_stream) {
if (seek_by_bytes) {
- pos = url_ftell(cur_stream->ic->pb);
+ if (cur_stream->video_stream >= 0 && cur_stream->video_current_pos>=0){
+ pos= cur_stream->video_current_pos;
+ }else if(cur_stream->audio_stream >= 0 && cur_stream->audio_pkt.pos>=0){
+ pos= cur_stream->audio_pkt.pos;
+ }else
+ pos = url_ftell(cur_stream->ic->pb);
if (cur_stream->ic->bit_rate)
- incr *= cur_stream->ic->bit_rate / 60.0;
+ incr *= cur_stream->ic->bit_rate / 8.0;
else
incr *= 180000.0;
pos += incr;
- stream_seek(cur_stream, pos, incr);
+ stream_seek(cur_stream, pos, incr, 1);
} else {
pos = get_master_clock(cur_stream);
pos += incr;
- stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE), (int64_t)(incr * AV_TIME_BASE));
+ stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE), (int64_t)(incr * AV_TIME_BASE), 0);
}
}
break;
}
break;
case SDL_MOUSEBUTTONDOWN:
+ case SDL_MOUSEMOTION:
+ if(event.type ==SDL_MOUSEBUTTONDOWN){
+ x= event.button.x;
+ }else{
+ if(event.motion.state != SDL_PRESSED)
+ break;
+ x= event.motion.x;
+ }
if (cur_stream) {
- 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);
- frac = (double)event.button.x/(double)cur_stream->width;
- 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);
+ if(seek_by_bytes || cur_stream->ic->duration<=0){
+ uint64_t size= url_fsize(cur_stream->ic->pb);
+ stream_seek(cur_stream, size*x/cur_stream->width, 0, 1);
+ }else{
+ 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);
+ frac = x/cur_stream->width;
+ 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);
+ }
}
break;
case SDL_VIDEORESIZE:
static void opt_frame_pix_fmt(const char *arg)
{
- frame_pix_fmt = avcodec_get_pix_fmt(arg);
+ frame_pix_fmt = av_get_pix_fmt(arg);
}
static int opt_sync(const char *opt, const char *arg)
{ "vst", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&wanted_video_stream}, "select desired video stream", "stream_number" },
{ "sst", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&wanted_subtitle_stream}, "select desired subtitle stream", "stream_number" },
{ "ss", HAS_ARG | OPT_FUNC2, {(void*)&opt_seek}, "seek to a given position in seconds", "pos" },
- { "bytes", OPT_BOOL, {(void*)&seek_by_bytes}, "seek by bytes" },
+ { "bytes", OPT_INT | HAS_ARG, {(void*)&seek_by_bytes}, "seek by bytes 0=off 1=on -1=auto", "val" },
{ "nodisp", OPT_BOOL, {(void*)&display_disable}, "disable graphical display" },
{ "f", HAS_ARG, {(void*)opt_format}, "force format", "fmt" },
{ "pix_fmt", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_frame_pix_fmt}, "set pixel format", "format" },
{ "vismv", HAS_ARG | OPT_FUNC2 | OPT_EXPERT, {(void*)opt_vismv}, "visualize motion vectors", "" },
{ "fast", OPT_BOOL | OPT_EXPERT, {(void*)&fast}, "non spec compliant optimizations", "" },
{ "genpts", OPT_BOOL | OPT_EXPERT, {(void*)&genpts}, "generate pts", "" },
- { "drp", OPT_BOOL |OPT_EXPERT, {(void*)&decoder_reorder_pts}, "let decoder reorder pts", ""},
+ { "drp", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&decoder_reorder_pts}, "let decoder reorder pts 0=off 1=on -1=auto", ""},
{ "lowres", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&lowres}, "", "" },
{ "skiploop", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&skip_loop_filter}, "", "" },
{ "skipframe", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&skip_frame}, "", "" },
{ "ec", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&error_concealment}, "set error concealment options", "bit_mask" },
{ "sync", HAS_ARG | OPT_FUNC2 | OPT_EXPERT, {(void*)opt_sync}, "set audio-video sync. type (type=audio/video/ext)", "type" },
{ "threads", HAS_ARG | OPT_FUNC2 | OPT_EXPERT, {(void*)opt_thread_count}, "thread count", "count" },
+ { "autoexit", OPT_BOOL | OPT_EXPERT, {(void*)&autoexit}, "exit at the end", "" },
{ "default", OPT_FUNC2 | HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, {(void*)opt_default}, "generic catch all option", "" },
{ NULL, },
};
-static void show_help(void)
+static void show_usage(void)
{
- printf("usage: ffplay [options] input_file\n"
- "Simple media player\n");
+ printf("Simple media player\n");
+ printf("usage: ffplay [options] input_file\n");
printf("\n");
+}
+
+static void show_help(void)
+{
+ show_usage();
show_help_options(options, "Main options:\n",
OPT_EXPERT, 0);
show_help_options(options, "\nAdvanced options:\n",
parse_options(argc, argv, options, opt_input_file);
if (!input_filename) {
+ show_usage();
fprintf(stderr, "An input file must be specified\n");
+ fprintf(stderr, "Use -h to get full help or, even better, run 'man ffplay'\n");
exit(1);
}
}
SDL_EventState(SDL_ACTIVEEVENT, SDL_IGNORE);
- SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE);
SDL_EventState(SDL_USEREVENT, SDL_IGNORE);