diff --git a/libavcodec/mpegvideo.c b/libavcodec/mpegvideo.c index 23a5797c7a..c603eeb75e 100644 --- a/libavcodec/mpegvideo.c +++ b/libavcodec/mpegvideo.c @@ -965,6 +965,77 @@ static int free_context_frame(MpegEncContext *s) return 0; } +int ff_MPV_common_frame_size_change(MpegEncContext *s) +{ + int i, err = 0; + + if (s->slice_context_count > 1) { + for (i = 0; i < s->slice_context_count; i++) { + free_duplicate_context(s->thread_context[i]); + } + for (i = 1; i < s->slice_context_count; i++) { + av_freep(&s->thread_context[i]); + } + } else + free_duplicate_context(s); + + free_context_frame(s); + + if (s->picture) + for (i = 0; i < s->picture_count; i++) { + s->picture[i].needs_realloc = 1; + } + + s->last_picture_ptr = + s->next_picture_ptr = + s->current_picture_ptr = NULL; + + // init + if (s->codec_id == AV_CODEC_ID_MPEG2VIDEO && !s->progressive_sequence) + s->mb_height = (s->height + 31) / 32 * 2; + else if (s->codec_id != AV_CODEC_ID_H264) + s->mb_height = (s->height + 15) / 16; + + if ((s->width || s->height) && + av_image_check_size(s->width, s->height, 0, s->avctx)) + return AVERROR_INVALIDDATA; + + if ((err = init_context_frame(s))) + goto fail; + + s->thread_context[0] = s; + + if (s->width && s->height) { + int nb_slices = s->slice_context_count; + if (nb_slices > 1) { + for (i = 1; i < nb_slices; i++) { + s->thread_context[i] = av_malloc(sizeof(MpegEncContext)); + memcpy(s->thread_context[i], s, sizeof(MpegEncContext)); + } + + for (i = 0; i < nb_slices; i++) { + if (init_duplicate_context(s->thread_context[i], s) < 0) + goto fail; + s->thread_context[i]->start_mb_y = + (s->mb_height * (i) + nb_slices / 2) / nb_slices; + s->thread_context[i]->end_mb_y = + (s->mb_height * (i + 1) + nb_slices / 2) / nb_slices; + } + } else { + if (init_duplicate_context(s, s) < 0) + goto fail; + s->start_mb_y = 0; + s->end_mb_y = s->mb_height; + } + s->slice_context_count = nb_slices; + } + + return 0; + fail: + ff_MPV_common_end(s); + return err; +} + /* init common structure for both encoder and decoder */ void ff_MPV_common_end(MpegEncContext *s) { @@ -1124,7 +1195,17 @@ void ff_release_unused_pictures(MpegEncContext*s, int remove_current) } } -int ff_find_unused_picture(MpegEncContext *s, int shared) +static inline int pic_is_unused(MpegEncContext *s, Picture *pic) +{ + if (pic->f.data[0] == NULL) + return 1; + if (pic->needs_realloc) + if (!pic->owner2 || pic->owner2 == s) + return 1; + return 0; +} + +static int find_unused_picture(MpegEncContext *s, int shared) { int i; @@ -1135,11 +1216,11 @@ int ff_find_unused_picture(MpegEncContext *s, int shared) } } else { for (i = s->picture_range_start; i < s->picture_range_end; i++) { - if (s->picture[i].f.data[0] == NULL && s->picture[i].f.type != 0) + if (pic_is_unused(s, &s->picture[i]) && s->picture[i].f.type != 0) return i; // FIXME } for (i = s->picture_range_start; i < s->picture_range_end; i++) { - if (s->picture[i].f.data[0] == NULL) + if (pic_is_unused(s, &s->picture[i])) return i; } } @@ -1161,6 +1242,20 @@ int ff_find_unused_picture(MpegEncContext *s, int shared) return -1; } +int ff_find_unused_picture(MpegEncContext *s, int shared) +{ + int ret = find_unused_picture(s, shared); + + if (ret >= 0 && ret < s->picture_range_end) { + if (s->picture[ret].needs_realloc) { + s->picture[ret].needs_realloc = 0; + free_picture(s, &s->picture[ret]); + avcodec_get_frame_defaults(&s->picture[ret].f); + } + } + return ret; +} + static void update_noise_reduction(MpegEncContext *s) { int intra, i; @@ -1213,7 +1308,7 @@ int ff_MPV_frame_start(MpegEncContext *s, AVCodecContext *avctx) if (s->picture[i].owner2 == s && s->picture[i].f.data[0] && &s->picture[i] != s->last_picture_ptr && &s->picture[i] != s->next_picture_ptr && - s->picture[i].f.reference) { + s->picture[i].f.reference && !s->picture[i].needs_realloc) { if (!(avctx->active_thread_type & FF_THREAD_FRAME)) av_log(avctx, AV_LOG_ERROR, "releasing zombie picture\n"); diff --git a/libavcodec/mpegvideo.h b/libavcodec/mpegvideo.h index 39f23222d3..1b8791cbce 100644 --- a/libavcodec/mpegvideo.h +++ b/libavcodec/mpegvideo.h @@ -141,6 +141,7 @@ typedef struct Picture{ int32_t *mb_cmp_score; ///< Table for MB cmp scores, for mb decision FIXME remove int b_frame_score; /* */ struct MpegEncContext *owner2; ///< pointer to the MpegEncContext that allocated this picture + int needs_realloc; ///< Picture needs to be reallocated (eg due to a frame size change) } Picture; /** @@ -758,6 +759,7 @@ void ff_MPV_common_defaults(MpegEncContext *s); void ff_MPV_decode_defaults(MpegEncContext *s); int ff_MPV_common_init(MpegEncContext *s); +int ff_MPV_common_frame_size_change(MpegEncContext *s); void ff_MPV_common_end(MpegEncContext *s); void ff_MPV_decode_mb(MpegEncContext *s, DCTELEM block[12][64]); int ff_MPV_frame_start(MpegEncContext *s, AVCodecContext *avctx); diff --git a/libavcodec/rv34.c b/libavcodec/rv34.c index 5c19735375..4ca134e208 100644 --- a/libavcodec/rv34.c +++ b/libavcodec/rv34.c @@ -1537,10 +1537,9 @@ int ff_rv34_decode_update_thread_context(AVCodecContext *dst, const AVCodecConte return 0; if (s->height != s1->height || s->width != s1->width) { - ff_MPV_common_end(s); s->height = s1->height; s->width = s1->width; - if ((err = ff_MPV_common_init(s)) < 0) + if ((err = ff_MPV_common_frame_size_change(s)) < 0) return err; if ((err = rv34_decoder_realloc(r)) < 0) return err; @@ -1659,20 +1658,13 @@ int ff_rv34_decode_frame(AVCodecContext *avctx, if (s->width != si.width || s->height != si.height) { int err; - if (HAVE_THREADS && - (s->avctx->active_thread_type & FF_THREAD_FRAME)) { - av_log_missing_feature(s->avctx, "Width/height changing with " - "frame threading is", 0); - return AVERROR_PATCHWELCOME; - } - av_log(s->avctx, AV_LOG_WARNING, "Changing dimensions to %dx%d\n", si.width, si.height); - ff_MPV_common_end(s); + s->width = si.width; s->height = si.height; avcodec_set_dimensions(s->avctx, s->width, s->height); - if ((err = ff_MPV_common_init(s)) < 0) + if ((err = ff_MPV_common_frame_size_change(s)) < 0) return err; if ((err = rv34_decoder_realloc(r)) < 0) return err;