Revision d38c9e7a ffplay.c
ffplay.c | ||
---|---|---|
48 | 48 |
#undef main /* We don't want SDL to override our main() */ |
49 | 49 |
#endif |
50 | 50 |
|
51 |
#include <unistd.h> |
|
52 |
#include <assert.h> |
|
53 |
|
|
51 | 54 |
const char program_name[] = "FFplay"; |
52 | 55 |
const int program_birth_year = 2003; |
53 | 56 |
|
... | ... | |
66 | 69 |
/* no AV correction is done if too big error */ |
67 | 70 |
#define AV_NOSYNC_THRESHOLD 10.0 |
68 | 71 |
|
72 |
#define FRAME_SKIP_FACTOR 0.05 |
|
73 |
|
|
69 | 74 |
/* maximum audio speed change to get correct sync */ |
70 | 75 |
#define SAMPLE_CORRECTION_PERCENT_MAX 10 |
71 | 76 |
|
... | ... | |
93 | 98 |
|
94 | 99 |
typedef struct VideoPicture { |
95 | 100 |
double pts; ///<presentation time stamp for this picture |
101 |
double target_clock; ///<av_gettime() time at which this should be displayed ideally |
|
96 | 102 |
int64_t pos; ///<byte position in file |
97 | 103 |
SDL_Overlay *bmp; |
98 | 104 |
int width, height; /* source height & width */ |
99 | 105 |
int allocated; |
100 |
SDL_TimerID timer_id; |
|
101 | 106 |
enum PixelFormat pix_fmt; |
102 | 107 |
|
103 | 108 |
#if CONFIG_AVFILTER |
... | ... | |
119 | 124 |
typedef struct VideoState { |
120 | 125 |
SDL_Thread *parse_tid; |
121 | 126 |
SDL_Thread *video_tid; |
127 |
SDL_Thread *refresh_tid; |
|
122 | 128 |
AVInputFormat *iformat; |
123 | 129 |
int no_background; |
124 | 130 |
int abort_request; |
... | ... | |
206 | 212 |
#if CONFIG_AVFILTER |
207 | 213 |
AVFilterContext *out_video_filter; ///<the last filter in the video chain |
208 | 214 |
#endif |
215 |
|
|
216 |
float skip_frames; |
|
217 |
float skip_frames_index; |
|
218 |
int refresh; |
|
209 | 219 |
} VideoState; |
210 | 220 |
|
211 | 221 |
static void show_help(void); |
... | ... | |
249 | 259 |
static int error_concealment = 3; |
250 | 260 |
static int decoder_reorder_pts= -1; |
251 | 261 |
static int autoexit; |
262 |
static int framedrop=1; |
|
252 | 263 |
#if CONFIG_AVFILTER |
253 | 264 |
static char *vfilters = NULL; |
254 | 265 |
#endif |
... | ... | |
999 | 1010 |
video_image_display(is); |
1000 | 1011 |
} |
1001 | 1012 |
|
1002 |
static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque)
|
|
1013 |
static int refresh_thread(void *opaque)
|
|
1003 | 1014 |
{ |
1015 |
VideoState *is= opaque; |
|
1016 |
while(!is->abort_request){ |
|
1004 | 1017 |
SDL_Event event; |
1005 | 1018 |
event.type = FF_REFRESH_EVENT; |
1006 | 1019 |
event.user.data1 = opaque; |
1020 |
if(!is->refresh){ |
|
1021 |
is->refresh=1; |
|
1007 | 1022 |
SDL_PushEvent(&event); |
1008 |
return 0; /* 0 means stop timer */ |
|
1009 |
} |
|
1010 |
|
|
1011 |
/* schedule a video refresh in 'delay' ms */ |
|
1012 |
static SDL_TimerID schedule_refresh(VideoState *is, int delay) |
|
1013 |
{ |
|
1014 |
if(!delay) delay=1; //SDL seems to be buggy when the delay is 0 |
|
1015 |
return SDL_AddTimer(delay, sdl_refresh_timer_cb, is); |
|
1023 |
} |
|
1024 |
usleep(5000); //FIXME ideally we should wait the correct time but SDLs event passing is so slow it would be silly |
|
1025 |
} |
|
1026 |
return 0; |
|
1016 | 1027 |
} |
1017 | 1028 |
|
1018 | 1029 |
/* get the current audio clock value */ |
... | ... | |
1097 | 1108 |
is->paused = !is->paused; |
1098 | 1109 |
} |
1099 | 1110 |
|
1100 |
static double compute_frame_delay(double frame_current_pts, VideoState *is)
|
|
1111 |
static double compute_target_time(double frame_current_pts, VideoState *is)
|
|
1101 | 1112 |
{ |
1102 |
double actual_delay, delay, sync_threshold, diff;
|
|
1113 |
double delay, sync_threshold, diff; |
|
1103 | 1114 |
|
1104 | 1115 |
/* compute nominal delay */ |
1105 | 1116 |
delay = frame_current_pts - is->frame_last_pts; |
... | ... | |
1129 | 1140 |
delay = 2 * delay; |
1130 | 1141 |
} |
1131 | 1142 |
} |
1132 |
|
|
1133 | 1143 |
is->frame_timer += delay; |
1134 |
/* compute the REAL delay (we need to do that to avoid |
|
1135 |
long term errors */ |
|
1136 |
actual_delay = is->frame_timer - (av_gettime() / 1000000.0); |
|
1137 |
if (actual_delay < 0.010) { |
|
1138 |
/* XXX: should skip picture */ |
|
1139 |
actual_delay = 0.010; |
|
1140 |
} |
|
1141 |
|
|
1142 | 1144 |
#if defined(DEBUG_SYNC) |
1143 | 1145 |
printf("video: delay=%0.3f actual_delay=%0.3f pts=%0.3f A-V=%f\n", |
1144 | 1146 |
delay, actual_delay, frame_current_pts, -diff); |
1145 | 1147 |
#endif |
1146 | 1148 |
|
1147 |
return actual_delay;
|
|
1149 |
return is->frame_timer;
|
|
1148 | 1150 |
} |
1149 | 1151 |
|
1150 | 1152 |
/* called to display each frame */ |
... | ... | |
1156 | 1158 |
SubPicture *sp, *sp2; |
1157 | 1159 |
|
1158 | 1160 |
if (is->video_st) { |
1161 |
retry: |
|
1159 | 1162 |
if (is->pictq_size == 0) { |
1160 |
fprintf(stderr, "Internal error detected in the SDL timer\n");
|
|
1163 |
//nothing to do, no picture to display in the que
|
|
1161 | 1164 |
} else { |
1165 |
double time= av_gettime()/1000000.0; |
|
1166 |
double next_target; |
|
1162 | 1167 |
/* dequeue the picture */ |
1163 | 1168 |
vp = &is->pictq[is->pictq_rindex]; |
1164 | 1169 |
|
1170 |
if(time < vp->target_clock) |
|
1171 |
return; |
|
1165 | 1172 |
/* update current video pts */ |
1166 | 1173 |
is->video_current_pts = vp->pts; |
1167 |
is->video_current_pts_drift = is->video_current_pts - av_gettime() / 1000000.0;
|
|
1174 |
is->video_current_pts_drift = is->video_current_pts - time;
|
|
1168 | 1175 |
is->video_current_pos = vp->pos; |
1176 |
if(is->pictq_size > 1){ |
|
1177 |
VideoPicture *nextvp= &is->pictq[(is->pictq_rindex+1)%VIDEO_PICTURE_QUEUE_SIZE]; |
|
1178 |
assert(nextvp->target_clock >= vp->target_clock); |
|
1179 |
next_target= nextvp->target_clock; |
|
1180 |
}else{ |
|
1181 |
next_target= vp->target_clock + is->video_clock - vp->pts; //FIXME pass durations cleanly |
|
1182 |
} |
|
1183 |
if(framedrop && time > next_target){ |
|
1184 |
is->skip_frames *= 1.0 + FRAME_SKIP_FACTOR; |
|
1185 |
if(is->pictq_size > 1 || time > next_target + 0.5){ |
|
1186 |
/* update queue size and signal for next picture */ |
|
1187 |
if (++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) |
|
1188 |
is->pictq_rindex = 0; |
|
1189 |
|
|
1190 |
SDL_LockMutex(is->pictq_mutex); |
|
1191 |
is->pictq_size--; |
|
1192 |
SDL_CondSignal(is->pictq_cond); |
|
1193 |
SDL_UnlockMutex(is->pictq_mutex); |
|
1194 |
goto retry; |
|
1195 |
} |
|
1196 |
} |
|
1169 | 1197 |
|
1170 | 1198 |
if(is->subtitle_st) { |
1171 | 1199 |
if (is->subtitle_stream_changed) { |
... | ... | |
1219 | 1247 |
is->pictq_rindex = 0; |
1220 | 1248 |
|
1221 | 1249 |
SDL_LockMutex(is->pictq_mutex); |
1222 |
vp->timer_id= 0; |
|
1223 | 1250 |
is->pictq_size--; |
1224 | 1251 |
SDL_CondSignal(is->pictq_cond); |
1225 | 1252 |
SDL_UnlockMutex(is->pictq_mutex); |
... | ... | |
1227 | 1254 |
} else if (is->audio_st) { |
1228 | 1255 |
/* draw the next audio frame */ |
1229 | 1256 |
|
1230 |
schedule_refresh(is, 40); |
|
1231 |
|
|
1232 | 1257 |
/* if only audio stream, then display the audio bars (better |
1233 | 1258 |
than nothing, just to test the implementation */ |
1234 | 1259 |
|
1235 | 1260 |
/* display picture */ |
1236 | 1261 |
video_display(is); |
1237 |
} else { |
|
1238 |
schedule_refresh(is, 100); |
|
1239 | 1262 |
} |
1240 | 1263 |
if (show_status) { |
1241 | 1264 |
static int64_t last_time; |
... | ... | |
1314 | 1337 |
#endif |
1315 | 1338 |
/* wait until we have space to put a new picture */ |
1316 | 1339 |
SDL_LockMutex(is->pictq_mutex); |
1340 |
|
|
1341 |
if(is->pictq_size>=VIDEO_PICTURE_QUEUE_SIZE && !is->refresh) |
|
1342 |
is->skip_frames= FFMAX(1.0 - FRAME_SKIP_FACTOR, is->skip_frames * (1.0-FRAME_SKIP_FACTOR)); |
|
1343 |
|
|
1317 | 1344 |
while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && |
1318 | 1345 |
!is->videoq.abort_request) { |
1319 | 1346 |
SDL_CondWait(is->pictq_cond, is->pictq_mutex); |
... | ... | |
1411 | 1438 |
if (++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) |
1412 | 1439 |
is->pictq_windex = 0; |
1413 | 1440 |
SDL_LockMutex(is->pictq_mutex); |
1441 |
vp->target_clock= compute_target_time(vp->pts, is); |
|
1442 |
|
|
1414 | 1443 |
is->pictq_size++; |
1415 |
//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 |
|
1416 |
vp->timer_id= schedule_refresh(is, (int)(compute_frame_delay(vp->pts, is) * 1000 + 0.5)); |
|
1417 | 1444 |
SDL_UnlockMutex(is->pictq_mutex); |
1418 | 1445 |
} |
1419 | 1446 |
return 0; |
... | ... | |
1462 | 1489 |
SDL_LockMutex(is->pictq_mutex); |
1463 | 1490 |
//Make sure there are no long delay timers (ideally we should just flush the que but thats harder) |
1464 | 1491 |
for(i=0; i<VIDEO_PICTURE_QUEUE_SIZE; i++){ |
1465 |
if(is->pictq[i].timer_id){ |
|
1466 |
if(SDL_RemoveTimer(is->pictq[i].timer_id)){ |
|
1467 |
is->pictq[i].timer_id=0; |
|
1468 |
schedule_refresh(is, 1); |
|
1469 |
} |
|
1470 |
} |
|
1492 |
is->pictq[i].target_clock= 0; |
|
1471 | 1493 |
} |
1472 | 1494 |
while (is->pictq_size && !is->videoq.abort_request) { |
1473 | 1495 |
SDL_CondWait(is->pictq_cond, is->pictq_mutex); |
... | ... | |
1480 | 1502 |
is->frame_last_pts= AV_NOPTS_VALUE; |
1481 | 1503 |
is->frame_last_delay = 0; |
1482 | 1504 |
is->frame_timer = (double)av_gettime() / 1000000.0; |
1483 |
|
|
1505 |
is->skip_frames= 1; |
|
1506 |
is->skip_frames_index= 0; |
|
1484 | 1507 |
return 0; |
1485 | 1508 |
} |
1486 | 1509 |
|
... | ... | |
1514 | 1537 |
|
1515 | 1538 |
// if (len1 < 0) |
1516 | 1539 |
// break; |
1517 |
if (got_picture) |
|
1518 |
return 1; |
|
1540 |
if (got_picture){ |
|
1541 |
is->skip_frames_index += 1; |
|
1542 |
if(is->skip_frames_index >= is->skip_frames){ |
|
1543 |
is->skip_frames_index -= FFMAX(is->skip_frames, 1.0); |
|
1544 |
return 1; |
|
1545 |
} |
|
1546 |
|
|
1547 |
} |
|
1519 | 1548 |
return 0; |
1520 | 1549 |
} |
1521 | 1550 |
|
... | ... | |
2364 | 2393 |
if (st_index[CODEC_TYPE_VIDEO] >= 0) { |
2365 | 2394 |
ret= stream_component_open(is, st_index[CODEC_TYPE_VIDEO]); |
2366 | 2395 |
} |
2396 |
is->refresh_tid = SDL_CreateThread(refresh_thread, is); |
|
2367 | 2397 |
if(ret<0) { |
2368 |
/* add the refresh timer to draw the picture */ |
|
2369 |
schedule_refresh(is, 40); |
|
2370 |
|
|
2371 | 2398 |
if (!display_disable) |
2372 | 2399 |
is->show_audio = 2; |
2373 | 2400 |
} |
... | ... | |
2539 | 2566 |
/* XXX: use a special url_shutdown call to abort parse cleanly */ |
2540 | 2567 |
is->abort_request = 1; |
2541 | 2568 |
SDL_WaitThread(is->parse_tid, NULL); |
2569 |
SDL_WaitThread(is->refresh_tid, NULL); |
|
2542 | 2570 |
|
2543 | 2571 |
/* free all pictures */ |
2544 | 2572 |
for(i=0;i<VIDEO_PICTURE_QUEUE_SIZE; i++) { |
... | ... | |
2805 | 2833 |
break; |
2806 | 2834 |
case FF_REFRESH_EVENT: |
2807 | 2835 |
video_refresh_timer(event.user.data1); |
2836 |
cur_stream->refresh=0; |
|
2808 | 2837 |
break; |
2809 | 2838 |
default: |
2810 | 2839 |
break; |
... | ... | |
2926 | 2955 |
{ "sync", HAS_ARG | OPT_FUNC2 | OPT_EXPERT, {(void*)opt_sync}, "set audio-video sync. type (type=audio/video/ext)", "type" }, |
2927 | 2956 |
{ "threads", HAS_ARG | OPT_FUNC2 | OPT_EXPERT, {(void*)opt_thread_count}, "thread count", "count" }, |
2928 | 2957 |
{ "autoexit", OPT_BOOL | OPT_EXPERT, {(void*)&autoexit}, "exit at the end", "" }, |
2958 |
{ "framedrop", OPT_BOOL | OPT_EXPERT, {(void*)&framedrop}, "drop frames when cpu is too slow", "" }, |
|
2929 | 2959 |
#if CONFIG_AVFILTER |
2930 | 2960 |
{ "vfilters", OPT_STRING | HAS_ARG, {(void*)&vfilters}, "video filters", "filter list" }, |
2931 | 2961 |
#endif |
Also available in: Unified diff