]> git.sesse.net Git - ffmpeg/blobdiff - ffplay.c
spam
[ffmpeg] / ffplay.c
index 1dcf471519f662fdec8eb4636b3ab81f21138416..f74f0e4e4f051b3ea45ffd0805c09264fe71975d 100644 (file)
--- a/ffplay.c
+++ b/ffplay.c
@@ -46,7 +46,7 @@
 #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
 
@@ -92,6 +92,7 @@ typedef struct VideoState {
     int paused;
     int last_paused;
     int seek_req;
+    int seek_flags;
     int64_t seek_pos;
     AVFormatContext *ic;
     int dtg_active_format;
@@ -113,7 +114,7 @@ typedef struct VideoState {
     /* 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;
@@ -143,6 +144,9 @@ typedef struct VideoState {
     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;
@@ -165,6 +169,14 @@ static int display_disable;
 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;
@@ -192,6 +204,7 @@ static void packet_queue_flush(PacketQueue *q)
     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;
@@ -580,10 +593,11 @@ static double get_master_clock(VideoState *is)
 }
 
 /* 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 */
@@ -606,7 +620,7 @@ static void video_refresh_timer(void *opaque)
     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];
@@ -836,16 +850,6 @@ static int output_picture2(VideoState *is, AVFrame *src_frame, double pts1)
     
     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;
@@ -895,29 +899,24 @@ static int video_thread(void *arg)
         /* 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);
@@ -1032,9 +1031,11 @@ static int audio_decode_frame(VideoState *is, uint8_t *audio_buf, double *pts_pt
     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;
@@ -1160,9 +1161,21 @@ static int stream_component_open(VideoState *is, int stream_index)
     }
 
     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;
@@ -1311,6 +1324,7 @@ static int decode_thread(void *arg)
             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 */
@@ -1321,7 +1335,7 @@ static int decode_thread(void *arg)
         /* 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);
@@ -1398,29 +1412,38 @@ static int decode_thread(void *arg)
 #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);
@@ -1480,6 +1503,9 @@ static VideoState *stream_open(const char *filename, AVInputFormat *iformat)
     /* 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);
@@ -1511,6 +1537,8 @@ static void stream_close(VideoState *is)
     }
     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)
@@ -1582,6 +1610,17 @@ void toggle_pause(void)
 {
     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)
@@ -1607,7 +1646,7 @@ void toggle_audio_display(void)
 void event_loop(void)
 {
     SDL_Event event;
-    double incr, pos;
+    double incr, pos, frac;
 
     for(;;) {
         SDL_WaitEvent(&event);
@@ -1625,6 +1664,9 @@ void event_loop(void)
             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);
@@ -1651,13 +1693,31 @@ void event_loop(void)
                 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, 
@@ -1741,8 +1801,26 @@ void opt_seek(const char *arg)
     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
@@ -1756,10 +1834,17 @@ const OptionDef options[] = {
     { "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, },
 };
 
@@ -1782,6 +1867,7 @@ void show_help(void)
            "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);
 }