X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=ffplay.c;h=598ce749d5d5a84c59f346b03505f0dd14e4f630;hb=0fa352623d2322f99221e6e58d48fce9637054ee;hp=4015aaf8ec914c05fc2cb1d62d8448c7ac209fd4;hpb=715c8a5a5052d67423f27192444474a3d61dce60;p=ffmpeg diff --git a/ffplay.c b/ffplay.c index 4015aaf8ec9..598ce749d5d 100644 --- a/ffplay.c +++ b/ffplay.c @@ -37,6 +37,7 @@ #include "libavutil/parseutils.h" #include "libavutil/samplefmt.h" #include "libavutil/avassert.h" +#include "libavutil/time.h" #include "libavformat/avformat.h" #include "libavdevice/avdevice.h" #include "libswscale/swscale.h" @@ -49,6 +50,7 @@ # include "libavfilter/avfilter.h" # include "libavfilter/avfiltergraph.h" # include "libavfilter/buffersink.h" +# include "libavfilter/buffersrc.h" #endif #include @@ -56,7 +58,6 @@ #include "cmdutils.h" -#include #include const char program_name[] = "ffplay"; @@ -99,14 +100,13 @@ typedef struct PacketQueue { typedef struct VideoPicture { double pts; ///< presentation time stamp for this picture - double duration; ///< expected duration of the frame int64_t pos; ///< byte position in file int skip; SDL_Overlay *bmp; int width, height; /* source height & width */ + AVRational sample_aspect_ratio; int allocated; int reallocate; - enum PixelFormat pix_fmt; #if CONFIG_AVFILTER AVFilterBufferRef *picref; @@ -118,6 +118,13 @@ typedef struct SubPicture { AVSubtitle sub; } SubPicture; +typedef struct AudioParams { + int freq; + int channels; + int channel_layout; + enum AVSampleFormat fmt; +} AudioParams; + enum { AV_SYNC_AUDIO_MASTER, /* default choice */ AV_SYNC_VIDEO_MASTER, @@ -164,14 +171,8 @@ typedef struct VideoState { int audio_write_buf_size; AVPacket audio_pkt_temp; AVPacket audio_pkt; - enum AVSampleFormat audio_src_fmt; - enum AVSampleFormat audio_tgt_fmt; - int audio_src_channels; - int audio_tgt_channels; - int64_t audio_src_channel_layout; - int64_t audio_tgt_channel_layout; - int audio_src_freq; - int audio_tgt_freq; + struct AudioParams audio_src; + struct AudioParams audio_tgt; struct SwrContext *swr_ctx; double audio_current_pts; double audio_current_pts_drift; @@ -227,12 +228,21 @@ typedef struct VideoState { int step; #if CONFIG_AVFILTER + AVFilterContext *in_video_filter; ///< the first filter in the video chain AVFilterContext *out_video_filter; ///< the last filter in the video chain + int use_dr1; + FrameBuffer *buffer_pool; #endif int refresh; + int last_video_stream, last_audio_stream, last_subtitle_stream; } VideoState; +typedef struct AllocEventProps { + VideoState *is; + AVFrame *frame; +} AllocEventProps; + static int opt_help(const char *opt, const char *arg); /* options specified by the user */ @@ -271,6 +281,7 @@ static int exit_on_keydown; static int exit_on_mousedown; static int loop = 1; static int framedrop = -1; +static int infinite_buffer = 0; static enum ShowMode show_mode = SHOW_MODE_NONE; static const char *audio_codec_name; static const char *subtitle_codec_name; @@ -297,13 +308,12 @@ void av_noreturn exit_program(int ret) exit(ret); } -static int packet_queue_put(PacketQueue *q, AVPacket *pkt) +static int packet_queue_put_private(PacketQueue *q, AVPacket *pkt) { AVPacketList *pkt1; - /* duplicate the packet */ - if (pkt != &flush_pkt && av_dup_packet(pkt) < 0) - return -1; + if (q->abort_request) + return -1; pkt1 = av_malloc(sizeof(AVPacketList)); if (!pkt1) @@ -311,11 +321,7 @@ static int packet_queue_put(PacketQueue *q, AVPacket *pkt) pkt1->pkt = *pkt; pkt1->next = NULL; - - SDL_LockMutex(q->mutex); - if (!q->last_pkt) - q->first_pkt = pkt1; else q->last_pkt->next = pkt1; @@ -324,9 +330,25 @@ static int packet_queue_put(PacketQueue *q, AVPacket *pkt) q->size += pkt1->pkt.size + sizeof(*pkt1); /* XXX: should duplicate packet data in DV case */ SDL_CondSignal(q->cond); + return 0; +} + +static int packet_queue_put(PacketQueue *q, AVPacket *pkt) +{ + int ret; + + /* duplicate the packet */ + if (pkt != &flush_pkt && av_dup_packet(pkt) < 0) + return -1; + SDL_LockMutex(q->mutex); + ret = packet_queue_put_private(q, pkt); SDL_UnlockMutex(q->mutex); - return 0; + + if (pkt != &flush_pkt && ret < 0) + av_free_packet(pkt); + + return ret; } /* packet queue handling */ @@ -335,7 +357,7 @@ static void packet_queue_init(PacketQueue *q) memset(q, 0, sizeof(PacketQueue)); q->mutex = SDL_CreateMutex(); q->cond = SDL_CreateCond(); - packet_queue_put(q, &flush_pkt); + q->abort_request = 1; } static void packet_queue_flush(PacketQueue *q) @@ -355,7 +377,7 @@ static void packet_queue_flush(PacketQueue *q) SDL_UnlockMutex(q->mutex); } -static void packet_queue_end(PacketQueue *q) +static void packet_queue_destroy(PacketQueue *q) { packet_queue_flush(q); SDL_DestroyMutex(q->mutex); @@ -373,6 +395,14 @@ static void packet_queue_abort(PacketQueue *q) SDL_UnlockMutex(q->mutex); } +static void packet_queue_start(PacketQueue *q) +{ + SDL_LockMutex(q->mutex); + q->abort_request = 0; + packet_queue_put_private(q, &flush_pkt); + SDL_UnlockMutex(q->mutex); +} + /* return < 0 if aborted, 0 if no packet and > 0 if packet. */ static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) { @@ -666,21 +696,11 @@ static void video_image_display(VideoState *is) vp = &is->pictq[is->pictq_rindex]; if (vp->bmp) { -#if CONFIG_AVFILTER - if (vp->picref->video->sample_aspect_ratio.num == 0) - aspect_ratio = 0; - else - aspect_ratio = av_q2d(vp->picref->video->sample_aspect_ratio); -#else - - /* XXX: use variable in the frame */ - if (is->video_st->sample_aspect_ratio.num) - aspect_ratio = av_q2d(is->video_st->sample_aspect_ratio); - else if (is->video_st->codec->sample_aspect_ratio.num) - aspect_ratio = av_q2d(is->video_st->codec->sample_aspect_ratio); - else + if (vp->sample_aspect_ratio.num == 0) aspect_ratio = 0; -#endif + else + aspect_ratio = av_q2d(vp->sample_aspect_ratio); + if (aspect_ratio <= 0.0) aspect_ratio = 1.0; aspect_ratio *= (float)vp->width / (float)vp->height; @@ -745,7 +765,7 @@ static void video_audio_display(VideoState *s) nb_freq = 1 << (rdft_bits - 1); /* compute display index : center on currently output samples */ - channels = s->audio_tgt_channels; + channels = s->audio_tgt.channels; nb_display_channels = channels; if (!s->paused) { int data_used= s->show_mode == SHOW_MODE_WAVES ? s->width : (2*nb_freq); @@ -757,7 +777,7 @@ static void video_audio_display(VideoState *s) the last buffer computation */ if (audio_callback_time) { time_diff = av_gettime() - audio_callback_time; - delay -= (time_diff * s->audio_tgt_freq) / 1000000; + delay -= (time_diff * s->audio_tgt.freq) / 1000000; } delay += 2 * data_used; @@ -881,15 +901,15 @@ static void stream_close(VideoState *is) is->abort_request = 1; SDL_WaitThread(is->read_tid, NULL); SDL_WaitThread(is->refresh_tid, NULL); + packet_queue_destroy(&is->videoq); + packet_queue_destroy(&is->audioq); + packet_queue_destroy(&is->subtitleq); /* free all pictures */ for (i = 0; i < VIDEO_PICTURE_QUEUE_SIZE; i++) { vp = &is->pictq[i]; #if CONFIG_AVFILTER - if (vp->picref) { - avfilter_unref_buffer(vp->picref); - vp->picref = NULL; - } + avfilter_unref_bufferp(&vp->picref); #endif if (vp->bmp) { SDL_FreeYUVOverlay(vp->bmp); @@ -934,6 +954,7 @@ static int video_open(VideoState *is, int force_set_video_mode) { int flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL; int w,h; + VideoPicture *vp = &is->pictq[is->pictq_rindex]; if (is_full_screen) flags |= SDL_FULLSCREEN; else flags |= SDL_RESIZABLE; @@ -944,15 +965,9 @@ static int video_open(VideoState *is, int force_set_video_mode) } else if (!is_full_screen && screen_width) { w = screen_width; h = screen_height; -#if CONFIG_AVFILTER - } else if (is->out_video_filter && is->out_video_filter->inputs[0]) { - w = is->out_video_filter->inputs[0]->w; - h = is->out_video_filter->inputs[0]->h; -#else - } else if (is->video_st && is->video_st->codec->width) { - w = is->video_st->codec->width; - h = is->video_st->codec->height; -#endif + } else if (vp->width) { + w = vp->width; + h = vp->height; } else { w = 640; h = 480; @@ -998,7 +1013,7 @@ static int refresh_thread(void *opaque) SDL_PushEvent(&event); } //FIXME ideally we should wait the correct time but SDLs event passing is so slow it would be silly - usleep(is->audio_st && is->show_mode != SHOW_MODE_VIDEO ? rdftspeed*1000 : 5000); + av_usleep(is->audio_st && is->show_mode != SHOW_MODE_VIDEO ? rdftspeed*1000 : 5000); } return 0; } @@ -1180,13 +1195,8 @@ retry: if (is->pictq_size > 1) { VideoPicture *nextvp = &is->pictq[(is->pictq_rindex + 1) % VIDEO_PICTURE_QUEUE_SIZE]; - duration = nextvp->pts - vp->pts; // More accurate this way, 1/time_base is often not reflecting FPS - } else { - duration = vp->duration; - } - - if((framedrop>0 || (framedrop && is->audio_st)) && time > is->frame_timer + duration){ - if(is->pictq_size > 1){ + duration = nextvp->pts - vp->pts; + if((framedrop>0 || (framedrop && is->audio_st)) && time > is->frame_timer + duration){ is->frame_drops_late++; pictq_next_picture(is); goto retry; @@ -1293,9 +1303,10 @@ display: /* allocate a picture (needs to do that in main thread to avoid potential locking problems */ -static void alloc_picture(void *opaque) +static void alloc_picture(AllocEventProps *event_props) { - VideoState *is = opaque; + VideoState *is = event_props->is; + AVFrame *frame = event_props->frame; VideoPicture *vp; vp = &is->pictq[is->pictq_windex]; @@ -1304,19 +1315,14 @@ static void alloc_picture(void *opaque) SDL_FreeYUVOverlay(vp->bmp); #if CONFIG_AVFILTER - if (vp->picref) - avfilter_unref_buffer(vp->picref); - vp->picref = NULL; - - vp->width = is->out_video_filter->inputs[0]->w; - vp->height = is->out_video_filter->inputs[0]->h; - vp->pix_fmt = is->out_video_filter->inputs[0]->format; -#else - vp->width = is->video_st->codec->width; - vp->height = is->video_st->codec->height; - vp->pix_fmt = is->video_st->codec->pix_fmt; + avfilter_unref_bufferp(&vp->picref); #endif + vp->width = frame->width; + vp->height = frame->height; + + video_open(event_props->is, 0); + vp->bmp = SDL_CreateYUVOverlay(vp->width, vp->height, SDL_YV12_OVERLAY, screen); @@ -1374,26 +1380,24 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts1, int64_ vp = &is->pictq[is->pictq_windex]; - vp->duration = frame_delay; - /* alloc or resize hardware picture buffer */ if (!vp->bmp || vp->reallocate || -#if CONFIG_AVFILTER - vp->width != is->out_video_filter->inputs[0]->w || - vp->height != is->out_video_filter->inputs[0]->h) { -#else - vp->width != is->video_st->codec->width || - vp->height != is->video_st->codec->height) { -#endif + vp->width != src_frame->width || + vp->height != src_frame->height) { SDL_Event event; + AllocEventProps event_props; + + event_props.frame = src_frame; + event_props.is = is; vp->allocated = 0; vp->reallocate = 0; /* the allocation must be done in the main thread to avoid - locking problems */ + locking problems. We wait in this block for the event to complete, + so we can pass a pointer to event_props to it. */ event.type = FF_ALLOC_EVENT; - event.user.data1 = is; + event.user.data1 = &event_props; SDL_PushEvent(&event); /* wait until the picture is allocated */ @@ -1417,8 +1421,7 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts1, int64_ if (vp->bmp) { AVPicture pict = { { 0 } }; #if CONFIG_AVFILTER - if (vp->picref) - avfilter_unref_buffer(vp->picref); + avfilter_unref_bufferp(&vp->picref); vp->picref = src_frame->opaque; #endif @@ -1436,11 +1439,12 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts1, int64_ #if CONFIG_AVFILTER // FIXME use direct rendering av_picture_copy(&pict, (AVPicture *)src_frame, - vp->pix_fmt, vp->width, vp->height); + src_frame->format, vp->width, vp->height); + vp->sample_aspect_ratio = vp->picref->video->sample_aspect_ratio; #else sws_flags = av_get_int(sws_opts, "sws_flags", NULL); is->img_convert_ctx = sws_getCachedContext(is->img_convert_ctx, - vp->width, vp->height, vp->pix_fmt, vp->width, vp->height, + vp->width, vp->height, src_frame->format, vp->width, vp->height, PIX_FMT_YUV420P, sws_flags, NULL, NULL, NULL); if (is->img_convert_ctx == NULL) { fprintf(stderr, "Cannot initialize the conversion context\n"); @@ -1448,6 +1452,7 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts1, int64_ } sws_scale(is->img_convert_ctx, src_frame->data, src_frame->linesize, 0, vp->height, pict.data, pict.linesize); + vp->sample_aspect_ratio = av_guess_sample_aspect_ratio(is->ic, is->video_st, src_frame); #endif /* update the bitmap content */ SDL_UnlockYUVOverlay(vp->bmp); @@ -1494,7 +1499,8 @@ static int get_video_frame(VideoState *is, AVFrame *frame, int64_t *pts, AVPacke return 0; } - avcodec_decode_video2(is->video_st->codec, frame, &got_picture, pkt); + if(avcodec_decode_video2(is->video_st->codec, frame, &got_picture, pkt) < 0) + return 0; if (got_picture) { int ret = 1; @@ -1530,267 +1536,94 @@ static int get_video_frame(VideoState *is, AVFrame *frame, int64_t *pts, AVPacke SDL_UnlockMutex(is->pictq_mutex); } - if (ret) - is->frame_last_returned_time = av_gettime() / 1000000.0; - return ret; } return 0; } #if CONFIG_AVFILTER -typedef struct { - VideoState *is; - AVFrame *frame; - int use_dr1; -} FilterPriv; - -static int input_get_buffer(AVCodecContext *codec, AVFrame *pic) +static int configure_filtergraph(AVFilterGraph *graph, const char *filtergraph, + AVFilterContext *source_ctx, AVFilterContext *sink_ctx) { - AVFilterContext *ctx = codec->opaque; - AVFilterBufferRef *ref; - int perms = AV_PERM_WRITE; - int i, w, h, stride[AV_NUM_DATA_POINTERS]; - unsigned edge; - int pixel_size; - - av_assert0(codec->flags & CODEC_FLAG_EMU_EDGE); - - if (codec->codec->capabilities & CODEC_CAP_NEG_LINESIZES) - perms |= AV_PERM_NEG_LINESIZES; - - if (pic->buffer_hints & FF_BUFFER_HINTS_VALID) { - if (pic->buffer_hints & FF_BUFFER_HINTS_READABLE) perms |= AV_PERM_READ; - if (pic->buffer_hints & FF_BUFFER_HINTS_PRESERVE) perms |= AV_PERM_PRESERVE; - if (pic->buffer_hints & FF_BUFFER_HINTS_REUSABLE) perms |= AV_PERM_REUSE2; - } - if (pic->reference) perms |= AV_PERM_READ | AV_PERM_PRESERVE; - - w = codec->width; - h = codec->height; - - if(av_image_check_size(w, h, 0, codec) || codec->pix_fmt<0) - return -1; - - avcodec_align_dimensions2(codec, &w, &h, stride); - edge = codec->flags & CODEC_FLAG_EMU_EDGE ? 0 : avcodec_get_edge_width(); - w += edge << 1; - h += edge << 1; - if (codec->pix_fmt != ctx->outputs[0]->format) { - av_log(codec, AV_LOG_ERROR, "Pixel format mismatches %d %d\n", codec->pix_fmt, ctx->outputs[0]->format); - return -1; - } - if (!(ref = avfilter_get_video_buffer(ctx->outputs[0], perms, w, h))) - return -1; - - pixel_size = av_pix_fmt_descriptors[ref->format].comp[0].step_minus1 + 1; - ref->video->w = codec->width; - ref->video->h = codec->height; - for (i = 0; i < 4; i ++) { - unsigned hshift = (i == 1 || i == 2) ? av_pix_fmt_descriptors[ref->format].log2_chroma_w : 0; - unsigned vshift = (i == 1 || i == 2) ? av_pix_fmt_descriptors[ref->format].log2_chroma_h : 0; + int ret; + AVFilterInOut *outputs = NULL, *inputs = NULL; - if (ref->data[i]) { - ref->data[i] += ((edge * pixel_size) >> hshift) + ((edge * ref->linesize[i]) >> vshift); + if (filtergraph) { + outputs = avfilter_inout_alloc(); + inputs = avfilter_inout_alloc(); + if (!outputs || !inputs) { + ret = AVERROR(ENOMEM); + goto fail; } - pic->data[i] = ref->data[i]; - pic->linesize[i] = ref->linesize[i]; - } - pic->opaque = ref; - pic->type = FF_BUFFER_TYPE_USER; - pic->reordered_opaque = codec->reordered_opaque; - pic->width = codec->width; - pic->height = codec->height; - pic->format = codec->pix_fmt; - pic->sample_aspect_ratio = codec->sample_aspect_ratio; - if (codec->pkt) pic->pkt_pts = codec->pkt->pts; - else pic->pkt_pts = AV_NOPTS_VALUE; - return 0; -} - -static void input_release_buffer(AVCodecContext *codec, AVFrame *pic) -{ - memset(pic->data, 0, sizeof(pic->data)); - avfilter_unref_buffer(pic->opaque); -} - -static int input_reget_buffer(AVCodecContext *codec, AVFrame *pic) -{ - AVFilterBufferRef *ref = pic->opaque; - - if (pic->data[0] == NULL) { - pic->buffer_hints |= FF_BUFFER_HINTS_READABLE; - return codec->get_buffer(codec, pic); - } - - if ((codec->width != ref->video->w) || (codec->height != ref->video->h) || - (codec->pix_fmt != ref->format)) { - av_log(codec, AV_LOG_ERROR, "Picture properties changed.\n"); - return -1; - } - - pic->reordered_opaque = codec->reordered_opaque; - if (codec->pkt) pic->pkt_pts = codec->pkt->pts; - else pic->pkt_pts = AV_NOPTS_VALUE; - return 0; -} - -static int input_init(AVFilterContext *ctx, const char *args, void *opaque) -{ - FilterPriv *priv = ctx->priv; - AVCodecContext *codec; - if (!opaque) return -1; - - priv->is = opaque; - codec = priv->is->video_st->codec; - codec->opaque = ctx; - if (codec->codec->capabilities & CODEC_CAP_DR1) { - av_assert0(codec->flags & CODEC_FLAG_EMU_EDGE); - priv->use_dr1 = 1; - codec->get_buffer = input_get_buffer; - codec->release_buffer = input_release_buffer; - codec->reget_buffer = input_reget_buffer; - codec->thread_safe_callbacks = 1; - } - - priv->frame = avcodec_alloc_frame(); - - return 0; -} -static void input_uninit(AVFilterContext *ctx) -{ - FilterPriv *priv = ctx->priv; - av_free(priv->frame); -} + outputs->name = av_strdup("in"); + outputs->filter_ctx = source_ctx; + outputs->pad_idx = 0; + outputs->next = NULL; -static int input_request_frame(AVFilterLink *link) -{ - FilterPriv *priv = link->src->priv; - AVFilterBufferRef *picref; - int64_t pts = 0; - AVPacket pkt; - int ret; + inputs->name = av_strdup("out"); + inputs->filter_ctx = sink_ctx; + inputs->pad_idx = 0; + inputs->next = NULL; - while (!(ret = get_video_frame(priv->is, priv->frame, &pts, &pkt))) - av_free_packet(&pkt); - if (ret < 0) - return -1; - - if (priv->use_dr1 && priv->frame->opaque) { - picref = avfilter_ref_buffer(priv->frame->opaque, ~0); + if ((ret = avfilter_graph_parse(graph, filtergraph, &inputs, &outputs, NULL)) < 0) + goto fail; } else { - picref = avfilter_get_video_buffer(link, AV_PERM_WRITE, link->w, link->h); - av_image_copy(picref->data, picref->linesize, - (const uint8_t **)(void **)priv->frame->data, priv->frame->linesize, - picref->format, link->w, link->h); + if ((ret = avfilter_link(source_ctx, 0, sink_ctx, 0)) < 0) + goto fail; } - av_free_packet(&pkt); - - avfilter_copy_frame_props(picref, priv->frame); - picref->pts = pts; - - avfilter_start_frame(link, picref); - avfilter_draw_slice(link, 0, link->h, 1); - avfilter_end_frame(link); - - return 0; -} - -static int input_query_formats(AVFilterContext *ctx) -{ - FilterPriv *priv = ctx->priv; - enum PixelFormat pix_fmts[] = { - priv->is->video_st->codec->pix_fmt, PIX_FMT_NONE - }; - - avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts)); - return 0; -} - -static int input_config_props(AVFilterLink *link) -{ - FilterPriv *priv = link->src->priv; - AVStream *s = priv->is->video_st; - - link->w = s->codec->width; - link->h = s->codec->height; - link->sample_aspect_ratio = s->sample_aspect_ratio.num ? - s->sample_aspect_ratio : s->codec->sample_aspect_ratio; - link->time_base = s->time_base; - return 0; + return avfilter_graph_config(graph, NULL); +fail: + avfilter_inout_free(&outputs); + avfilter_inout_free(&inputs); + return ret; } -static AVFilter input_filter = -{ - .name = "ffplay_input", - - .priv_size = sizeof(FilterPriv), - - .init = input_init, - .uninit = input_uninit, - - .query_formats = input_query_formats, - - .inputs = (AVFilterPad[]) {{ .name = NULL }}, - .outputs = (AVFilterPad[]) {{ .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .request_frame = input_request_frame, - .config_props = input_config_props, }, - { .name = NULL }}, -}; - static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const char *vfilters) { static const enum PixelFormat pix_fmts[] = { PIX_FMT_YUV420P, PIX_FMT_NONE }; char sws_flags_str[128]; + char buffersrc_args[256]; int ret; AVBufferSinkParams *buffersink_params = av_buffersink_params_alloc(); - AVFilterContext *filt_src = NULL, *filt_out = NULL; + AVFilterContext *filt_src = NULL, *filt_out = NULL, *filt_format; + AVCodecContext *codec = is->video_st->codec; + snprintf(sws_flags_str, sizeof(sws_flags_str), "flags=%d", sws_flags); graph->scale_sws_opts = av_strdup(sws_flags_str); - if ((ret = avfilter_graph_create_filter(&filt_src, &input_filter, "src", - NULL, is, graph)) < 0) + snprintf(buffersrc_args, sizeof(buffersrc_args), + "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", + codec->width, codec->height, codec->pix_fmt, + is->video_st->time_base.num, is->video_st->time_base.den, + codec->sample_aspect_ratio.num, codec->sample_aspect_ratio.den); + + if ((ret = avfilter_graph_create_filter(&filt_src, + avfilter_get_by_name("buffer"), + "ffplay_buffer", buffersrc_args, NULL, + graph)) < 0) return ret; -#if FF_API_OLD_VSINK_API - ret = avfilter_graph_create_filter(&filt_out, avfilter_get_by_name("buffersink"), "out", - NULL, pix_fmts, graph); -#else buffersink_params->pixel_fmts = pix_fmts; - ret = avfilter_graph_create_filter(&filt_out, avfilter_get_by_name("buffersink"), "out", - NULL, buffersink_params, graph); -#endif + ret = avfilter_graph_create_filter(&filt_out, + avfilter_get_by_name("buffersink"), + "ffplay_buffersink", NULL, buffersink_params, graph); av_freep(&buffersink_params); if (ret < 0) return ret; - if (vfilters) { - AVFilterInOut *outputs = avfilter_inout_alloc(); - AVFilterInOut *inputs = avfilter_inout_alloc(); - - outputs->name = av_strdup("in"); - outputs->filter_ctx = filt_src; - outputs->pad_idx = 0; - outputs->next = NULL; - - inputs->name = av_strdup("out"); - inputs->filter_ctx = filt_out; - inputs->pad_idx = 0; - inputs->next = NULL; - - if ((ret = avfilter_graph_parse(graph, vfilters, &inputs, &outputs, NULL)) < 0) - return ret; - } else { - if ((ret = avfilter_link(filt_src, 0, filt_out, 0)) < 0) - return ret; - } + if ((ret = avfilter_graph_create_filter(&filt_format, + avfilter_get_by_name("format"), + "format", "yuv420p", NULL, graph)) < 0) + return ret; + if ((ret = avfilter_link(filt_format, 0, filt_out, 0)) < 0) + return ret; - if ((ret = avfilter_graph_config(graph, NULL)) < 0) + if ((ret = configure_filtergraph(graph, vfilters, filt_src, filt_format)) < 0) return ret; + is->in_video_filter = filt_src; is->out_video_filter = filt_out; return ret; @@ -1800,6 +1633,7 @@ static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const c static int video_thread(void *arg) { + AVPacket pkt = { 0 }; VideoState *is = arg; AVFrame *frame = avcodec_alloc_frame(); int64_t pts_int = AV_NOPTS_VALUE, pos = -1; @@ -1807,75 +1641,119 @@ static int video_thread(void *arg) int ret; #if CONFIG_AVFILTER + AVCodecContext *codec = is->video_st->codec; AVFilterGraph *graph = avfilter_graph_alloc(); - AVFilterContext *filt_out = NULL; - int last_w = is->video_st->codec->width; - int last_h = is->video_st->codec->height; + AVFilterContext *filt_out = NULL, *filt_in = NULL; + int last_w = 0; + int last_h = 0; + enum PixelFormat last_format = -2; - if ((ret = configure_video_filters(graph, is, vfilters)) < 0) - goto the_end; - filt_out = is->out_video_filter; + if (codec->codec->capabilities & CODEC_CAP_DR1) { + is->use_dr1 = 1; + codec->get_buffer = codec_get_buffer; + codec->release_buffer = codec_release_buffer; + codec->opaque = &is->buffer_pool; + } #endif for (;;) { -#if !CONFIG_AVFILTER - AVPacket pkt; -#else +#if CONFIG_AVFILTER AVFilterBufferRef *picref; - AVRational tb = filt_out->inputs[0]->time_base; + AVRational tb; #endif while (is->paused && !is->videoq.abort_request) SDL_Delay(10); + + avcodec_get_frame_defaults(frame); + av_free_packet(&pkt); + + ret = get_video_frame(is, frame, &pts_int, &pkt); + if (ret < 0) + goto the_end; + + if (!ret) + continue; + #if CONFIG_AVFILTER if ( last_w != is->video_st->codec->width - || last_h != is->video_st->codec->height) { + || last_h != is->video_st->codec->height + || last_format != is->video_st->codec->pix_fmt) { av_log(NULL, AV_LOG_INFO, "Frame changed from size:%dx%d to size:%dx%d\n", last_w, last_h, is->video_st->codec->width, is->video_st->codec->height); avfilter_graph_free(&graph); graph = avfilter_graph_alloc(); - if ((ret = configure_video_filters(graph, is, vfilters)) < 0) + if ((ret = configure_video_filters(graph, is, vfilters)) < 0) { + SDL_Event event; + event.type = FF_QUIT_EVENT; + event.user.data1 = is; + SDL_PushEvent(&event); + av_free_packet(&pkt); goto the_end; + } + filt_in = is->in_video_filter; filt_out = is->out_video_filter; last_w = is->video_st->codec->width; last_h = is->video_st->codec->height; - } - ret = av_buffersink_get_buffer_ref(filt_out, &picref, 0); - if (picref) { - avfilter_fill_frame_from_video_buffer_ref(frame, picref); - pts_int = picref->pts; - pos = picref->pos; - frame->opaque = picref; + last_format = is->video_st->codec->pix_fmt; } - if (ret >= 0 && av_cmp_q(tb, is->video_st->time_base)) { - av_unused int64_t pts1 = pts_int; - pts_int = av_rescale_q(pts_int, tb, is->video_st->time_base); - av_dlog(NULL, "video_thread(): " - "tb:%d/%d pts:%"PRId64" -> tb:%d/%d pts:%"PRId64"\n", - tb.num, tb.den, pts1, - is->video_st->time_base.num, is->video_st->time_base.den, pts_int); - } -#else - ret = get_video_frame(is, frame, &pts_int, &pkt); - pos = pkt.pos; + frame->pts = pts_int; + frame->sample_aspect_ratio = av_guess_sample_aspect_ratio(is->ic, is->video_st, frame); + if (is->use_dr1 && frame->opaque) { + FrameBuffer *buf = frame->opaque; + AVFilterBufferRef *fb = avfilter_get_video_buffer_ref_from_arrays( + frame->data, frame->linesize, + AV_PERM_READ | AV_PERM_PRESERVE, + frame->width, frame->height, + frame->format); + + avfilter_copy_frame_props(fb, frame); + fb->buf->priv = buf; + fb->buf->free = filter_release_buffer; + + buf->refcount++; + av_buffersrc_add_ref(filt_in, fb, AV_BUFFERSRC_FLAG_NO_COPY); + + } else + av_buffersrc_write_frame(filt_in, frame); + av_free_packet(&pkt); -#endif - if (ret < 0) - goto the_end; + while (ret >= 0) { + is->frame_last_returned_time = av_gettime() / 1000000.0; - is->frame_last_filter_delay = av_gettime() / 1000000.0 - is->frame_last_returned_time; - if (fabs(is->frame_last_filter_delay) > AV_NOSYNC_THRESHOLD / 10.0) - is->frame_last_filter_delay = 0; + ret = av_buffersink_get_buffer_ref(filt_out, &picref, 0); + if (ret < 0) { + ret = 0; + break; + } -#if CONFIG_AVFILTER - if (!picref) - continue; -#endif + is->frame_last_filter_delay = av_gettime() / 1000000.0 - is->frame_last_returned_time; + if (fabs(is->frame_last_filter_delay) > AV_NOSYNC_THRESHOLD / 10.0) + is->frame_last_filter_delay = 0; - pts = pts_int * av_q2d(is->video_st->time_base); + avfilter_copy_buf_props(frame, picref); - ret = queue_picture(is, frame, pts, pos); + pts_int = picref->pts; + tb = filt_out->inputs[0]->time_base; + pos = picref->pos; + frame->opaque = picref; + + if (av_cmp_q(tb, is->video_st->time_base)) { + av_unused int64_t pts1 = pts_int; + pts_int = av_rescale_q(pts_int, tb, is->video_st->time_base); + av_dlog(NULL, "video_thread(): " + "tb:%d/%d pts:%"PRId64" -> tb:%d/%d pts:%"PRId64"\n", + tb.num, tb.den, pts1, + is->video_st->time_base.num, is->video_st->time_base.den, pts_int); + } + pts = pts_int * av_q2d(is->video_st->time_base); + ret = queue_picture(is, frame, pts, pos); + } +#else + pts = pts_int * av_q2d(is->video_st->time_base); + ret = queue_picture(is, frame, pts, pkt.pos); +#endif if (ret < 0) goto the_end; @@ -1884,10 +1762,12 @@ static int video_thread(void *arg) stream_toggle_pause(is); } the_end: + avcodec_flush_buffers(is->video_st->codec); #if CONFIG_AVFILTER av_freep(&vfilters); avfilter_graph_free(&graph); #endif + av_free_packet(&pkt); av_free(frame); return 0; } @@ -2004,7 +1884,7 @@ static int synchronize_audio(VideoState *is, int nb_samples) avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); if (fabs(avg_diff) >= is->audio_diff_threshold) { - wanted_nb_samples = nb_samples + (int)(diff * is->audio_src_freq); + wanted_nb_samples = nb_samples + (int)(diff * is->audio_src.freq); min_nb_samples = ((nb_samples * (100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100)); max_nb_samples = ((nb_samples * (100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100)); wanted_nb_samples = FFMIN(FFMAX(wanted_nb_samples, min_nb_samples), max_nb_samples); @@ -2047,6 +1927,9 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr) } else avcodec_get_frame_defaults(is->frame); + if (is->paused) + return -1; + if (flush_complete) break; new_packet = 0; @@ -2070,60 +1953,57 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr) is->frame->nb_samples, dec->sample_fmt, 1); - dec_channel_layout = (dec->channel_layout && dec->channels == av_get_channel_layout_nb_channels(dec->channel_layout)) ? dec->channel_layout : av_get_default_channel_layout(dec->channels); + dec_channel_layout = + (dec->channel_layout && dec->channels == av_get_channel_layout_nb_channels(dec->channel_layout)) ? + dec->channel_layout : av_get_default_channel_layout(dec->channels); wanted_nb_samples = synchronize_audio(is, is->frame->nb_samples); - if (dec->sample_fmt != is->audio_src_fmt || - dec_channel_layout != is->audio_src_channel_layout || - dec->sample_rate != is->audio_src_freq || + if (dec->sample_fmt != is->audio_src.fmt || + dec_channel_layout != is->audio_src.channel_layout || + dec->sample_rate != is->audio_src.freq || (wanted_nb_samples != is->frame->nb_samples && !is->swr_ctx)) { - if (is->swr_ctx) - swr_free(&is->swr_ctx); + swr_free(&is->swr_ctx); is->swr_ctx = swr_alloc_set_opts(NULL, - is->audio_tgt_channel_layout, is->audio_tgt_fmt, is->audio_tgt_freq, + is->audio_tgt.channel_layout, is->audio_tgt.fmt, is->audio_tgt.freq, dec_channel_layout, dec->sample_fmt, dec->sample_rate, 0, NULL); if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) { fprintf(stderr, "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n", - dec->sample_rate, - av_get_sample_fmt_name(dec->sample_fmt), - dec->channels, - is->audio_tgt_freq, - av_get_sample_fmt_name(is->audio_tgt_fmt), - is->audio_tgt_channels); + dec->sample_rate, av_get_sample_fmt_name(dec->sample_fmt), dec->channels, + is->audio_tgt.freq, av_get_sample_fmt_name(is->audio_tgt.fmt), is->audio_tgt.channels); break; } - is->audio_src_channel_layout = dec_channel_layout; - is->audio_src_channels = dec->channels; - is->audio_src_freq = dec->sample_rate; - is->audio_src_fmt = dec->sample_fmt; + is->audio_src.channel_layout = dec_channel_layout; + is->audio_src.channels = dec->channels; + is->audio_src.freq = dec->sample_rate; + is->audio_src.fmt = dec->sample_fmt; } - resampled_data_size = data_size; if (is->swr_ctx) { - const uint8_t *in[] = { is->frame->data[0] }; + const uint8_t **in = (const uint8_t **)is->frame->extended_data; uint8_t *out[] = {is->audio_buf2}; + int out_count = sizeof(is->audio_buf2) / is->audio_tgt.channels / av_get_bytes_per_sample(is->audio_tgt.fmt); if (wanted_nb_samples != is->frame->nb_samples) { - if (swr_set_compensation(is->swr_ctx, (wanted_nb_samples - is->frame->nb_samples) * is->audio_tgt_freq / dec->sample_rate, - wanted_nb_samples * is->audio_tgt_freq / dec->sample_rate) < 0) { + if (swr_set_compensation(is->swr_ctx, (wanted_nb_samples - is->frame->nb_samples) * is->audio_tgt.freq / dec->sample_rate, + wanted_nb_samples * is->audio_tgt.freq / dec->sample_rate) < 0) { fprintf(stderr, "swr_set_compensation() failed\n"); break; } } - len2 = swr_convert(is->swr_ctx, out, sizeof(is->audio_buf2) / is->audio_tgt_channels / av_get_bytes_per_sample(is->audio_tgt_fmt), - in, is->frame->nb_samples); + len2 = swr_convert(is->swr_ctx, out, out_count, in, is->frame->nb_samples); if (len2 < 0) { - fprintf(stderr, "audio_resample() failed\n"); + fprintf(stderr, "swr_convert() failed\n"); break; } - if (len2 == sizeof(is->audio_buf2) / is->audio_tgt_channels / av_get_bytes_per_sample(is->audio_tgt_fmt)) { + if (len2 == out_count) { fprintf(stderr, "warning: audio buffer is probably too small\n"); swr_init(is->swr_ctx); } is->audio_buf = is->audio_buf2; - resampled_data_size = len2 * is->audio_tgt_channels * av_get_bytes_per_sample(is->audio_tgt_fmt); + resampled_data_size = len2 * is->audio_tgt.channels * av_get_bytes_per_sample(is->audio_tgt.fmt); } else { is->audio_buf = is->frame->data[0]; + resampled_data_size = data_size; } /* if no pts, then compute it */ @@ -2176,7 +2056,7 @@ static void sdl_audio_callback(void *opaque, Uint8 *stream, int len) VideoState *is = opaque; int audio_size, len1; int bytes_per_sec; - int frame_size = av_samples_get_buffer_size(NULL, is->audio_tgt_channels, 1, is->audio_tgt_fmt, 1); + int frame_size = av_samples_get_buffer_size(NULL, is->audio_tgt.channels, 1, is->audio_tgt.fmt, 1); double pts; audio_callback_time = av_gettime(); @@ -2203,37 +2083,87 @@ static void sdl_audio_callback(void *opaque, Uint8 *stream, int len) stream += len1; is->audio_buf_index += len1; } - bytes_per_sec = is->audio_tgt_freq * is->audio_tgt_channels * av_get_bytes_per_sample(is->audio_tgt_fmt); + bytes_per_sec = is->audio_tgt.freq * is->audio_tgt.channels * av_get_bytes_per_sample(is->audio_tgt.fmt); is->audio_write_buf_size = is->audio_buf_size - is->audio_buf_index; /* Let's assume the audio driver that is used by SDL has two periods. */ is->audio_current_pts = is->audio_clock - (double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / bytes_per_sec; is->audio_current_pts_drift = is->audio_current_pts - audio_callback_time / 1000000.0; } +static int audio_open(void *opaque, int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate, struct AudioParams *audio_hw_params) +{ + SDL_AudioSpec wanted_spec, spec; + const char *env; + const int next_nb_channels[] = {0, 0, 1, 6, 2, 6, 4, 6}; + + env = SDL_getenv("SDL_AUDIO_CHANNELS"); + if (env) { + wanted_nb_channels = SDL_atoi(env); + wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels); + } + if (!wanted_channel_layout || wanted_nb_channels != av_get_channel_layout_nb_channels(wanted_channel_layout)) { + wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels); + wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX; + } + wanted_spec.channels = av_get_channel_layout_nb_channels(wanted_channel_layout); + wanted_spec.freq = wanted_sample_rate; + if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0) { + fprintf(stderr, "Invalid sample rate or channel count!\n"); + return -1; + } + wanted_spec.format = AUDIO_S16SYS; + wanted_spec.silence = 0; + wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; + wanted_spec.callback = sdl_audio_callback; + wanted_spec.userdata = opaque; + while (SDL_OpenAudio(&wanted_spec, &spec) < 0) { + fprintf(stderr, "SDL_OpenAudio (%d channels): %s\n", wanted_spec.channels, SDL_GetError()); + wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)]; + if (!wanted_spec.channels) { + fprintf(stderr, "No more channel combinations to try, audio open failed\n"); + return -1; + } + wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels); + } + if (spec.format != AUDIO_S16SYS) { + fprintf(stderr, "SDL advised audio format %d is not supported!\n", spec.format); + return -1; + } + if (spec.channels != wanted_spec.channels) { + wanted_channel_layout = av_get_default_channel_layout(spec.channels); + if (!wanted_channel_layout) { + fprintf(stderr, "SDL advised channel count %d is not supported!\n", spec.channels); + return -1; + } + } + + audio_hw_params->fmt = AV_SAMPLE_FMT_S16; + audio_hw_params->freq = spec.freq; + audio_hw_params->channel_layout = wanted_channel_layout; + audio_hw_params->channels = spec.channels; + return spec.size; +} + /* open a given stream. Return 0 if OK */ static int stream_component_open(VideoState *is, int stream_index) { AVFormatContext *ic = is->ic; AVCodecContext *avctx; AVCodec *codec; - SDL_AudioSpec wanted_spec, spec; AVDictionary *opts; AVDictionaryEntry *t = NULL; - int64_t wanted_channel_layout = 0; - int wanted_nb_channels; - const char *env; if (stream_index < 0 || stream_index >= ic->nb_streams) return -1; avctx = ic->streams[stream_index]->codec; codec = avcodec_find_decoder(avctx->codec_id); - opts = filter_codec_opts(codec_opts, codec, ic, ic->streams[stream_index]); + opts = filter_codec_opts(codec_opts, avctx->codec_id, ic, ic->streams[stream_index], codec); switch(avctx->codec_type){ - case AVMEDIA_TYPE_AUDIO : if(audio_codec_name ) codec= avcodec_find_decoder_by_name( audio_codec_name); break; - case AVMEDIA_TYPE_SUBTITLE: if(subtitle_codec_name) codec= avcodec_find_decoder_by_name(subtitle_codec_name); break; - case AVMEDIA_TYPE_VIDEO : if(video_codec_name ) codec= avcodec_find_decoder_by_name( video_codec_name); break; + case AVMEDIA_TYPE_AUDIO : is->last_audio_stream = stream_index; if(audio_codec_name ) codec= avcodec_find_decoder_by_name( audio_codec_name); break; + case AVMEDIA_TYPE_SUBTITLE: is->last_subtitle_stream = stream_index; if(subtitle_codec_name) codec= avcodec_find_decoder_by_name(subtitle_codec_name); break; + case AVMEDIA_TYPE_VIDEO : is->last_video_stream = stream_index; if(video_codec_name ) codec= avcodec_find_decoder_by_name( video_codec_name); break; } if (!codec) return -1; @@ -2256,29 +2186,6 @@ static int stream_component_open(VideoState *is, int stream_index) if(codec->capabilities & CODEC_CAP_DR1) avctx->flags |= CODEC_FLAG_EMU_EDGE; - if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) { - memset(&is->audio_pkt_temp, 0, sizeof(is->audio_pkt_temp)); - env = SDL_getenv("SDL_AUDIO_CHANNELS"); - if (env) - wanted_channel_layout = av_get_default_channel_layout(SDL_atoi(env)); - if (!wanted_channel_layout) { - wanted_channel_layout = (avctx->channel_layout && avctx->channels == av_get_channel_layout_nb_channels(avctx->channel_layout)) ? avctx->channel_layout : av_get_default_channel_layout(avctx->channels); - wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX; - wanted_nb_channels = av_get_channel_layout_nb_channels(wanted_channel_layout); - /* SDL only supports 1, 2, 4 or 6 channels at the moment, so we have to make sure not to request anything else. */ - while (wanted_nb_channels > 0 && (wanted_nb_channels == 3 || wanted_nb_channels == 5 || wanted_nb_channels > 6)) { - wanted_nb_channels--; - wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels); - } - } - wanted_spec.channels = av_get_channel_layout_nb_channels(wanted_channel_layout); - wanted_spec.freq = avctx->sample_rate; - if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0) { - fprintf(stderr, "Invalid sample rate or channel count!\n"); - return -1; - } - } - if (!av_dict_get(opts, "threads", NULL, 0)) av_dict_set(&opts, "threads", "auto", 0); if (!codec || @@ -2291,31 +2198,11 @@ static int stream_component_open(VideoState *is, int stream_index) /* prepare audio output */ if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) { - wanted_spec.format = AUDIO_S16SYS; - 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; - if (spec.format != AUDIO_S16SYS) { - fprintf(stderr, "SDL advised audio format %d is not supported!\n", spec.format); + int audio_hw_buf_size = audio_open(is, avctx->channel_layout, avctx->channels, avctx->sample_rate, &is->audio_src); + if (audio_hw_buf_size < 0) return -1; - } - if (spec.channels != wanted_spec.channels) { - wanted_channel_layout = av_get_default_channel_layout(spec.channels); - if (!wanted_channel_layout) { - fprintf(stderr, "SDL advised channel count %d is not supported!\n", spec.channels); - return -1; - } - } - is->audio_src_fmt = is->audio_tgt_fmt = AV_SAMPLE_FMT_S16; - is->audio_src_freq = is->audio_tgt_freq = spec.freq; - is->audio_src_channel_layout = is->audio_tgt_channel_layout = wanted_channel_layout; - is->audio_src_channels = is->audio_tgt_channels = spec.channels; + is->audio_hw_buf_size = audio_hw_buf_size; + is->audio_tgt = is->audio_src; } ic->streams[stream_index]->discard = AVDISCARD_DEFAULT; @@ -2331,23 +2218,24 @@ static int stream_component_open(VideoState *is, int stream_index) 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 / wanted_spec.freq; + is->audio_diff_threshold = 2.0 * is->audio_hw_buf_size / av_samples_get_buffer_size(NULL, is->audio_tgt.channels, is->audio_tgt.freq, is->audio_tgt.fmt, 1); memset(&is->audio_pkt, 0, sizeof(is->audio_pkt)); - packet_queue_init(&is->audioq); + memset(&is->audio_pkt_temp, 0, sizeof(is->audio_pkt_temp)); + packet_queue_start(&is->audioq); SDL_PauseAudio(0); break; case AVMEDIA_TYPE_VIDEO: is->video_stream = stream_index; is->video_st = ic->streams[stream_index]; - packet_queue_init(&is->videoq); + packet_queue_start(&is->videoq); is->video_tid = SDL_CreateThread(video_thread, is); break; case AVMEDIA_TYPE_SUBTITLE: is->subtitle_stream = stream_index; is->subtitle_st = ic->streams[stream_index]; - packet_queue_init(&is->subtitleq); + packet_queue_start(&is->subtitleq); is->subtitle_tid = SDL_CreateThread(subtitle_thread, is); break; @@ -2372,10 +2260,9 @@ static void stream_component_close(VideoState *is, int stream_index) SDL_CloseAudio(); - packet_queue_end(&is->audioq); + packet_queue_flush(&is->audioq); av_free_packet(&is->audio_pkt); - if (is->swr_ctx) - swr_free(&is->swr_ctx); + swr_free(&is->swr_ctx); av_freep(&is->audio_buf1); is->audio_buf = NULL; av_freep(&is->frame); @@ -2398,7 +2285,7 @@ static void stream_component_close(VideoState *is, int stream_index) SDL_WaitThread(is->video_tid, NULL); - packet_queue_end(&is->videoq); + packet_queue_flush(&is->videoq); break; case AVMEDIA_TYPE_SUBTITLE: packet_queue_abort(&is->subtitleq); @@ -2413,7 +2300,7 @@ static void stream_component_close(VideoState *is, int stream_index) SDL_WaitThread(is->subtitle_tid, NULL); - packet_queue_end(&is->subtitleq); + packet_queue_flush(&is->subtitleq); break; default: break; @@ -2421,6 +2308,9 @@ static void stream_component_close(VideoState *is, int stream_index) ic->streams[stream_index]->discard = AVDISCARD_ALL; avcodec_close(avctx); +#if CONFIG_AVFILTER + free_buffer_pool(&is->buffer_pool); +#endif switch (avctx->codec_type) { case AVMEDIA_TYPE_AUDIO: is->audio_st = NULL; @@ -2460,9 +2350,9 @@ static int read_thread(void *arg) int orig_nb_streams; memset(st_index, -1, sizeof(st_index)); - is->video_stream = -1; - is->audio_stream = -1; - is->subtitle_stream = -1; + is->last_video_stream = is->video_stream = -1; + is->last_audio_stream = is->audio_stream = -1; + is->last_subtitle_stream = is->subtitle_stream = -1; ic = avformat_alloc_context(); ic->interrupt_callback.callback = decode_interrupt_cb; @@ -2615,10 +2505,11 @@ static int read_thread(void *arg) } /* if the queue are full, no need to read more */ - if ( is->audioq.size + is->videoq.size + is->subtitleq.size > MAX_QUEUE_SIZE - || ( (is->audioq .nb_packets > MIN_FRAMES || is->audio_stream < 0) - && (is->videoq .nb_packets > MIN_FRAMES || is->video_stream < 0) - && (is->subtitleq.nb_packets > MIN_FRAMES || is->subtitle_stream < 0))) { + if (!infinite_buffer && + (is->audioq.size + is->videoq.size + is->subtitleq.size > MAX_QUEUE_SIZE + || ( (is->audioq .nb_packets > MIN_FRAMES || is->audio_stream < 0 || is->audioq.abort_request) + && (is->videoq .nb_packets > MIN_FRAMES || is->video_stream < 0 || is->videoq.abort_request) + && (is->subtitleq.nb_packets > MIN_FRAMES || is->subtitle_stream < 0 || is->subtitleq.abort_request)))) { /* wait 10 ms */ SDL_Delay(10); continue; @@ -2723,6 +2614,10 @@ static VideoState *stream_open(const char *filename, AVInputFormat *iformat) is->subpq_mutex = SDL_CreateMutex(); is->subpq_cond = SDL_CreateCond(); + packet_queue_init(&is->videoq); + packet_queue_init(&is->audioq); + packet_queue_init(&is->subtitleq); + is->av_sync_type = av_sync_type; is->read_tid = SDL_CreateThread(read_thread, is); if (!is->read_tid) { @@ -2736,16 +2631,19 @@ static void stream_cycle_channel(VideoState *is, int codec_type) { AVFormatContext *ic = is->ic; int start_index, stream_index; + int old_index; AVStream *st; - if (codec_type == AVMEDIA_TYPE_VIDEO) - start_index = is->video_stream; - else if (codec_type == AVMEDIA_TYPE_AUDIO) - start_index = is->audio_stream; - else - start_index = is->subtitle_stream; - if (start_index < (codec_type == AVMEDIA_TYPE_SUBTITLE ? -1 : 0)) - return; + if (codec_type == AVMEDIA_TYPE_VIDEO) { + start_index = is->last_video_stream; + old_index = is->video_stream; + } else if (codec_type == AVMEDIA_TYPE_AUDIO) { + start_index = is->last_audio_stream; + old_index = is->audio_stream; + } else { + start_index = is->last_subtitle_stream; + old_index = is->subtitle_stream; + } stream_index = start_index; for (;;) { if (++stream_index >= is->ic->nb_streams) @@ -2753,9 +2651,12 @@ static void stream_cycle_channel(VideoState *is, int codec_type) if (codec_type == AVMEDIA_TYPE_SUBTITLE) { stream_index = -1; + is->last_subtitle_stream = -1; goto the_end; - } else - stream_index = 0; + } + if (start_index == -1) + return; + stream_index = 0; } if (stream_index == start_index) return; @@ -2777,21 +2678,20 @@ static void stream_cycle_channel(VideoState *is, int codec_type) } } the_end: - stream_component_close(is, start_index); + stream_component_close(is, old_index); stream_component_open(is, stream_index); } static void toggle_full_screen(VideoState *is) { - av_unused int i; - is_full_screen = !is_full_screen; #if defined(__APPLE__) && SDL_VERSION_ATLEAST(1, 2, 14) /* OS X needs to reallocate the SDL overlays */ - for (i = 0; i < VIDEO_PICTURE_QUEUE_SIZE; i++) { + int i; + for (i = 0; i < VIDEO_PICTURE_QUEUE_SIZE; i++) is->pictq[i].reallocate = 1; - } #endif + is_full_screen = !is_full_screen; video_open(is, 1); } @@ -2956,7 +2856,6 @@ static void event_loop(VideoState *cur_stream) do_exit(cur_stream); break; case FF_ALLOC_EVENT: - video_open(event.user.data1, 0); alloc_picture(event.user.data1); break; case FF_REFRESH_EVENT: @@ -3097,6 +2996,7 @@ static const OptionDef options[] = { { "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", "" }, + { "infbuf", OPT_BOOL | OPT_EXPERT, { (void*)&infinite_buffer }, "don't limit the input buffer size (useful with realtime streams)", "" }, { "window_title", OPT_STRING | HAS_ARG, { (void*)&window_title }, "set window title", "window title" }, #if CONFIG_AVFILTER { "vf", OPT_STRING | HAS_ARG, { (void*)&vfilters }, "video filters", "filter list" }, @@ -3171,6 +3071,7 @@ int main(int argc, char **argv) { int flags; VideoState *is; + char dummy_videodriver[] = "SDL_VIDEODRIVER=dummy"; av_log_set_flags(AV_LOG_SKIP_REPEATED); parse_loglevel(argc, argv, options); @@ -3208,6 +3109,8 @@ int main(int argc, char **argv) flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER; if (audio_disable) flags &= ~SDL_INIT_AUDIO; + if (display_disable) + SDL_putenv(dummy_videodriver); /* For the event queue, we always need a video driver. */ #if !defined(__MINGW32__) && !defined(__APPLE__) flags |= SDL_INIT_EVENTTHREAD; /* Not supported on Windows or Mac OS X */ #endif