Skip to content
Snippets Groups Projects
avplay.c 97.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • Fabrice Bellard's avatar
    Fabrice Bellard committed
                           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);
    
            memset(pkt_temp, 0, sizeof(*pkt_temp));
    
    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 ((new_packet = packet_queue_get(&is->audioq, pkt, 1)) < 0)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                return -1;
    
            if (pkt->data == flush_pkt.data) {
    
            *pkt_temp = *pkt;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* if update the audio clock with the pts */
            if (pkt->pts != AV_NOPTS_VALUE) {
    
                is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    }
    
    /* prepare a new audio buffer */
    
    static void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        VideoState *is = opaque;
        int audio_size, len1;
        double pts;
    
        audio_callback_time = av_gettime();
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        while (len > 0) {
            if (is->audio_buf_index >= is->audio_buf_size) {
    
               audio_size = audio_decode_frame(is, &pts);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
               if (audio_size < 0) {
                    /* if error, just output silence */
    
                   is->audio_buf      = is->silence_buf;
                   is->audio_buf_size = sizeof(is->silence_buf);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
               } 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,
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                                                  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;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        AVCodec *codec;
        SDL_AudioSpec wanted_spec, spec;
    
        AVDictionary *opts;
        AVDictionaryEntry *t = NULL;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        if (stream_index < 0 || stream_index >= ic->nb_streams)
            return -1;
    
        avctx = ic->streams[stream_index]->codec;
    
        opts = filter_codec_opts(codec_opts, avctx->codec_id, ic, ic->streams[stream_index]);
    
        codec = avcodec_find_decoder(avctx->codec_id);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        avctx->debug_mv          = debug_mv;
        avctx->debug             = debug;
        avctx->workaround_bugs   = workaround_bugs;
        avctx->idct_algo         = idct;
        avctx->skip_frame        = skip_frame;
        avctx->skip_idct         = skip_idct;
        avctx->skip_loop_filter  = skip_loop_filter;
        avctx->error_concealment = error_concealment;
    
        if (fast)   avctx->flags2 |= CODEC_FLAG2_FAST;
    
        if (!av_dict_get(opts, "threads", NULL, 0))
            av_dict_set(&opts, "threads", "auto", 0);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (!codec ||
    
            avcodec_open2(avctx, codec, &opts) < 0)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            return -1;
    
        if ((t = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
            av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
            return AVERROR_OPTION_NOT_FOUND;
        }
    
        if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) {
    
            wanted_spec.freq = avctx->sample_rate;
    
            wanted_spec.format = AUDIO_S16SYS;
    
    
            if (!avctx->channel_layout)
                avctx->channel_layout = av_get_default_channel_layout(avctx->channels);
            if (!avctx->channel_layout) {
                fprintf(stderr, "unable to guess channel layout\n");
                return -1;
            }
            if (avctx->channels == 1)
                is->sdl_channel_layout = AV_CH_LAYOUT_MONO;
            else
                is->sdl_channel_layout = AV_CH_LAYOUT_STEREO;
            is->sdl_channels = av_get_channel_layout_nb_channels(is->sdl_channel_layout);
    
            wanted_spec.channels = is->sdl_channels;
    
            wanted_spec.silence = 0;
            wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
            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());
                return -1;
            }
            is->audio_hw_buf_size = spec.size;
    
            is->sdl_sample_fmt          = AV_SAMPLE_FMT_S16;
            is->resample_sample_fmt     = is->sdl_sample_fmt;
            is->resample_channel_layout = is->sdl_channel_layout;
    
        ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        switch (avctx->codec_type) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            is->audio_stream = stream_index;
            is->audio_st = ic->streams[stream_index];
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            is->audio_buf_size  = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            is->audio_buf_index = 0;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            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 / avctx->sample_rate;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
            packet_queue_init(&is->audioq);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            is->video_stream = stream_index;
            is->video_st = ic->streams[stream_index];
    
            packet_queue_init(&is->videoq);
            is->video_tid = SDL_CreateThread(video_thread, is);
            break;
    
            is->subtitle_stream = stream_index;
            is->subtitle_st = ic->streams[stream_index];
            packet_queue_init(&is->subtitleq);
    
            is->subtitle_tid = SDL_CreateThread(subtitle_thread, is);
            break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        default:
            break;
        }
        return 0;
    }
    
    static void stream_component_close(VideoState *is, int stream_index)
    {
        AVFormatContext *ic = is->ic;
    
        if (stream_index < 0 || stream_index >= ic->nb_streams)
            return;
    
        avctx = ic->streams[stream_index]->codec;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        switch (avctx->codec_type) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            packet_queue_abort(&is->audioq);
    
            SDL_CloseAudio();
    
            packet_queue_end(&is->audioq);
    
            av_free_packet(&is->audio_pkt);
    
            av_freep(&is->audio_buf1);
            is->audio_buf = NULL;
            av_freep(&is->frame);
    
    
            if (is->rdft) {
                av_rdft_end(is->rdft);
                av_freep(&is->rdft_data);
    
                is->rdft = NULL;
                is->rdft_bits = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            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;
    
            packet_queue_abort(&is->subtitleq);
    
            /* note: we also signal this mutex to make sure we deblock the
               video thread in all cases */
            SDL_LockMutex(is->subpq_mutex);
            is->subtitle_stream_changed = 1;
    
            SDL_CondSignal(is->subpq_cond);
            SDL_UnlockMutex(is->subpq_mutex);
    
            SDL_WaitThread(is->subtitle_tid, NULL);
    
            packet_queue_end(&is->subtitleq);
            break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        default:
            break;
        }
    
    
        ic->streams[stream_index]->discard = AVDISCARD_ALL;
    
        free_buffer_pool(&is->buffer_pool);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        switch (avctx->codec_type) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            is->audio_st = NULL;
            is->audio_stream = -1;
            break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            is->video_st = NULL;
            is->video_stream = -1;
            break;
    
            is->subtitle_st = NULL;
            is->subtitle_stream = -1;
            break;
    
    /* 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 *ctx)
    
        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 = NULL;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        AVPacket pkt1, *pkt = &pkt1;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        int eof = 0;
    
    Robert Krüger's avatar
    Robert Krüger committed
        int pkt_in_play_range = 0;
    
        AVDictionaryEntry *t;
    
        AVDictionary **opts;
        int orig_nb_streams;
    
        memset(st_index, -1, sizeof(st_index));
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        is->video_stream = -1;
        is->audio_stream = -1;
    
        ic = avformat_alloc_context();
        ic->interrupt_callback.callback = decode_interrupt_cb;
    
        err = avformat_open_input(&ic, is->filename, is->iformat, &format_opts);
    
        if (err < 0) {
            print_error(is->filename, err);
            ret = -1;
            goto fail;
        }
    
        if ((t = av_dict_get(format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
            av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
            ret = AVERROR_OPTION_NOT_FOUND;
            goto fail;
        }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        is->ic = ic;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        if (genpts)
    
            ic->flags |= AVFMT_FLAG_GENPTS;
    
    
        opts = setup_find_stream_info_opts(ic, codec_opts);
    
        orig_nb_streams = ic->nb_streams;
    
        err = avformat_find_stream_info(ic, opts);
    
        if (err < 0) {
            fprintf(stderr, "%s: could not find codec parameters\n", is->filename);
            ret = -1;
            goto fail;
        }
    
        for (i = 0; i < orig_nb_streams; i++)
            av_dict_free(&opts[i]);
        av_freep(&opts);
    
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        if (ic->pb)
            ic->pb->eof_reached = 0; // FIXME hack, avplay maybe should not use url_feof() to test for the end
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        if (seek_by_bytes < 0)
            seek_by_bytes = !!(ic->iformat->flags & AVFMT_TS_DISCONT);
    
    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 = avformat_seek_file(ic, -1, INT64_MIN, timestamp, INT64_MAX, 0);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (ret < 0) {
    
                fprintf(stderr, "%s: could not seek to position %0.3f\n",
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                        is->filename, (double)timestamp / AV_TIME_BASE);
            }
        }
    
    
        for (i = 0; i < ic->nb_streams; i++)
    
            st_index[AVMEDIA_TYPE_VIDEO] =
                av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO,
                                    wanted_stream[AVMEDIA_TYPE_VIDEO], -1, NULL, 0);
    
            st_index[AVMEDIA_TYPE_AUDIO] =
                av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO,
                                    wanted_stream[AVMEDIA_TYPE_AUDIO],
                                    st_index[AVMEDIA_TYPE_VIDEO],
                                    NULL, 0);
    
            st_index[AVMEDIA_TYPE_SUBTITLE] =
                av_find_best_stream(ic, AVMEDIA_TYPE_SUBTITLE,
                                    wanted_stream[AVMEDIA_TYPE_SUBTITLE],
                                    (st_index[AVMEDIA_TYPE_AUDIO] >= 0 ?
                                     st_index[AVMEDIA_TYPE_AUDIO] :
                                     st_index[AVMEDIA_TYPE_VIDEO]),
                                    NULL, 0);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (show_status) {
    
            av_dump_format(ic, 0, is->filename, 0);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
        /* open the streams */
    
        if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) {
            stream_component_open(is, st_index[AVMEDIA_TYPE_AUDIO]);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        ret = -1;
    
        if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            ret = stream_component_open(is, st_index[AVMEDIA_TYPE_VIDEO]);
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
        }
    
        is->refresh_tid = SDL_CreateThread(refresh_thread, is);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        if (ret < 0) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (!display_disable)
    
        if (st_index[AVMEDIA_TYPE_SUBTITLE] >= 0) {
            stream_component_open(is, st_index[AVMEDIA_TYPE_SUBTITLE]);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        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;
        }
    
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        for (;;) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            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)
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                    is->read_pause_return = av_read_pause(ic);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                else
                    av_read_play(ic);
    
    #if CONFIG_RTSP_DEMUXER
            if (is->paused && !strcmp(ic->iformat->name, "rtsp")) {
    
                /* 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) {
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                int64_t seek_target = is->seek_pos;
                int64_t seek_min    = is->seek_rel > 0 ? seek_target - is->seek_rel + 2: INT64_MIN;
                int64_t seek_max    = is->seek_rel < 0 ? seek_target - is->seek_rel - 2: INT64_MAX;
    // FIXME the +-2 is due to rounding being not done in the correct direction in generation
    
    //      of the seek_pos/seek_rel variables
    
                ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                if (ret < 0) {
                    fprintf(stderr, "%s: error while seeking\n", is->ic->filename);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                } 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;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                eof = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* if the queue are full, no need to read more */
    
            if (   is->audioq.size + is->videoq.size + is->subtitleq.size > MAX_QUEUE_SIZE
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                || (   (is->audioq   .size  > MIN_AUDIOQ_SIZE || is->audio_stream < 0)
                    && (is->videoq   .nb_packets > MIN_FRAMES || is->video_stream < 0)
                    && (is->subtitleq.nb_packets > MIN_FRAMES || is->subtitle_stream < 0))) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                /* wait 10 ms */
                SDL_Delay(10);
                continue;
            }
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            if (eof) {
                if (is->video_stream >= 0) {
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
                    av_init_packet(pkt);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                    pkt->data = NULL;
                    pkt->size = 0;
                    pkt->stream_index = is->video_stream;
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
                    packet_queue_put(&is->videoq, pkt);
    
                if (is->audio_stream >= 0 &&
                    is->audio_st->codec->codec->capabilities & CODEC_CAP_DELAY) {
                    av_init_packet(pkt);
                    pkt->data = NULL;
                    pkt->size = 0;
                    pkt->stream_index = is->audio_stream;
                    packet_queue_put(&is->audioq, pkt);
                }
    
                SDL_Delay(10);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                if (is->audioq.size + is->videoq.size + is->subtitleq.size == 0) {
                    if (loop != 1 && (!loop || --loop)) {
    
                        stream_seek(cur_stream, start_time != AV_NOPTS_VALUE ? start_time : 0, 0, 0);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                    } else if (autoexit) {
                        ret = AVERROR_EOF;
    
                        goto fail;
                    }
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
                }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            ret = av_read_frame(ic, pkt);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (ret < 0) {
    
                if (ret == AVERROR_EOF || (ic->pb && ic->pb->eof_reached))
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                    eof = 1;
    
                if (ic->pb && ic->pb->error)
    
                SDL_Delay(100); /* wait for user event */
                continue;
    
    Robert Krüger's avatar
    Robert Krüger committed
            /* check if packet is in play range specified by user, then queue, otherwise discard */
            pkt_in_play_range = duration == AV_NOPTS_VALUE ||
                    (pkt->pts - ic->streams[pkt->stream_index]->start_time) *
                    av_q2d(ic->streams[pkt->stream_index]->time_base) -
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                    (double)(start_time != AV_NOPTS_VALUE ? start_time : 0) / 1000000
                    <= ((double)duration / 1000000);
    
    Robert Krüger's avatar
    Robert Krüger committed
            if (pkt->stream_index == is->audio_stream && pkt_in_play_range) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                packet_queue_put(&is->audioq, pkt);
    
    Robert Krüger's avatar
    Robert Krüger committed
            } else if (pkt->stream_index == is->video_stream && pkt_in_play_range) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                packet_queue_put(&is->videoq, pkt);
    
    Robert Krüger's avatar
    Robert Krüger committed
            } else if (pkt->stream_index == is->subtitle_stream && pkt_in_play_range) {
    
                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);
    
            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));
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        is->ytop    = 0;
        is->xleft   = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        /* start video display */
        is->pictq_mutex = SDL_CreateMutex();
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        is->pictq_cond  = SDL_CreateCond();
    
        is->subpq_mutex = SDL_CreateMutex();
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        is->subpq_cond  = SDL_CreateCond();
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        is->parse_tid    = SDL_CreateThread(decode_thread, is);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (!is->parse_tid) {
            av_free(is);
            return NULL;
        }
        return is;
    }
    
    
    static void stream_cycle_channel(VideoState *is, int codec_type)
    
    {
        AVFormatContext *ic = is->ic;
        int start_index, stream_index;
        AVStream *st;
    
    
        if (codec_type == AVMEDIA_TYPE_VIDEO)
    
        else if (codec_type == AVMEDIA_TYPE_AUDIO)
    
        if (start_index < (codec_type == AVMEDIA_TYPE_SUBTITLE ? -1 : 0))
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        for (;;) {
    
            if (++stream_index >= is->ic->nb_streams)
    
                if (codec_type == AVMEDIA_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) {
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                switch (codec_type) {
    
                    if (st->codec->sample_rate != 0 &&
                        st->codec->channels != 0)
    
                case AVMEDIA_TYPE_VIDEO:
                case AVMEDIA_TYPE_SUBTITLE:
    
                    goto the_end;
                default:
                    break;
                }
            }
        }
     the_end:
        stream_component_close(is, start_index);
        stream_component_open(is, stream_index);
    }
    
    
    
    #if defined(__APPLE__) && SDL_VERSION_ATLEAST(1, 2, 14)
    
        /* OS X needs to empty the picture_queue */
    
        int i;
        for (i = 0; i < VIDEO_PICTURE_QUEUE_SIZE; i++)
    
            cur_stream->pictq[i].reallocate = 1;
    #endif
    
        is_full_screen = !is_full_screen;
    
    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) {
    
            int bgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0x00);
    
            cur_stream->show_audio = (cur_stream->show_audio + 1) % 3;
    
            fill_rectangle(screen,
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                           cur_stream->xleft, cur_stream->ytop, cur_stream->width, cur_stream->height,
                           bgcolor);
    
            SDL_UpdateRect(screen, cur_stream->xleft, cur_stream->ytop, cur_stream->width, cur_stream->height);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    }
    
    /* handle an event sent by the GUI */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        SDL_Event event;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        for (;;) {
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
            double x;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            SDL_WaitEvent(&event);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
            switch (event.type) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            case SDL_KEYDOWN:
    
                if (exit_on_keydown) {
                    do_exit();
                    break;
                }
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                switch (event.key.keysym.sym) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                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;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                case SDLK_s: // S: Step to next frame
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                case SDLK_a:
    
                    if (cur_stream)
    
                        stream_cycle_channel(cur_stream, AVMEDIA_TYPE_AUDIO);
    
                    if (cur_stream)
    
                        stream_cycle_channel(cur_stream, AVMEDIA_TYPE_VIDEO);
    
                    if (cur_stream)
    
                        stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE);
    
    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) {
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                            if (cur_stream->video_stream >= 0 && cur_stream->video_current_pos >= 0) {
                                pos = cur_stream->video_current_pos;
                            } else if (cur_stream->audio_stream >= 0 && cur_stream->audio_pkt.pos >= 0) {
                                pos = cur_stream->audio_pkt.pos;
                            } else
    
                                pos = avio_tell(cur_stream->ic->pb);
    
                            if (cur_stream->ic->bit_rate)
    
                                incr *= cur_stream->ic->bit_rate / 8.0;
    
                            else
                                incr *= 180000.0;
                            pos += incr;
    
                        } else {
                            pos = get_master_clock(cur_stream);
                            pos += incr;
    
                            stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE), (int64_t)(incr * AV_TIME_BASE), 0);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    }
                    break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                default:
                    break;
                }
                break;
    
                if (exit_on_mousedown) {
                    do_exit();
                    break;
                }
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
            case SDL_MOUSEMOTION:
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                if (event.type == SDL_MOUSEBUTTONDOWN) {
                    x = event.button.x;
                } else {
                    if (event.motion.state != SDL_PRESSED)
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
                        break;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                    x = event.motion.x;
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
                }
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                    if (seek_by_bytes || cur_stream->ic->duration <= 0) {
                        uint64_t size =  avio_size(cur_stream->ic->pb);
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
                        stream_seek(cur_stream, size*x/cur_stream->width, 0, 1);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                    } else {
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
                        int64_t ts;
                        int ns, hh, mm, ss;
                        int tns, thh, tmm, tss;
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                        tns  = cur_stream->ic->duration / 1000000LL;
                        thh  = tns / 3600;
                        tmm  = (tns % 3600) / 60;
                        tss  = (tns % 60);
                        frac = x / cur_stream->width;
                        ns   = frac * tns;
                        hh   = ns / 3600;
                        mm   = (ns % 3600) / 60;
                        ss   = (ns % 60);
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
                        fprintf(stderr, "Seek to %2.0f%% (%2d:%02d:%02d) of total duration (%2d:%02d:%02d)       \n", frac*100,
                                hh, mm, ss, thh, tmm, tss);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                        ts = frac * cur_stream->ic->duration;
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
                        if (cur_stream->ic->start_time != AV_NOPTS_VALUE)
                            ts += cur_stream->ic->start_time;
                        stream_seek(cur_stream, ts, 0, 0);
    
    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);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                    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);
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
                cur_stream->refresh = 0;
    
    static int opt_frame_size(const char *opt, const char *arg)
    
        av_log(NULL, AV_LOG_ERROR,
               "Option '%s' has been removed, use private format options instead\n", opt);
        return AVERROR(EINVAL);
    
    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;
    
    static int opt_format(const char *opt, const char *arg)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        file_iformat = av_find_input_format(arg);
        if (!file_iformat) {
            fprintf(stderr, "Unknown input format: %s\n", arg);
    
            return AVERROR(EINVAL);
    
        return 0;
    
    static int opt_frame_pix_fmt(const char *opt, const char *arg)
    
        av_log(NULL, AV_LOG_ERROR,
               "Option '%s' has been removed, use private format options instead\n", opt);
        return AVERROR(EINVAL);
    
    static int opt_sync(const char *opt, 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;
    
            fprintf(stderr, "Unknown value for %s: %s\n", opt, arg);
    
    static int opt_seek(const char *opt, const char *arg)
    
        start_time = parse_time_or_die(opt, arg, 1);
        return 0;
    
    Robert Krüger's avatar
    Robert Krüger committed
    static int opt_duration(const char *opt, const char *arg)
    {
        duration = 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;
    
    #include "cmdutils_common_opts.h"
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        { "x", HAS_ARG, { (void*)opt_width }, "force displayed width", "width" },
        { "y", HAS_ARG, { (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" },
        { "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_stream[AVMEDIA_TYPE_AUDIO] }, "select desired audio stream", "stream_number" },
        { "vst", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&wanted_stream[AVMEDIA_TYPE_VIDEO] }, "select desired video stream", "stream_number" },
        { "sst", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&wanted_stream[AVMEDIA_TYPE_SUBTITLE] }, "select desired subtitle stream", "stream_number" },
        { "ss", HAS_ARG, { (void*)&opt_seek }, "seek to a given position in seconds", "pos" },
        { "t", HAS_ARG, { (void*)&opt_duration }, "play  \"duration\" seconds of audio/video", "duration" },
        { "bytes", OPT_INT | HAS_ARG, { (void*)&seek_by_bytes }, "seek by bytes 0=off 1=on -1=auto", "val" },
        { "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" },
        { "stats", OPT_BOOL | OPT_EXPERT, { (void*)&show_status }, "show status", "" },
        { "debug", HAS_ARG | OPT_EXPERT, { (void*)opt_debug }, "print specific debug info", "" },
        { "bug", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&workaround_bugs }, "workaround bugs", "" },
        { "vismv", HAS_ARG | OPT_EXPERT, { (void*)opt_vismv }, "visualize motion vectors", "" },
        { "fast", OPT_BOOL | OPT_EXPERT, { (void*)&fast }, "non spec compliant optimizations", "" },
        { "genpts", OPT_BOOL | OPT_EXPERT, { (void*)&genpts }, "generate pts", "" },
        { "drp", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&decoder_reorder_pts }, "let decoder reorder pts 0=off 1=on -1=auto", ""},
        { "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" },
        { "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" },
        { "autoexit", OPT_BOOL | OPT_EXPERT, { (void*)&autoexit }, "exit at the end", "" },
        { "exitonkeydown", OPT_BOOL | OPT_EXPERT, { (void*)&exit_on_keydown }, "exit on key down", "" },
        { "exitonmousedown", OPT_BOOL | OPT_EXPERT, { (void*)&exit_on_mousedown }, "exit on mouse down", "" },
        { "loop", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&loop }, "set number of times the playback shall be looped", "loop count" },
        { "framedrop", OPT_BOOL | OPT_EXPERT, { (void*)&framedrop }, "drop frames when cpu is too slow", "" },
        { "window_title", OPT_STRING | HAS_ARG, { (void*)&window_title }, "set window title", "window title" },
    
    #if CONFIG_AVFILTER
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        { "vf", OPT_STRING | HAS_ARG, { (void*)&vfilters }, "video filters", "filter list" },
    
    Aneesh Dogra's avatar
    Aneesh Dogra committed
        { "rdftspeed", OPT_INT | HAS_ARG| OPT_AUDIO | OPT_EXPERT, { (void*)&rdftspeed }, "rdft speed", "msecs" },
        { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, { (void*)opt_default }, "generic catch all option", "" },
        { "i", 0, { NULL }, "avconv compatibility dummy option", ""},
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        { NULL, },
    };
    
    
    static void show_usage(void)
    
        printf("Simple media player\n");
    
    Anton Khirnov's avatar
    Anton Khirnov committed
        printf("usage: %s [options] input_file\n", program_name);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        printf("\n");
    
    }
    
    static void show_help(void)
    {
    
        av_log_set_callback(log_callback_help);
    
        show_usage();
    
        show_help_options(options, "Main options:\n",
                          OPT_EXPERT, 0);
        show_help_options(options, "\nAdvanced options:\n",
                          OPT_EXPERT, OPT_EXPERT);
    
        show_help_children(avcodec_get_class(), AV_OPT_FLAG_DECODING_PARAM);
        show_help_children(avformat_get_class(), AV_OPT_FLAG_DECODING_PARAM);
    
    #if !CONFIG_AVFILTER
    
        show_help_children(sws_get_class(), AV_OPT_FLAG_ENCODING_PARAM);
    
    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"
    
               "s                   activate frame-step mode\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(void *optctx, const char *filename)
    
        if (input_filename) {
            fprintf(stderr, "Argument '%s' provided as input filename, but '%s' was already specified.\n",
                    filename, input_filename);
            exit(1);
        }
    
            filename = "pipe:";
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        input_filename = filename;
    }
    
    /* Called from the main */
    int main(int argc, char **argv)
    {
    
        av_log_set_flags(AV_LOG_SKIP_REPEATED);
    
        parse_loglevel(argc, argv, options);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* register all codecs, demux and protocols */
    
    Luca Abeni's avatar
    Luca Abeni committed
        avcodec_register_all();
    
    Luca Abeni's avatar
    Luca Abeni committed
        avdevice_register_all();
    
    #if CONFIG_AVFILTER
        avfilter_register_all();
    #endif
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        av_register_all();
    
        avformat_network_init();
    
        parse_options(NULL, argc, argv, options, opt_input_file);
    
            fprintf(stderr, "An input file must be specified\n");
    
    Anton Khirnov's avatar
    Anton Khirnov committed
            fprintf(stderr, "Use -h to get full help or, even better, run 'man %s'\n", program_name);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        if (display_disable) {
            video_disable = 1;
        }