Skip to content
Snippets Groups Projects
ffplay.c 55.5 KiB
Newer Older
  • Learn to ignore specific revisions
  •                         
                            /* add samples */
                            nb = (samples_size - wanted_size);
                            samples_end = (uint8_t *)samples + samples_size - n;
                            q = samples_end + n;
                            while (nb > 0) {
                                memcpy(q, samples_end, n);
                                q += n;
                                nb -= n;
                            }
                            samples_size = wanted_size;
                        }
                    }
    #if 0
                    printf("diff=%f adiff=%f sample_diff=%d apts=%0.3f vpts=%0.3f %f\n", 
                           diff, avg_diff, samples_size - samples_size1, 
                           is->audio_clock, is->video_clock, is->audio_diff_threshold);
    #endif
    
            } else {
                /* too big difference : may be initial PTS errors, so
                   reset A-V filter */
                is->audio_diff_avg_count = 0;
                is->audio_diff_cum = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            }
        }
    
        return samples_size;
    }
    
    /* decode one audio frame and returns its uncompressed size */
    static int audio_decode_frame(VideoState *is, uint8_t *audio_buf, double *pts_ptr)
    {
        AVPacket *pkt = &is->audio_pkt;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int n, len1, data_size;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        double pts;
    
        for(;;) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* NOTE: the audio packet can contain several frames */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            while (is->audio_pkt_size > 0) {
                len1 = avcodec_decode_audio(&is->audio_st->codec, 
                                            (int16_t *)audio_buf, &data_size, 
                                            is->audio_pkt_data, is->audio_pkt_size);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                if (len1 < 0) {
                    /* if error, we skip the frame */
                    is->audio_pkt_size = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                is->audio_pkt_data += len1;
                is->audio_pkt_size -= len1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                if (data_size <= 0)
                    continue;
                /* if no pts, then compute it */
                pts = is->audio_clock;
                *pts_ptr = pts;
                n = 2 * is->audio_st->codec.channels;
                is->audio_clock += (double)data_size / 
                    (double)(n * is->audio_st->codec.sample_rate);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                {
                    static double last_clock;
                    printf("audio: delay=%0.3f clock=%0.3f pts=%0.3f\n",
                           is->audio_clock - last_clock,
                           is->audio_clock, pts);
                    last_clock = is->audio_clock;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    #endif
                return data_size;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* free the current packet */
            if (pkt->data)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                av_free_packet(pkt);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            
            if (is->paused || is->audioq.abort_request) {
                return -1;
            }
            
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* read next packet */
            if (packet_queue_get(&is->audioq, pkt, 1) < 0)
                return -1;
            is->audio_pkt_data = pkt->data;
            is->audio_pkt_size = pkt->size;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            
            /* if update the audio clock with the pts */
            if (pkt->pts != AV_NOPTS_VALUE) {
                is->audio_clock = (double)pkt->pts / AV_TIME_BASE;
            }
    
    /* get the current audio output buffer size, in samples. With SDL, we
       cannot have a precise information */
    static int audio_write_get_buf_size(VideoState *is)
    
        return is->audio_hw_buf_size - is->audio_buf_index;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    }
    
    
    /* prepare a new audio buffer */
    void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
    {
        VideoState *is = opaque;
        int audio_size, len1;
        double pts;
    
        audio_callback_time = av_gettime();
        
        while (len > 0) {
            if (is->audio_buf_index >= is->audio_buf_size) {
               audio_size = audio_decode_frame(is, is->audio_buf, &pts);
               if (audio_size < 0) {
                    /* if error, just output silence */
                   is->audio_buf_size = 1024;
                   memset(is->audio_buf, 0, is->audio_buf_size);
               } else {
                   if (is->show_audio)
                       update_sample_display(is, (int16_t *)is->audio_buf, audio_size);
                   audio_size = synchronize_audio(is, (int16_t *)is->audio_buf, audio_size, 
                                                  pts);
                   is->audio_buf_size = audio_size;
               }
               is->audio_buf_index = 0;
            }
            len1 = is->audio_buf_size - is->audio_buf_index;
            if (len1 > len)
                len1 = len;
            memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);
            len -= len1;
            stream += len1;
            is->audio_buf_index += len1;
        }
    }
    
    
    /* open a given stream. Return 0 if OK */
    static int stream_component_open(VideoState *is, int stream_index)
    {
        AVFormatContext *ic = is->ic;
        AVCodecContext *enc;
        AVCodec *codec;
        SDL_AudioSpec wanted_spec, spec;
    
        if (stream_index < 0 || stream_index >= ic->nb_streams)
            return -1;
        enc = &ic->streams[stream_index]->codec;
        
        /* prepare audio output */
        if (enc->codec_type == CODEC_TYPE_AUDIO) {
            wanted_spec.freq = enc->sample_rate;
            wanted_spec.format = AUDIO_S16SYS;
    
            /* hack for AC3. XXX: suppress that */
            if (enc->channels > 2)
                enc->channels = 2;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            wanted_spec.channels = enc->channels;
            wanted_spec.silence = 0;
    
            wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            wanted_spec.callback = sdl_audio_callback;
            wanted_spec.userdata = is;
    
            if (SDL_OpenAudio(&wanted_spec, &spec) < 0) {
                fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                return -1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            is->audio_hw_buf_size = spec.size;
        }
    
        codec = avcodec_find_decoder(enc->codec_id);
        if (!codec ||
            avcodec_open(enc, codec) < 0)
            return -1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        case CODEC_TYPE_AUDIO:
            is->audio_stream = stream_index;
            is->audio_st = ic->streams[stream_index];
            is->audio_buf_size = 0;
            is->audio_buf_index = 0;
    
    
            /* init averaging filter */
            is->audio_diff_avg_coef = exp(log(0.01) / AUDIO_DIFF_AVG_NB);
            is->audio_diff_avg_count = 0;
            /* since we do not have a precise anough audio fifo fullness,
               we correct audio sync only if larger than this threshold */
            is->audio_diff_threshold = 2.0 * SDL_AUDIO_BUFFER_SIZE / enc->sample_rate;
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
            packet_queue_init(&is->audioq);
    	SDL_PauseAudio(0);
            break;
        case CODEC_TYPE_VIDEO:
            is->video_stream = stream_index;
            is->video_st = ic->streams[stream_index];
    
    
            is->frame_last_delay = 40e-3;
            is->frame_timer = (double)av_gettime() / 1000000.0;
            is->video_current_pts_time = av_gettime();
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            packet_queue_init(&is->videoq);
            is->video_tid = SDL_CreateThread(video_thread, is);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            break;
        default:
            break;
        }
        return 0;
    }
    
    static void stream_component_close(VideoState *is, int stream_index)
    {
        AVFormatContext *ic = is->ic;
        AVCodecContext *enc;
        
        enc = &ic->streams[stream_index]->codec;
    
        switch(enc->codec_type) {
        case CODEC_TYPE_AUDIO:
            packet_queue_abort(&is->audioq);
    
            SDL_CloseAudio();
    
            packet_queue_end(&is->audioq);
            break;
        case CODEC_TYPE_VIDEO:
            packet_queue_abort(&is->videoq);
    
            /* note: we also signal this mutex to make sure we deblock the
               video thread in all cases */
            SDL_LockMutex(is->pictq_mutex);
            SDL_CondSignal(is->pictq_cond);
            SDL_UnlockMutex(is->pictq_mutex);
    
            SDL_WaitThread(is->video_tid, NULL);
    
            packet_queue_end(&is->videoq);
            break;
        default:
            break;
        }
    
        avcodec_close(enc);
        switch(enc->codec_type) {
        case CODEC_TYPE_AUDIO:
            is->audio_st = NULL;
            is->audio_stream = -1;
            break;
        case CODEC_TYPE_VIDEO:
            is->video_st = NULL;
            is->video_stream = -1;
            break;
        default:
            break;
        }
    }
    
    
    void dump_stream_info(AVFormatContext *s)
    {
        if (s->track != 0)
            fprintf(stderr, "Track: %d\n", s->track);
        if (s->title[0] != '\0')
            fprintf(stderr, "Title: %s\n", s->title);
        if (s->author[0] != '\0')
            fprintf(stderr, "Author: %s\n", s->author);
        if (s->album[0] != '\0')
            fprintf(stderr, "Album: %s\n", s->album);
        if (s->year != 0)
            fprintf(stderr, "Year: %d\n", s->year);
        if (s->genre[0] != '\0')
            fprintf(stderr, "Genre: %s\n", s->genre);
    }
    
    
    /* since we have only one decoding thread, we can use a global
       variable instead of a thread local variable */
    static VideoState *global_video_state;
    
    static int decode_interrupt_cb(void)
    {
        return (global_video_state && global_video_state->abort_request);
    }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    /* this thread gets the stream from the disk or the network */
    static int decode_thread(void *arg)
    {
        VideoState *is = arg;
        AVFormatContext *ic;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int err, i, ret, video_index, audio_index, use_play;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        AVPacket pkt1, *pkt = &pkt1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        video_index = -1;
        audio_index = -1;
        is->video_stream = -1;
        is->audio_stream = -1;
    
    
        global_video_state = is;
        url_set_interrupt_cb(decode_interrupt_cb);
    
    
        memset(ap, 0, sizeof(*ap));
        ap->image_format = image_format;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        ap->initial_pause = 1; /* we force a pause when starting an RTSP
                                  stream */
        
    
        err = av_open_input_file(&ic, is->filename, is->iformat, 0, ap);
    
        if (err < 0) {
            print_error(is->filename, err);
            ret = -1;
            goto fail;
        }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        is->ic = ic;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    #ifdef CONFIG_NETWORK
        use_play = (ic->iformat == &rtsp_demux);
    #else
        use_play = 0;
    #endif
        if (!use_play) {
            err = av_find_stream_info(ic);
            if (err < 0) {
                fprintf(stderr, "%s: could not find codec parameters\n", is->filename);
                ret = -1;
                goto fail;
            }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        /* if seeking requested, we execute it */
        if (start_time != AV_NOPTS_VALUE) {
            int64_t timestamp;
    
            timestamp = start_time;
            /* add the stream start time */
            if (ic->start_time != AV_NOPTS_VALUE)
                timestamp += ic->start_time;
            ret = av_seek_frame(ic, -1, timestamp);
            if (ret < 0) {
                fprintf(stderr, "%s: could not seek to position %0.3f\n", 
                        is->filename, (double)timestamp / AV_TIME_BASE);
            }
        }
    
        /* now we can begin to play (RTSP stream only) */
        av_read_play(ic);
    
        if (use_play) {
            err = av_find_stream_info(ic);
            if (err < 0) {
                fprintf(stderr, "%s: could not find codec parameters\n", is->filename);
                ret = -1;
                goto fail;
            }
        }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        for(i = 0; i < ic->nb_streams; i++) {
            AVCodecContext *enc = &ic->streams[i]->codec;
            switch(enc->codec_type) {
            case CODEC_TYPE_AUDIO:
                if (audio_index < 0 && !audio_disable)
                    audio_index = i;
                break;
            case CODEC_TYPE_VIDEO:
                if (video_index < 0 && !video_disable)
                    video_index = i;
                break;
            default:
                break;
            }
        }
        if (show_status) {
            dump_format(ic, 0, is->filename, 0);
    
            dump_stream_info(ic);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
        /* open the streams */
        if (audio_index >= 0) {
            stream_component_open(is, audio_index);
        }
    
        if (video_index >= 0) {
            stream_component_open(is, video_index);
        } else {
            if (!display_disable)
                is->show_audio = 1;
        }
    
        if (is->video_stream < 0 && is->audio_stream < 0) {
    
            fprintf(stderr, "%s: could not open codecs\n", is->filename);
            ret = -1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            goto fail;
        }
    
        for(;;) {
            if (is->abort_request)
                break;
    
            if (is->paused != is->last_paused) {
                is->last_paused = is->paused;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                if (is->paused)
                    av_read_pause(ic);
                else
                    av_read_play(ic);
    
            }
            if (is->paused && ic->iformat == &rtsp_demux) {
                /* wait 10 ms to avoid trying to get another packet */
                /* XXX: horrible */
                SDL_Delay(10);
                continue;
            }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (is->seek_req) {
                /* XXX: must lock decoder threads */
                ret = av_seek_frame(is->ic, -1, is->seek_pos);
                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);
    
                        avcodec_flush_buffers(&ic->streams[video_index]->codec);
    
    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) {
                /* 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) {
                break;
            }
            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 {
                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->ic) {
            av_close_input_file(is->ic);
            is->ic = NULL; /* safety */
        }
    
        if (ret != 0) {
            SDL_Event event;
            
            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
        if (screen) {
            is->width = screen->w;
            is->height = screen->h;
        }
        is->ytop = 0;
        is->xleft = 0;
    
        /* start video display */
        is->pictq_mutex = SDL_CreateMutex();
        is->pictq_cond = SDL_CreateCond();
    
        /* 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);
    }
    
    
    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
            start_index = is->audio_stream;
        if (start_index < 0)
            return;
        stream_index = start_index;
        for(;;) {
            if (++stream_index >= is->ic->nb_streams)
                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;
                    break;
                case CODEC_TYPE_VIDEO:
                    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_w:
    
    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));
                    }
                    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));
    	    }
    	    break;
    
    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);
    }
        
    
    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", "" },
    
        { "vismv", HAS_ARG | OPT_EXPERT, {(void*)opt_vismv}, "visualize motion vectors", "" },
    
        { "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" },
    
    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"
               "w                   show audio waves\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();
    
        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;
    }