Based on a patch by Vittorio Giovara <[email protected]>

Fixes Bug 378.
---
 doc/APIchanges             |    3 ++
 libavcodec/avcodec.h       |    5 ++++
 libavcodec/h264.c          |   70 +++++++++++++++++++++++++++++++-------------
 libavcodec/h264_ps.c       |   14 ++++-----
 libavcodec/options_table.h |    1 +
 5 files changed, 64 insertions(+), 29 deletions(-)

diff --git a/doc/APIchanges b/doc/APIchanges
index 0d317fc..ff5884a 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -13,6 +13,9 @@ libavutil:     2012-10-22
 
 API changes, most recent first:
 
+2013-03-xx - xxxxxxx - lavc 55.0.0 - avcodec.h
+  Add CODEC_FLAG_UNALIGNED to allow decoders to produce unaligned output.
+
 2013-03-xx - Reference counted buffers - lavu 52.8.0, lavc 55.0.0, lavf 55.0.0,
 lavd 54.0.0, lavfi 3.5.0
   xxxxxxx, xxxxxxx - add a new API for reference counted buffers and buffer
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index e48b114..ced7fed 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -609,6 +609,11 @@ typedef struct RcOverride{
    Note: Not everything is supported yet.
 */
 
+/**
+ * Allow decoders to produce frames with data planes that are not aligned
+ * to CPU requirements (e.g. due to cropping).
+ */
+#define CODEC_FLAG_UNALIGNED 0x0001
 #define CODEC_FLAG_QSCALE 0x0002  ///< Use fixed qscale.
 #define CODEC_FLAG_4MV    0x0004  ///< 4 MV per MB allowed / advanced 
prediction for H.263.
 #define CODEC_FLAG_QPEL   0x0010  ///< Use qpel MC.
diff --git a/libavcodec/h264.c b/libavcodec/h264.c
index 3fa6475..1d84a9d 100644
--- a/libavcodec/h264.c
+++ b/libavcodec/h264.c
@@ -1423,9 +1423,6 @@ av_cold int ff_h264_decode_init(AVCodecContext *avctx)
 
     h->avctx = avctx;
 
-    h->width    = h->avctx->width;
-    h->height   = h->avctx->height;
-
     h->bit_depth_luma    = 8;
     h->chroma_format_idc = 1;
 
@@ -2973,11 +2970,30 @@ static int h264_slice_header_init(H264Context *h, int 
reinit)
                     h->avctx->thread_count : 1;
     int i;
 
-    avcodec_set_dimensions(h->avctx, h->width, h->height);
-    h->avctx->sample_aspect_ratio = h->sps.sar;
-    av_assert0(h->avctx->sample_aspect_ratio.den);
     av_pix_fmt_get_chroma_sub_sample(h->avctx->pix_fmt,
                                      &h->chroma_x_shift, &h->chroma_y_shift);
+    h->avctx->coded_width  = h->width;
+    h->avctx->coded_height = h->height;
+    h->avctx->width        = h->width - (2 >> CHROMA444(h)) * 
(h->sps.crop_right +
+                                                               
h->sps.crop_left);
+    h->avctx->height       = h->height - ((h->sps.frame_mbs_only_flag ? 1 : 2) 
<<
+                                          h->chroma_y_shift) *
+                                         (h->sps.crop_top + 
h->sps.crop_bottom);
+
+    if (h->avctx->width <= 0 || h->avctx->height <= 0) {
+        av_log(h->avctx, AV_LOG_ERROR, "Invalid cropped dimensions: %dx%d.\n",
+               h->avctx->width, h->avctx->height);
+        if (h->avctx->err_recognition & AV_EF_EXPLODE)
+            return AVERROR_INVALIDDATA;
+
+        av_log(h->avctx, AV_LOG_WARNING, "Ignoring cropping information.\n");
+        h->sps.crop_bottom = h->sps.crop_top = h->sps.crop_right = 
h->sps.crop_left = 0;
+        h->avctx->width  = h->width;
+        h->avctx->height = h->height;
+    }
+
+    h->avctx->sample_aspect_ratio = h->sps.sar;
+    av_assert0(h->avctx->sample_aspect_ratio.den);
 
     if (h->sps.timing_info_present_flag) {
         int64_t den = h->sps.time_scale;
@@ -3184,17 +3200,8 @@ static int decode_slice_header(H264Context *h, 
H264Context *h0)
 
     h->chroma_y_shift = h->sps.chroma_format_idc <= 1; // 400 uses yuv420p
 
-    h->width = 16 * h->mb_width - (2 >> CHROMA444(h)) * 
FFMIN(h->sps.crop_right, (8 << CHROMA444(h)) - 1);
-    if (h->sps.frame_mbs_only_flag)
-        h->height = 16 * h->mb_height - (1 << h->chroma_y_shift) * 
FFMIN(h->sps.crop_bottom, (16 >> h->chroma_y_shift) - 1);
-    else
-        h->height = 16 * h->mb_height - (2 << h->chroma_y_shift) * 
FFMIN(h->sps.crop_bottom, (16 >> h->chroma_y_shift) - 1);
-
-    if (FFALIGN(h->avctx->width,  16) == h->width &&
-        FFALIGN(h->avctx->height, 16) == h->height) {
-        h->width  = h->avctx->width;
-        h->height = h->avctx->height;
-    }
+    h->width  = 16 * h->mb_width;
+    h->height = 16 * h->mb_height;
 
     if (h->sps.video_signal_type_present_flag) {
         h->avctx->color_range = h->sps.full_range ? AVCOL_RANGE_JPEG
@@ -3209,8 +3216,8 @@ static int decode_slice_header(H264Context *h, 
H264Context *h0)
     }
 
     if (h->context_initialized &&
-        (h->width  != h->avctx->width   ||
-         h->height != h->avctx->height  ||
+        (h->width  != h->avctx->coded_width   ||
+         h->height != h->avctx->coded_height  ||
          needs_reinit)) {
 
         if (h != h0) {
@@ -4597,6 +4604,25 @@ static int get_consumed_bytes(int pos, int buf_size)
     return pos;
 }
 
+static int output_frame(H264Context *h, AVFrame *dst, AVFrame *src)
+{
+    int i;
+    int ret = av_frame_ref(dst, src);
+    if (ret < 0)
+        return ret;
+
+    for (i = 0; i < 3; i++) {
+        int step_x = (2 >> CHROMA444(h));
+        int step_y = (h->sps.frame_mbs_only_flag ? 1 : 2) << h->chroma_y_shift;
+        int hshift = (i > 0) ? h->chroma_x_shift : 0;
+        int vshift = (i > 0) ? h->chroma_y_shift : 0;
+        int off    = (((step_x * h->sps.crop_left) >> hshift) << 
h->pixel_shift) +
+                      ((step_y * h->sps.crop_top) >> vshift) * 
dst->linesize[i];
+        dst->data[i] += off;
+    }
+    return 0;
+}
+
 static int decode_frame(AVCodecContext *avctx, void *data,
                         int *got_frame, AVPacket *avpkt)
 {
@@ -4634,7 +4660,8 @@ out:
             h->delayed_pic[i] = h->delayed_pic[i + 1];
 
         if (out) {
-            if ((ret = av_frame_ref(pict, &out->f)) < 0)
+            ret = output_frame(h, pict, &out->f);
+            if (ret < 0)
                 return ret;
             *got_frame = 1;
         }
@@ -4669,7 +4696,8 @@ out:
             /* Wait for second field. */
             *got_frame = 0;
         } else {
-            if ((ret = av_frame_ref(pict, &h->next_output_pic->f)) < 0)
+            ret = output_frame(h, pict, &h->next_output_pic->f);
+            if (ret < 0)
                 return ret;
             *got_frame = 1;
         }
diff --git a/libavcodec/h264_ps.c b/libavcodec/h264_ps.c
index 6c895a4..8ec7ccf 100644
--- a/libavcodec/h264_ps.c
+++ b/libavcodec/h264_ps.c
@@ -413,8 +413,6 @@ int ff_h264_decode_seq_parameter_set(H264Context *h){
 #endif
     sps->crop= get_bits1(&h->gb);
     if(sps->crop){
-        int crop_vertical_limit   = sps->chroma_format_idc  & 2 ? 16 : 8;
-        int crop_horizontal_limit = sps->chroma_format_idc == 3 ? 16 : 8;
         sps->crop_left  = get_ue_golomb(&h->gb);
         sps->crop_right = get_ue_golomb(&h->gb);
         sps->crop_top   = get_ue_golomb(&h->gb);
@@ -432,12 +430,12 @@ int ff_h264_decode_seq_parameter_set(H264Context *h){
             sps->crop_right  =
             sps->crop_top    =
             sps->crop_bottom = 0;
-        }
-        if(sps->crop_left || sps->crop_top){
-            av_log(h->avctx, AV_LOG_ERROR, "insane cropping not completely 
supported, this could look slightly wrong ...\n");
-        }
-        if(sps->crop_right >= crop_horizontal_limit || sps->crop_bottom >= 
crop_vertical_limit){
-            av_log(h->avctx, AV_LOG_ERROR, "brainfart cropping not supported, 
this could look slightly wrong ...\n");
+        } else if (sps->crop_left && !(h->avctx->flags & 
CODEC_FLAG_UNALIGNED)) {
+            if (sps->crop_left & (0x1F >> (sps->bit_depth_luma > 8))) {
+                sps->crop_left &= ~(0x1F >> (sps->bit_depth_luma > 8));
+                av_log(h->avctx, AV_LOG_WARNING, "Reducing left cropping to %d 
"
+                       "to preserve alignment.\n", sps->crop_left);
+            }
         }
     }else{
         sps->crop_left  =
diff --git a/libavcodec/options_table.h b/libavcodec/options_table.h
index e1b124b..b5889f8 100644
--- a/libavcodec/options_table.h
+++ b/libavcodec/options_table.h
@@ -46,6 +46,7 @@ static const AVOption options[]={
        "to minimum/maximum bitrate. Lowering tolerance too much has an adverse 
effect on quality.",
        OFFSET(bit_rate_tolerance), AV_OPT_TYPE_INT, {.i64 = 
AV_CODEC_DEFAULT_BITRATE*20 }, 1, INT_MAX, V|E},
 {"flags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, 
UINT_MAX, V|A|E|D, "flags"},
+{"unaligned", "allow decoders to produce unaligned output", 0, 
AV_OPT_TYPE_CONST, { .i64 = CODEC_FLAG_UNALIGNED }, INT_MIN, INT_MAX, V | E, 
"flags" },
 {"mv4", "use four motion vectors per macroblock (MPEG-4)", 0, 
AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG_4MV }, INT_MIN, INT_MAX, V|E, "flags"},
 {"qpel", "use 1/4-pel motion compensation", 0, AV_OPT_TYPE_CONST, {.i64 = 
CODEC_FLAG_QPEL }, INT_MIN, INT_MAX, V|E, "flags"},
 {"loop", "use loop filter", 0, AV_OPT_TYPE_CONST, {.i64 = 
CODEC_FLAG_LOOP_FILTER }, INT_MIN, INT_MAX, V|E, "flags"},
-- 
1.7.10.4

_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to