Skip to content
Snippets Groups Projects
ffplay.c 73.7 KiB
Newer Older
  • Learn to ignore specific revisions
  •         } else if (pkt->stream_index == is->subtitle_stream) {
                packet_queue_put(&is->subtitleq, pkt);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else {
                av_free_packet(pkt);
            }
        }
        /* wait until the end */
        while (!is->abort_request) {
            SDL_Delay(100);
        }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     fail:
    
        /* disable interrupting */
        global_video_state = NULL;
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* close each stream */
        if (is->audio_stream >= 0)
            stream_component_close(is, is->audio_stream);
        if (is->video_stream >= 0)
            stream_component_close(is, is->video_stream);
    
        if (is->subtitle_stream >= 0)
            stream_component_close(is, is->subtitle_stream);
    
        if (is->ic) {
            av_close_input_file(is->ic);
            is->ic = NULL; /* safety */
        }
    
            event.type = FF_QUIT_EVENT;
            event.user.data1 = is;
            SDL_PushEvent(&event);
        }
    
    static VideoState *stream_open(const char *filename, AVInputFormat *iformat)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        VideoState *is;
    
        is = av_mallocz(sizeof(VideoState));
        if (!is)
            return NULL;
        pstrcpy(is->filename, sizeof(is->filename), filename);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        is->ytop = 0;
        is->xleft = 0;
    
        /* start video display */
        is->pictq_mutex = SDL_CreateMutex();
        is->pictq_cond = SDL_CreateCond();
    
        is->subpq_mutex = SDL_CreateMutex();
        is->subpq_cond = SDL_CreateCond();
    
    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);
    
    static 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
    {
        is_full_screen = !is_full_screen;
    
        if (!fs_screen_width) {
            /* use default SDL method */
            SDL_WM_ToggleFullScreen(screen);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        } else {
    
            /* use the recorded resolution */
    
            video_open(cur_stream);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        if (cur_stream)
            stream_pause(cur_stream);
    
    {
        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
    {
        if (cur_stream) {
            stream_close(cur_stream);
            cur_stream = NULL;
        }
        if (show_status)
            printf("\n");
        SDL_Quit();
        exit(0);
    }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        if (cur_stream) {
            cur_stream->show_audio = !cur_stream->show_audio;
        }
    }
    
    /* handle an event sent by the GUI */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        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;
    
                    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) {
    
                        if (seek_by_bytes) {
                            pos = url_ftell(&cur_stream->ic->pb);
                            if (cur_stream->ic->bit_rate)
                                incr *= cur_stream->ic->bit_rate / 60.0;
                            else
                                incr *= 180000.0;
                            pos += incr;
                            stream_seek(cur_stream, pos, incr);
                        } else {
                            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;
    
                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;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            case SDL_VIDEORESIZE:
                if (cur_stream) {
    
                    screen = SDL_SetVideoMode(event.resize.w, event.resize.h, 0,
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                                              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:
    
                video_open(event.user.data1);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                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);
        }
    }
    
    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);
    }
    
    
        av_log_set_level(99);
    
    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" },
    
        { "fs", OPT_BOOL, {(void*)&is_full_screen}, "force full screen" },
    
    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" },
    
        { "bytes", OPT_BOOL, {(void*)&seek_by_bytes}, "seek by bytes" },
    
    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" },
        { "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", "" },
    
        { "genpts", OPT_BOOL | OPT_EXPERT, {(void*)&genpts}, "generate pts", "" },
    
        { "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-2006 Fabrice Bellard, et al.\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;
    
    #if !defined(__MINGW32__) && !defined(CONFIG_DARWIN)
    
        flags |= SDL_INIT_EVENTTHREAD; /* Not supported on win32 or darwin */
    
    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) {
    
            const SDL_VideoInfo *vi = SDL_GetVideoInfo();
            fs_screen_width = vi->current_w;
            fs_screen_height = vi->current_h;
    
    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);
    
    
        av_init_packet(&flush_pkt);
        flush_pkt.data= "FLUSH";
    
    
        cur_stream = stream_open(input_filename, file_iformat);
    
        if(video_disable && !display_disable)
            video_open(cur_stream);
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        event_loop();
    
        /* never returns */
    
        return 0;
    }