#include "libavcodec/audioconvert.h"
#include "libavcodec/colorspace.h"
#include "libavcodec/opt.h"
+#include "libavcodec/dsputil.h"
#include "cmdutils.h"
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 {
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;
PacketQueue videoq;
double video_current_pts; ///<current displayed pts (different from video_clock if frame fifos are used)
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;
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 int error_recognition = FF_ER_CAREFUL;
static int error_concealment = 3;
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 */
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;
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) {
-// fprintf(stderr, "Internal error detected in the SDL timer\n");
+ 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_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);
*
* @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);
- schedule_refresh(is, (int)(compute_frame_delay(vp->pts, is) * 1000 + 0.5));
}
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(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 (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
+ || (decoder_reorder_pts && is->faulty_pts<is->faulty_dts)
|| pkt->dts == AV_NOPTS_VALUE)
&& frame->reordered_opaque != AV_NOPTS_VALUE)
pts= frame->reordered_opaque;
// 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);
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();
packet_queue_init(&is->videoq);
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) {
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;
}
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) {
if(seek_by_bytes || cur_stream->ic->duration<=0){
uint64_t size= url_fsize(cur_stream->ic->pb);
- stream_seek(cur_stream, size*(double)event.button.x/(double)cur_stream->width, 0, 1);
+ stream_seek(cur_stream, size*x/cur_stream->width, 0, 1);
}else{
int64_t ts;
int ns, hh, mm, ss;
thh = tns/3600;
tmm = (tns%3600)/60;
tss = (tns%60);
- frac = (double)event.button.x/(double)cur_stream->width;
+ frac = x/cur_stream->width;
ns = frac*tns;
hh = ns/3600;
mm = (ns%3600)/60;
{ "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" },
{ "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, },
};
}
SDL_EventState(SDL_ACTIVEEVENT, SDL_IGNORE);
- SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE);
SDL_EventState(SDL_USEREVENT, SDL_IGNORE);