#define SDL_AUDIO_BUFFER_SIZE 1024
/* no AV sync correction is done if below the AV sync threshold */
-#define AV_SYNC_THRESHOLD 0.08
+#define AV_SYNC_THRESHOLD 0.01
/* no AV correction is done if too big error */
#define AV_NOSYNC_THRESHOLD 10.0
int paused;
int last_paused;
int seek_req;
+ int seek_flags;
int64_t seek_pos;
AVFormatContext *ic;
int dtg_active_format;
/* samples output by the codec. we reserve more space for avsync
compensation */
uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
- int audio_buf_size; /* in bytes */
+ unsigned int audio_buf_size; /* in bytes */
int audio_buf_index; /* in bytes */
AVPacket audio_pkt;
uint8_t *audio_pkt_data;
SDL_mutex *pictq_mutex;
SDL_cond *pictq_cond;
+ SDL_mutex *video_decoder_mutex;
+ SDL_mutex *audio_decoder_mutex;
+
// QETimer *video_timer;
char filename[1024];
int width, height, xleft, ytop;
static int show_status;
static int av_sync_type = AV_SYNC_AUDIO_MASTER;
static int64_t start_time = AV_NOPTS_VALUE;
+static int debug = 0;
+static int debug_mv = 0;
+static int step = 0;
+static int thread_count = 1;
+static int workaround_bugs = 1;
+static int fast = 0;
+static int lowres = 0;
+static int idct = FF_IDCT_AUTO;
/* current context */
static int is_full_screen;
for(pkt = q->first_pkt; pkt != NULL; pkt = pkt1) {
pkt1 = pkt->next;
av_free_packet(&pkt->pkt);
+ av_freep(&pkt);
}
q->last_pkt = NULL;
q->first_pkt = NULL;
}
/* seek in the stream */
-static void stream_seek(VideoState *is, int64_t pos)
+static void stream_seek(VideoState *is, int64_t pos, int rel)
{
is->seek_pos = pos;
is->seek_req = 1;
+ is->seek_flags = rel < 0 ? AVSEEK_FLAG_BACKWARD : 0;
}
/* pause or resume the video */
if (is->video_st) {
if (is->pictq_size == 0) {
/* if no picture, need to wait */
- schedule_refresh(is, 40);
+ schedule_refresh(is, 1);
} else {
/* dequeue the picture */
vp = &is->pictq[is->pictq_rindex];
pts = pts1;
- /* if B frames are present, and if the current picture is a I
- or P frame, we use the last pts */
- if (is->video_st->codec.has_b_frames &&
- src_frame->pict_type != FF_B_TYPE) {
- /* use last pts */
- pts = is->video_last_P_pts;
- /* get the pts for the next I or P frame if present */
- is->video_last_P_pts = pts1;
- }
-
if (pts != 0) {
/* update video clock with pts, if present */
is->video_clock = pts;
/* NOTE: ipts is the PTS of the _first_ picture beginning in
this packet, if any */
pts = 0;
- if (pkt->pts != AV_NOPTS_VALUE)
- pts = (double)pkt->pts / AV_TIME_BASE;
-
- if (is->video_st->codec.codec_id == CODEC_ID_RAWVIDEO) {
- avpicture_fill((AVPicture *)frame, pkt->data,
- is->video_st->codec.pix_fmt,
- is->video_st->codec.width,
- is->video_st->codec.height);
- frame->pict_type = FF_I_TYPE;
- if (output_picture2(is, frame, pts) < 0)
- goto the_end;
- } else {
+ if (pkt->dts != AV_NOPTS_VALUE)
+ pts = (double)pkt->dts / AV_TIME_BASE;
+
+ SDL_LockMutex(is->video_decoder_mutex);
len1 = avcodec_decode_video(&is->video_st->codec,
frame, &got_picture,
pkt->data, pkt->size);
- if (len1 < 0)
- break;
+ SDL_UnlockMutex(is->video_decoder_mutex);
+// if (len1 < 0)
+// break;
if (got_picture) {
if (output_picture2(is, frame, pts) < 0)
goto the_end;
}
- }
av_free_packet(pkt);
+ if (step)
+ if (cur_stream)
+ stream_pause(cur_stream);
}
the_end:
av_free(frame);
for(;;) {
/* NOTE: the audio packet can contain several frames */
while (is->audio_pkt_size > 0) {
+ SDL_LockMutex(is->audio_decoder_mutex);
len1 = avcodec_decode_audio(&is->audio_st->codec,
(int16_t *)audio_buf, &data_size,
is->audio_pkt_data, is->audio_pkt_size);
+ SDL_UnlockMutex(is->audio_decoder_mutex);
if (len1 < 0) {
/* if error, we skip the frame */
is->audio_pkt_size = 0;
}
codec = avcodec_find_decoder(enc->codec_id);
+ enc->debug_mv = debug_mv;
+ enc->debug = debug;
+ enc->workaround_bugs = workaround_bugs;
+ enc->lowres = lowres;
+ if(lowres) enc->flags |= CODEC_FLAG_EMU_EDGE;
+ enc->idct_algo= idct;
+ if(fast) enc->flags2 |= CODEC_FLAG2_FAST;
if (!codec ||
avcodec_open(enc, codec) < 0)
return -1;
+#if defined(HAVE_THREADS)
+ if(thread_count>1)
+ avcodec_thread_init(enc, thread_count);
+#endif
+ enc->thread_count= thread_count;
switch(enc->codec_type) {
case CODEC_TYPE_AUDIO:
is->audio_stream = stream_index;
ret = -1;
goto fail;
}
+ ic->pb.eof_reached= 0; //FIXME hack, ffplay maybe shouldnt use url_feof() to test for the end
}
/* if seeking requested, we execute it */
/* add the stream start time */
if (ic->start_time != AV_NOPTS_VALUE)
timestamp += ic->start_time;
- ret = av_seek_frame(ic, -1, timestamp);
+ ret = av_seek_frame(ic, -1, timestamp, AVSEEK_FLAG_BACKWARD);
if (ret < 0) {
fprintf(stderr, "%s: could not seek to position %0.3f\n",
is->filename, (double)timestamp / AV_TIME_BASE);
#endif
if (is->seek_req) {
/* XXX: must lock decoder threads */
- if (is->audio_stream >= 0) {
- packet_queue_flush(&is->audioq);
- }
- if (is->video_stream >= 0) {
- packet_queue_flush(&is->videoq);
- }
- ret = av_seek_frame(is->ic, -1, is->seek_pos);
+ ret = av_seek_frame(is->ic, -1, is->seek_pos, is->seek_flags);
if (ret < 0) {
fprintf(stderr, "%s: error while seeking\n", is->ic->filename);
+ }else{
+ if (is->audio_stream >= 0) {
+ packet_queue_flush(&is->audioq);
+ }
+ if (is->video_stream >= 0) {
+ packet_queue_flush(&is->videoq);
+ SDL_LockMutex(is->video_decoder_mutex);
+ avcodec_flush_buffers(&ic->streams[video_index]->codec);
+ SDL_UnlockMutex(is->video_decoder_mutex);
+ }
}
is->seek_req = 0;
}
/* if the queue are full, no need to read more */
if (is->audioq.size > MAX_AUDIOQ_SIZE ||
- is->videoq.size > MAX_VIDEOQ_SIZE) {
+ is->videoq.size > MAX_VIDEOQ_SIZE ||
+ url_feof(&ic->pb)) {
/* wait 10 ms */
SDL_Delay(10);
continue;
}
ret = av_read_frame(ic, pkt);
if (ret < 0) {
- break;
+ if (url_feof(&ic->pb) && url_ferror(&ic->pb) == 0) {
+ SDL_Delay(100); /* wait for user event */
+ continue;
+ } else
+ break;
}
if (pkt->stream_index == is->audio_stream) {
packet_queue_put(&is->audioq, pkt);
/* start video display */
is->pictq_mutex = SDL_CreateMutex();
is->pictq_cond = SDL_CreateCond();
+
+ is->audio_decoder_mutex = SDL_CreateMutex();
+ is->video_decoder_mutex = SDL_CreateMutex();
/* add the refresh timer to draw the picture */
schedule_refresh(is, 40);
}
SDL_DestroyMutex(is->pictq_mutex);
SDL_DestroyCond(is->pictq_cond);
+ SDL_DestroyMutex(is->audio_decoder_mutex);
+ SDL_DestroyMutex(is->video_decoder_mutex);
}
void stream_cycle_channel(VideoState *is, int codec_type)
{
if (cur_stream)
stream_pause(cur_stream);
+ step = 0;
+}
+
+void step_to_next_frame(void)
+{
+ if (cur_stream) {
+ if (cur_stream->paused)
+ cur_stream->paused=0;
+ cur_stream->video_current_pts = get_video_clock(cur_stream);
+ }
+ step = 1;
}
void do_exit(void)
void event_loop(void)
{
SDL_Event event;
- double incr, pos;
+ double incr, pos, frac;
for(;;) {
SDL_WaitEvent(&event);
case SDLK_SPACE:
toggle_pause();
break;
+ case SDLK_s: //S: Step to next frame
+ step_to_next_frame();
+ break;
case SDLK_a:
if (cur_stream)
stream_cycle_channel(cur_stream, CODEC_TYPE_AUDIO);
if (cur_stream) {
pos = get_master_clock(cur_stream);
pos += incr;
- stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE));
+ stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE), incr);
}
break;
default:
break;
}
break;
+ case SDL_MOUSEBUTTONDOWN:
+ if (cur_stream) {
+ 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);
+ stream_seek(cur_stream, (int64_t)(cur_stream->ic->start_time+frac*cur_stream->ic->duration), 0);
+ }
+ break;
case SDL_VIDEORESIZE:
if (cur_stream) {
screen = SDL_SetVideoMode(event.resize.w, event.resize.h, 0,
start_time = parse_date(arg, 1);
}
+static void opt_debug(const char *arg)
+{
+ debug = atoi(arg);
+}
+
+static void opt_vismv(const char *arg)
+{
+ debug_mv = atoi(arg);
+}
+
+static void opt_thread_count(const char *arg)
+{
+ thread_count= atoi(arg);
+#if !defined(HAVE_THREADS)
+ fprintf(stderr, "Warning: not compiled with thread support, using thread emulation\n");
+#endif
+}
+
const OptionDef options[] = {
- { "h", 0, {(void*)show_help}, "show help" },
+ { "h", 0, {(void*)show_help}, "show help" },
{ "x", HAS_ARG, {(void*)opt_width}, "force displayed width", "width" },
{ "y", HAS_ARG, {(void*)opt_height}, "force displayed height", "height" },
#if 0
{ "f", HAS_ARG, {(void*)opt_format}, "force format", "fmt" },
{ "img", HAS_ARG, {(void*)opt_image_format}, "force image format", "img_fmt" },
{ "stats", OPT_BOOL | OPT_EXPERT, {(void*)&show_status}, "show status", "" },
+ { "debug", HAS_ARG | OPT_EXPERT, {(void*)opt_debug}, "print specific debug info", "" },
+ { "bug", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&workaround_bugs}, "workaround bugs", "" },
+ { "vismv", HAS_ARG | OPT_EXPERT, {(void*)opt_vismv}, "visualize motion vectors", "" },
+ { "fast", OPT_BOOL | OPT_EXPERT, {(void*)&fast}, "non spec compliant optimizations", "" },
+ { "lowres", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&lowres}, "", "" },
+ { "idct", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&idct}, "set idct algo", "algo" },
#ifdef CONFIG_NETWORK
{ "rtp_tcp", OPT_EXPERT, {(void*)&opt_rtp_tcp}, "force RTP/TCP protocol usage", "" },
#endif
- { "sync", HAS_ARG | OPT_EXPERT, {(void*)&opt_sync}, "set audio-video sync. type (type=audio/video/ext)", "type" },
+ { "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" },
{ NULL, },
};
"w show audio waves\n"
"left/right seek backward/forward 10 seconds\n"
"down/up seek backward/forward 1 minute\n"
+ "mouse click seek to percentage in file corresponding to fraction of width\n"
);
exit(1);
}