Skip to content
Snippets Groups Projects
ffplay.c 77.1 KiB
Newer Older
  • Learn to ignore specific revisions
  •             int64_t seek_target= is->seek_pos;
    
                if     (is->   video_stream >= 0) stream_index= is->   video_stream;
                else if(is->   audio_stream >= 0) stream_index= is->   audio_stream;
                else if(is->subtitle_stream >= 0) stream_index= is->subtitle_stream;
    
                if(stream_index>=0){
                    seek_target= av_rescale_q(seek_target, AV_TIME_BASE_Q, ic->streams[stream_index]->time_base);
                }
    
                ret = av_seek_frame(is->ic, stream_index, seek_target, is->seek_flags);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                if (ret < 0) {
                    fprintf(stderr, "%s: error while seeking\n", is->ic->filename);
    
                }else{
                    if (is->audio_stream >= 0) {
                        packet_queue_flush(&is->audioq);
    
                        packet_queue_put(&is->audioq, &flush_pkt);
    
                    if (is->subtitle_stream >= 0) {
                        packet_queue_flush(&is->subtitleq);
    
                        packet_queue_put(&is->subtitleq, &flush_pkt);
    
                    if (is->video_stream >= 0) {
                        packet_queue_flush(&is->videoq);
    
                        packet_queue_put(&is->videoq, &flush_pkt);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                }
                is->seek_req = 0;
            }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* if the queue are full, no need to read more */
            if (is->audioq.size > MAX_AUDIOQ_SIZE ||
    
                is->videoq.size > MAX_VIDEOQ_SIZE ||
                is->subtitleq.size > MAX_SUBTITLEQ_SIZE ||
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                /* wait 10 ms */
                SDL_Delay(10);
                continue;
            }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            ret = av_read_frame(ic, pkt);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (ret < 0) {
    
                if (url_ferror(ic->pb) == 0) {
    
                    SDL_Delay(100); /* wait for user event */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            }
            if (pkt->stream_index == is->audio_stream) {
                packet_queue_put(&is->audioq, pkt);
            } else if (pkt->stream_index == is->video_stream) {
                packet_queue_put(&is->videoq, pkt);
    
            } 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;
    
        av_strlcpy(is->filename, filename, sizeof(is->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 */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        if (cur_stream)
            stream_pause(cur_stream);
    
            /* if the stream is paused unpause it, then step */
    
    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);
    
                    screen_width = cur_stream->width = event.resize.w;
                    screen_height= cur_stream->height= event.resize.h;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                }
                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;
            }
        }
    }
    
    
    static void opt_frame_size(const char *arg)
    {
    
        if (av_parse_video_frame_size(&frame_width, &frame_height, arg) < 0) {
    
            fprintf(stderr, "Incorrect frame size\n");
            exit(1);
        }
        if ((frame_width % 2) != 0 || (frame_height % 2) != 0) {
            fprintf(stderr, "Frame size must be a multiple of 2\n");
            exit(1);
        }
    }
    
    
    static int opt_width(const char *opt, const char *arg)
    
        screen_width = parse_number_or_die(opt, arg, OPT_INT64, 1, INT_MAX);
        return 0;
    
    static int opt_height(const char *opt, const char *arg)
    
        screen_height = parse_number_or_die(opt, arg, OPT_INT64, 1, INT_MAX);
        return 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    }
    
    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_frame_pix_fmt(const char *arg)
    {
        frame_pix_fmt = avcodec_get_pix_fmt(arg);
    }
    
    
    static 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;
    
    static int opt_seek(const char *opt, const char *arg)
    
        start_time = parse_time_or_die(opt, arg, 1);
        return 0;
    
    static int opt_debug(const char *opt, const char *arg)
    
        av_log_set_level(99);
    
        debug = parse_number_or_die(opt, arg, OPT_INT64, 0, INT_MAX);
        return 0;
    
    static int opt_vismv(const char *opt, const char *arg)
    
        debug_mv = parse_number_or_die(opt, arg, OPT_INT64, INT_MIN, INT_MAX);
        return 0;
    
    static int opt_thread_count(const char *opt, const char *arg)
    
        thread_count= parse_number_or_die(opt, arg, OPT_INT64, 0, INT_MAX);
    
        fprintf(stderr, "Warning: not compiled with thread support, using thread emulation\n");
    #endif
    
        { "h", OPT_EXIT, {(void*)show_help}, "show help" },
        { "version", OPT_EXIT, {(void*)show_version}, "show version" },
    
    Stefano Sabatini's avatar
    Stefano Sabatini committed
        { "L", OPT_EXIT, {(void*)show_license}, "show license" },
    
        { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
    
        { "x", HAS_ARG | OPT_FUNC2, {(void*)opt_width}, "force displayed width", "width" },
        { "y", HAS_ARG | OPT_FUNC2, {(void*)opt_height}, "force displayed height", "height" },
    
        { "s", HAS_ARG | OPT_VIDEO, {(void*)opt_frame_size}, "set frame size (WxH or abbreviation)", "size" },
    
        { "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" },
    
        { "ast", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&wanted_audio_stream}, "", "" },
    
        { "vst", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&wanted_video_stream}, "", "" },
    
        { "ss", HAS_ARG | OPT_FUNC2, {(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" },
    
        { "pix_fmt", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_frame_pix_fmt}, "set pixel format", "format" },
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        { "stats", OPT_BOOL | OPT_EXPERT, {(void*)&show_status}, "show status", "" },
    
        { "debug", HAS_ARG | OPT_FUNC2 | 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_FUNC2 | 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", "" },
    
        { "drp", OPT_BOOL |OPT_EXPERT, {(void*)&decoder_reorder_pts}, "let decoder reorder 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" },
    
        { "sync", HAS_ARG | OPT_EXPERT, {(void*)opt_sync}, "set audio-video sync. type (type=audio/video/ext)", "type" },
    
        { "threads", HAS_ARG | OPT_FUNC2 | OPT_EXPERT, {(void*)opt_thread_count}, "thread count", "count" },
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        { NULL, },
    };
    
    
        printf("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"
    
    static void opt_input_file(const char *filename)
    
            filename = "pipe:";
    
    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 */
    
    Luca Abeni's avatar
    Luca Abeni committed
        avcodec_register_all();
        avdevice_register_all();
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        av_register_all();
    
    
        show_banner(program_name, program_birth_year);
    
    
        parse_options(argc, argv, options, opt_input_file);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            show_help();
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        if (display_disable) {
            video_disable = 1;
        }
    
        flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
    
    #if !defined(__MINGW32__) && !defined(__APPLE__)
        flags |= SDL_INIT_EVENTTHREAD; /* Not supported on Windows or Mac OS X */
    
    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);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        event_loop();
    
        /* never returns */
    
        return 0;
    }