Skip to content
Snippets Groups Projects
ffplay.c 73.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • Fabrice Bellard's avatar
    Fabrice Bellard committed
        is->pictq_mutex = SDL_CreateMutex();
        is->pictq_cond = SDL_CreateCond();
    
        is->subpq_mutex = SDL_CreateMutex();
        is->subpq_cond = SDL_CreateCond();
        
        is->subtitle_decoder_mutex = SDL_CreateMutex();
    
        is->audio_decoder_mutex = SDL_CreateMutex();
        is->video_decoder_mutex = SDL_CreateMutex();
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        /* add the refresh timer to draw the picture */
        schedule_refresh(is, 40);
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        is->parse_tid = SDL_CreateThread(decode_thread, is);
        if (!is->parse_tid) {
            av_free(is);
            return NULL;
        }
        return is;
    }
    
    static void stream_close(VideoState *is)
    {
        VideoPicture *vp;
        int i;
        /* XXX: use a special url_shutdown call to abort parse cleanly */
        is->abort_request = 1;
        SDL_WaitThread(is->parse_tid, NULL);
    
        /* free all pictures */
        for(i=0;i<VIDEO_PICTURE_QUEUE_SIZE; i++) {
            vp = &is->pictq[i];
            if (vp->bmp) {
                SDL_FreeYUVOverlay(vp->bmp);
                vp->bmp = NULL;
            }
        }
        SDL_DestroyMutex(is->pictq_mutex);
        SDL_DestroyCond(is->pictq_cond);
    
        SDL_DestroyMutex(is->subpq_mutex);
        SDL_DestroyCond(is->subpq_cond);
        SDL_DestroyMutex(is->subtitle_decoder_mutex);
    
        SDL_DestroyMutex(is->audio_decoder_mutex);
        SDL_DestroyMutex(is->video_decoder_mutex);
    
    void stream_cycle_channel(VideoState *is, int codec_type)
    {
        AVFormatContext *ic = is->ic;
        int start_index, stream_index;
        AVStream *st;
    
        if (codec_type == CODEC_TYPE_VIDEO)
            start_index = is->video_stream;
    
        else if (codec_type == CODEC_TYPE_AUDIO)
    
        else
            start_index = is->subtitle_stream;
        if (start_index < (codec_type == CODEC_TYPE_SUBTITLE ? -1 : 0))
    
            return;
        stream_index = start_index;
        for(;;) {
            if (++stream_index >= is->ic->nb_streams)
    
            {
                if (codec_type == CODEC_TYPE_SUBTITLE)
                {
                    stream_index = -1;
                    goto the_end;
                } else
                    stream_index = 0;
            }
    
            if (stream_index == start_index)
                return;
            st = ic->streams[stream_index];
    
            if (st->codec->codec_type == codec_type) {
    
                /* check that parameters are OK */
                switch(codec_type) {
                case CODEC_TYPE_AUDIO:
    
                    if (st->codec->sample_rate != 0 &&
                        st->codec->channels != 0)
    
                    goto the_end;
                default:
                    break;
                }
            }
        }
     the_end:
        stream_component_close(is, start_index);
        stream_component_open(is, stream_index);
    }
    
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    void toggle_full_screen(void)
    {
        int w, h, flags;
        is_full_screen = !is_full_screen;
        if (!fs_screen_width) {
            /* use default SDL method */
            SDL_WM_ToggleFullScreen(screen);
        } else {
            /* use the recorded resolution */
            flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL;
            if (is_full_screen) {
                w = fs_screen_width;
                h = fs_screen_height;
                flags |= SDL_FULLSCREEN;
            } else {
                w = screen_width;
                h = screen_height;
                flags |= SDL_RESIZABLE;
            }
            screen = SDL_SetVideoMode(w, h, 0, flags);
            cur_stream->width = w;
            cur_stream->height = h;
        }
    }
    
    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;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    }
    
    void do_exit(void)
    {
        if (cur_stream) {
            stream_close(cur_stream);
            cur_stream = NULL;
        }
        if (show_status)
            printf("\n");
        SDL_Quit();
        exit(0);
    }
    
    void toggle_audio_display(void)
    {
        if (cur_stream) {
            cur_stream->show_audio = !cur_stream->show_audio;
        }
    }
    
    /* handle an event sent by the GUI */
    void event_loop(void)
    {
        SDL_Event event;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        for(;;) {
            SDL_WaitEvent(&event);
            switch(event.type) {
            case SDL_KEYDOWN:
                switch(event.key.keysym.sym) {
                case SDLK_ESCAPE:
                case SDLK_q:
                    do_exit();
                    break;
                case SDLK_f:
                    toggle_full_screen();
                    break;
                case SDLK_p:
                case SDLK_SPACE:
                    toggle_pause();
                    break;
    
                case SDLK_s: //S: Step to next frame
                    step_to_next_frame();
                    break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                case SDLK_a:
    
                    if (cur_stream) 
                        stream_cycle_channel(cur_stream, CODEC_TYPE_AUDIO);
                    break;
                case SDLK_v:
                    if (cur_stream) 
                        stream_cycle_channel(cur_stream, CODEC_TYPE_VIDEO);
                    break;
    
                case SDLK_t:
                    if (cur_stream) 
                        stream_cycle_channel(cur_stream, CODEC_TYPE_SUBTITLE);
                    break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    toggle_audio_display();
                    break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                case SDLK_LEFT:
                    incr = -10.0;
                    goto do_seek;
                case SDLK_RIGHT:
                    incr = 10.0;
                    goto do_seek;
                case SDLK_UP:
                    incr = 60.0;
                    goto do_seek;
                case SDLK_DOWN:
                    incr = -60.0;
                do_seek:
                    if (cur_stream) {
                        pos = get_master_clock(cur_stream);
                        pos += incr;
    
                        stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE), incr);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    }
                    break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                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);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            case SDL_VIDEORESIZE:
                if (cur_stream) {
                    screen = SDL_SetVideoMode(event.resize.w, event.resize.h, 0, 
                                              SDL_HWSURFACE|SDL_RESIZABLE|SDL_ASYNCBLIT|SDL_HWACCEL);
                    cur_stream->width = event.resize.w;
                    cur_stream->height = event.resize.h;
                }
                break;
            case SDL_QUIT:
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                do_exit();
                break;
            case FF_ALLOC_EVENT:
                alloc_picture(event.user.data1);
                break;
            case FF_REFRESH_EVENT:
                video_refresh_timer(event.user.data1);
                break;
            default:
                break;
            }
        }
    }
    
    void opt_width(const char *arg)
    {
        screen_width = atoi(arg);
    }
    
    void opt_height(const char *arg)
    {
        screen_height = atoi(arg);
    }
    
    static void opt_format(const char *arg)
    {
        file_iformat = av_find_input_format(arg);
        if (!file_iformat) {
            fprintf(stderr, "Unknown input format: %s\n", arg);
            exit(1);
        }
    }
    
    
    static void opt_image_format(const char *arg)
    {
        AVImageFormat *f;
        
        for(f = first_image_format; f != NULL; f = f->next) {
            if (!strcmp(arg, f->name))
                break;
        }
        if (!f) {
            fprintf(stderr, "Unknown image format: '%s'\n", arg);
            exit(1);
        }
        image_format = f;
    }
    
    
    void opt_rtp_tcp(void)
    {
        /* only tcp protocol */
        rtsp_default_protocols = (1 << RTSP_PROTOCOL_RTP_TCP);
    }
    
    void opt_sync(const char *arg)
    {
        if (!strcmp(arg, "audio"))
            av_sync_type = AV_SYNC_AUDIO_MASTER;
        else if (!strcmp(arg, "video"))
            av_sync_type = AV_SYNC_VIDEO_MASTER;
        else if (!strcmp(arg, "ext"))
            av_sync_type = AV_SYNC_EXTERNAL_CLOCK;
        else
            show_help();
    }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    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);
    
        fprintf(stderr, "Warning: not compiled with thread support, using thread emulation\n");
    #endif
    }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    const OptionDef options[] = {
    
        { "h", 0, {(void*)show_help}, "show help" },    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        { "x", HAS_ARG, {(void*)opt_width}, "force displayed width", "width" },
        { "y", HAS_ARG, {(void*)opt_height}, "force displayed height", "height" },
    
    #if 0
        /* disabled as SDL/X11 does not support it correctly on application launch */
        { "fs", OPT_BOOL, {(void*)&is_full_screen}, "force full screen" },
    #endif
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        { "an", OPT_BOOL, {(void*)&audio_disable}, "disable audio" },
        { "vn", OPT_BOOL, {(void*)&video_disable}, "disable video" },
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        { "ss", HAS_ARG, {(void*)&opt_seek}, "seek to a given position in seconds", "pos" },
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        { "nodisp", OPT_BOOL, {(void*)&display_disable}, "disable graphical display" },
        { "f", HAS_ARG, {(void*)opt_format}, "force format", "fmt" },
    
        { "img", HAS_ARG, {(void*)opt_image_format}, "force image format", "img_fmt" },
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        { "stats", OPT_BOOL | OPT_EXPERT, {(void*)&show_status}, "show status", "" },
    
        { "debug", HAS_ARG | OPT_EXPERT, {(void*)opt_debug}, "print specific debug info", "" },
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
        { "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}, "", "" },
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
        { "skiploop", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&skip_loop_filter}, "", "" },
        { "skipframe", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&skip_frame}, "", "" },
        { "skipidct", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&skip_idct}, "", "" },
    
        { "idct", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&idct}, "set idct algo",  "algo" },
    
        { "er", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&error_resilience}, "set error detection threshold (0-4)",  "threshold" },
        { "ec", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&error_concealment}, "set error concealment options",  "bit_mask" },
    
        { "rtp_tcp", OPT_EXPERT, {(void*)&opt_rtp_tcp}, "force RTP/TCP protocol usage", "" },
    
        { "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" },
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        { NULL, },
    };
    
    void show_help(void)
    {
    
        printf("ffplay version " FFMPEG_VERSION ", Copyright (c) 2003 Fabrice Bellard\n"
               "usage: ffplay [options] input_file\n"
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
               "Simple media player\n");
        printf("\n");
    
        show_help_options(options, "Main options:\n",
                          OPT_EXPERT, 0);
        show_help_options(options, "\nAdvanced options:\n",
                          OPT_EXPERT, OPT_EXPERT);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        printf("\nWhile playing:\n"
               "q, ESC              quit\n"
               "f                   toggle full screen\n"
               "p, SPC              pause\n"
    
               "a                   cycle audio channel\n"
               "v                   cycle video channel\n"
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
               "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"
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
               );
        exit(1);
    }
    
    void parse_arg_file(const char *filename)
    {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        input_filename = filename;
    }
    
    /* Called from the main */
    int main(int argc, char **argv)
    {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        
        /* register all codecs, demux and protocols */
        av_register_all();
    
    
        #ifdef CONFIG_OS2
          MorphToPM(); // Morph the VIO application to a PM one to be able to use Win* functions
     
          // Make stdout and stderr unbuffered
          setbuf( stdout, NULL );
          setbuf( stderr, NULL );
        #endif
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        parse_options(argc, argv, options);
    
        if (!input_filename)
            show_help();
    
        if (display_disable) {
            video_disable = 1;
        }
    
        flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
    #ifndef CONFIG_WIN32
        flags |= SDL_INIT_EVENTTHREAD; /* Not supported on win32 */
    #endif
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (SDL_Init (flags)) {
    
            fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            exit(1);
        }
    
        if (!display_disable) {
    #ifdef HAVE_X11
            /* save the screen resolution... SDL should allow full screen
               by resizing the window */
            {
                Display *dpy;
                dpy = XOpenDisplay(NULL);
                if (dpy) {
                    fs_screen_width = DisplayWidth(dpy, DefaultScreen(dpy));
                    fs_screen_height = DisplayHeight(dpy, DefaultScreen(dpy));
    		XCloseDisplay(dpy);
                }
            }
    #endif
    
            flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL;
            if (is_full_screen && fs_screen_width) {
                w = fs_screen_width;
                h = fs_screen_height;
                flags |= SDL_FULLSCREEN;
            } else {
                w = screen_width;
                h = screen_height;
                flags |= SDL_RESIZABLE;
            }
            screen = SDL_SetVideoMode(w, h, 0, flags);
            if (!screen) {
                fprintf(stderr, "SDL: could not set video mode - exiting\n");
                exit(1);
            }
            SDL_WM_SetCaption("FFplay", "FFplay");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
        SDL_EventState(SDL_ACTIVEEVENT, SDL_IGNORE);
        SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
        SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE);
        SDL_EventState(SDL_USEREVENT, SDL_IGNORE);
    
    
        cur_stream = stream_open(input_filename, file_iformat);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        event_loop();
    
        /* never returns */
    
        return 0;
    }