$OpenBSD: patch-ffmpeg_frame_c,v 1.3 2009/08/17 01:40:30 jolan Exp $
--- ffmpeg_frame.c.orig	Mon Oct 13 18:02:01 2008
+++ ffmpeg_frame.c	Sun Aug 16 20:01:01 2009
@@ -31,7 +31,7 @@
    obligated to do so. If you do not wish to do so, delete this exception
    statement from your version.
 
- */
+*/
 
 #include "php.h"
 #include "php_ini.h"
@@ -51,7 +51,7 @@
    include gd header from local include dir. This is a copy of gd.h that is 
    distributed with php-5.2.5. It is distributed along with ffmpeg-php to
    allow ffmpeg-php to be built without access to the php sources
- */
+   */
 #if HAVE_LIBGD20
 #include "gd.h" 
 
@@ -64,7 +64,7 @@
 #define gdImageBoundsSafeMacro(im, x, y) (!((((y) < (im)->cy1) || ((y) > (im)->cy2)) || (((x) < (im)->cx1) || ((x) > (im)->cx2))))
 
 static int le_gd; // this is only valid after calling 
-                  // FFMPEG_PHP_FETCH_IMAGE_RESOURCE() 
+// FFMPEG_PHP_FETCH_IMAGE_RESOURCE() 
 
 #endif // HAVE_LIBGD20
 
@@ -72,39 +72,40 @@ int le_ffmpeg_frame; // not static since it is used in
 
 static zend_class_entry *ffmpeg_frame_class_entry_ptr;
 zend_class_entry ffmpeg_frame_class_entry;
- 
+
 /* {{{ ffmpeg_frame methods[]
-    Methods of the ffmpeg_frame class 
-*/
+   Methods of the ffmpeg_frame class 
+   */
 zend_function_entry ffmpeg_frame_class_methods[] = {
-    
+
     /* object can't be created from user space so no PHP constructor */
     //PHP_ME(ffmpeg_frame, __construct, NULL, 0)
-  
+
 #if HAVE_LIBGD20
     /* gd methods */
-    PHP_MALIAS(ffmpeg_frame, togdimage,      toGDImage,     NULL, 0)
+    FFMPEG_PHP_MALIAS(ffmpeg_frame, togdimage,      toGDImage,     NULL, 0)
 #endif // HAVE_LIBGD20
 
-    /* methods */
-    PHP_MALIAS(ffmpeg_frame, getwidth,                    getWidth,                   NULL, 0)
-    PHP_MALIAS(ffmpeg_frame, getheight,                   getHeight,                  NULL, 0)
-    PHP_MALIAS(ffmpeg_frame, iskeyframe,                  isKeyFrame,                 NULL, 0)
-    PHP_MALIAS(ffmpeg_frame, getpresentationtimestamp,    getPresentationTimestamp,   NULL, 0)
-    PHP_MALIAS(ffmpeg_frame, getpts,                      getPresentationTimestamp,   NULL, 0)
-	{NULL, NULL, NULL}
+        /* methods */
+        FFMPEG_PHP_MALIAS(ffmpeg_frame, getwidth,                    getWidth,                   NULL, 0)
+        FFMPEG_PHP_MALIAS(ffmpeg_frame, getheight,                   getHeight,                  NULL, 0)
+        FFMPEG_PHP_MALIAS(ffmpeg_frame, resize,                      resize,                     NULL, 0)
+        FFMPEG_PHP_MALIAS(ffmpeg_frame, iskeyframe,                  isKeyFrame,                 NULL, 0)
+        FFMPEG_PHP_MALIAS(ffmpeg_frame, getpresentationtimestamp,    getPresentationTimestamp,   NULL, 0)
+        FFMPEG_PHP_MALIAS(ffmpeg_frame, getpts,                      getPresentationTimestamp,   NULL, 0)
+        FFMPEG_PHP_END_METHODS
 };
 /* }}} */
 
 
 /* {{{ _php_alloc_ff_frame()
- */
+*/
 static ff_frame_context* _php_alloc_ff_frame()
 {
     ff_frame_context *ff_frame = NULL;
 
     ff_frame = emalloc(sizeof(ff_frame_context));
-    
+
     if (!ff_frame) {
         zend_error(E_ERROR, "Error allocating ffmpeg_frame");
     }
@@ -123,16 +124,16 @@ static ff_frame_context* _php_alloc_ff_frame()
    creates an ffmpeg_frame object, adds a ffmpeg_frame resource to the
    object, registers the resource and returns a direct pointer to the 
    resource.
- */
+   */
 ff_frame_context* _php_create_ffmpeg_frame(INTERNAL_FUNCTION_PARAMETERS)
 {
     int ret;
-	ff_frame_context *ff_frame;
-    
+    ff_frame_context *ff_frame;
+
     ff_frame = _php_alloc_ff_frame();
-    
-	ret = ZEND_REGISTER_RESOURCE(NULL, ff_frame, le_ffmpeg_frame);
-    
+
+    ret = ZEND_REGISTER_RESOURCE(NULL, ff_frame, le_ffmpeg_frame);
+
     object_init_ex(return_value, ffmpeg_frame_class_entry_ptr);
     add_property_resource(return_value, "ffmpeg_frame", ret);
     return ff_frame;
@@ -141,7 +142,7 @@ ff_frame_context* _php_create_ffmpeg_frame(INTERNAL_FU
 
 
 /* {{{ _php_free_av_frame()
- */
+*/
 static void _php_free_av_frame(AVFrame *av_frame)
 {
     if (av_frame) {
@@ -156,7 +157,7 @@ static void _php_free_av_frame(AVFrame *av_frame)
 
 
 /* {{{ _php_free_ffmpeg_frame()
- */
+*/
 static void _php_free_ffmpeg_frame(zend_rsrc_list_entry *rsrc TSRMLS_DC)
 {
     ff_frame_context *ff_frame = (ff_frame_context*)rsrc->ptr;    
@@ -167,7 +168,7 @@ static void _php_free_ffmpeg_frame(zend_rsrc_list_entr
 
 
 /* {{{ register_ffmpeg_frame_class()
- */
+*/
 void register_ffmpeg_frame_class(int module_number)
 {
     TSRMLS_FETCH();
@@ -187,36 +188,33 @@ void register_ffmpeg_frame_class(int module_number)
 
 /* {{{ _php_convert_frame()
 */
-int _php_convert_frame(ff_frame_context *ff_frame_ctx, int dst_fmt) {
-    AVFrame *src_frame;
+int _php_convert_frame(ff_frame_context *ff_frame, int dst_fmt) {
     AVFrame *dst_frame;
     int result = 0;
 
-    if (!ff_frame_ctx->av_frame) {
+    if (!ff_frame->av_frame) {
         return -1;
     }
 
-    src_frame = ff_frame_ctx->av_frame;
+    if (dst_fmt == ff_frame->pixel_format) {
+        return 0; // NOP
+    }
 
     dst_frame = avcodec_alloc_frame();
-    avpicture_alloc((AVPicture*)dst_frame, dst_fmt, ff_frame_ctx->width,
-            ff_frame_ctx->height);
+    avpicture_alloc((AVPicture*)dst_frame, dst_fmt, ff_frame->width,
+            ff_frame->height);
 
-    result = ffmpeg_img_convert((AVPicture*)dst_frame, dst_fmt, 
-                (AVPicture *)src_frame, 
-                ff_frame_ctx->pixel_format, ff_frame_ctx->width, 
-                ff_frame_ctx->height);
+    result = phpimg_convert( (AVPicture *)dst_frame, dst_fmt,
+            (AVPicture *) ff_frame->av_frame, ff_frame->pixel_format, ff_frame->width, ff_frame->height);
 
     if (result) {
         zend_error(E_ERROR, "Error converting frame");
-        goto fail;
+        _php_free_av_frame(dst_frame);
+    } else {
+        ff_frame->av_frame = dst_frame;
+        ff_frame->pixel_format = dst_fmt;
     }
 
-    ff_frame_ctx->av_frame = dst_frame;
-    ff_frame_ctx->pixel_format = dst_fmt;
-
-fail:
-    _php_free_av_frame(src_frame);
     return result;
 }
 /* }}} */
@@ -224,7 +222,7 @@ fail:
 #if HAVE_LIBGD20
 
 /* {{{ _php_get_gd_image()
- */
+*/
 static int _php_get_gd_image(int w, int h)
 {
     zval *function_name, *width, *height;
@@ -234,7 +232,7 @@ static int _php_get_gd_image(int w, int h)
     char *function_cname = "imagecreatetruecolor";
     int ret;
     TSRMLS_FETCH();
-   
+
     if (zend_hash_find(EG(function_table), function_cname, 
                 strlen(function_cname) + 1, (void **)&func) == FAILURE) {
         zend_error(E_ERROR, "Error can't find %s function", function_cname);
@@ -255,11 +253,11 @@ static int _php_get_gd_image(int w, int h)
                 &retval, 2, argv, 0, NULL TSRMLS_CC) == FAILURE) {
         zend_error(E_ERROR, "Error calling %s function", function_cname);
     }
-    
+
     FREE_ZVAL(function_name); 
     FREE_ZVAL(width); 
     FREE_ZVAL(height); 
-    
+
     if (!retval || retval->type != IS_RESOURCE) {
         php_error_docref(NULL TSRMLS_CC, E_ERROR,
                 "Error creating GD Image");
@@ -277,7 +275,7 @@ static int _php_get_gd_image(int w, int h)
 
 
 /* {{{ _php_avframe_to_gd_image()
- */
+*/
 static int _php_avframe_to_gd_image(AVFrame *frame, gdImage *dest, int width, 
         int height)
 {
@@ -286,13 +284,8 @@ static int _php_avframe_to_gd_image(AVFrame *frame, gd
 
     for (y = 0; y < height; y++) {
         for (x = 0; x < width; x++) {
-		
-			if (gdImageBoundsSafeMacro(dest, x, y)) {
-                /* copy pixel to gdimage buffer zeroing the alpha channel */
-                dest->tpixels[y][x] = src[x] & 0x00ffffff;
-            } else {
-                return -1;
-            }
+            /* copy pixel to gdimage buffer zeroing the alpha channel */
+            dest->tpixels[y][x] = src[x] & 0x00ffffff;
         }
         src += width;
     }
@@ -302,7 +295,7 @@ static int _php_avframe_to_gd_image(AVFrame *frame, gd
 
 
 /* {{{ _php_gd_image_to_avframe()
- */
+*/
 static int _php_gd_image_to_avframe(gdImage *src, AVFrame *frame, int width, 
         int height) 
 {
@@ -325,7 +318,7 @@ static int _php_gd_image_to_avframe(gdImage *src, AVFr
 
 
 /* {{{ proto resource toGDImage()
- */
+*/
 FFMPEG_PHP_METHOD(ffmpeg_frame, toGDImage)
 {
     ff_frame_context *ff_frame;
@@ -333,7 +326,7 @@ FFMPEG_PHP_METHOD(ffmpeg_frame, toGDImage)
 
     GET_FRAME_RESOURCE(getThis(), ff_frame);
 
-    _php_convert_frame(ff_frame, PIX_FMT_RGBA32);
+    _php_convert_frame(ff_frame, PIX_FMT_RGB32);
 
     return_value->value.lval = _php_get_gd_image(ff_frame->width, 
             ff_frame->height);
@@ -343,36 +336,15 @@ FFMPEG_PHP_METHOD(ffmpeg_frame, toGDImage)
     FFMPEG_PHP_FETCH_IMAGE_RESOURCE(gd_img, &return_value);
 
     if (_php_avframe_to_gd_image(ff_frame->av_frame, gd_img,
-            ff_frame->width, ff_frame->height)) {
+                ff_frame->width, ff_frame->height)) {
         zend_error(E_ERROR, "failed to convert frame to gd image");
     }
 }
 /* }}} */
 
 
-/* {{{ proto object _php_read_frame_from_file(mixed)
- */
-/*_php_read_frame_from_file(ff_frame_context *ff_frame, char* filename)
-{
-    AVFrame *frame = NULL;
-    AVFormatContext *ic;
-    AVFormatParameters *ap;
-    int err;
-
-    // open the input file with generic libav function
-    err = av_open_input_file(&ic, filename, NULL, 0, ap);
-    if (err < 0) {
-        zend_error(E_ERROR, "Can't open image file %d, %d", err, 
-        AVERROR_NOFMT);
-    }
-
-    
-}*/
-/* }}} */
-
-
 /* {{{ proto object ffmpeg_frame(mixed)
- */
+*/
 FFMPEG_PHP_METHOD(ffmpeg_frame, ffmpeg_frame)
 {
     zval **argv[1];
@@ -392,17 +364,17 @@ FFMPEG_PHP_METHOD(ffmpeg_frame, ffmpeg_frame)
     }
 
     ff_frame = _php_alloc_ff_frame();
-    
-	ret = ZEND_REGISTER_RESOURCE(NULL, ff_frame, le_ffmpeg_frame);
-    
+
+    ret = ZEND_REGISTER_RESOURCE(NULL, ff_frame, le_ffmpeg_frame);
+
     object_init_ex(getThis(), ffmpeg_frame_class_entry_ptr);
     add_property_resource(getThis(), "ffmpeg_frame", ret);
-    
+
     switch (Z_TYPE_PP(argv[0])) {
         case IS_STRING:
             convert_to_string_ex(argv[0]);
             zend_error(E_ERROR, 
-                  "Creating an ffmpeg_frame from a file is not implemented\n");
+                    "Creating an ffmpeg_frame from a file is not implemented\n");
             //_php_read_frame_from_file(ff_frame, Z_STRVAL_PP(argv[0]));
             break;
         case IS_RESOURCE:
@@ -418,18 +390,18 @@ FFMPEG_PHP_METHOD(ffmpeg_frame, ffmpeg_frame)
 
             /* create a an av_frame and allocate space for it */
             frame = avcodec_alloc_frame();
-            avpicture_alloc((AVPicture*)frame, PIX_FMT_RGBA32, width, height);
+            avpicture_alloc((AVPicture*)frame, PIX_FMT_RGB32, width, height);
 
             /* copy the gd image to the av_frame */
             _php_gd_image_to_avframe(gd_img, frame, width, height);
-            
+
             /* set the ffmepg_frame to point to this av_frame */
             ff_frame->av_frame = frame;
-            
+
             /* set the ffpmeg_frame's properties */
             ff_frame->width = width;
             ff_frame->height = height;
-            ff_frame->pixel_format = PIX_FMT_RGBA32;
+            ff_frame->pixel_format = PIX_FMT_RGB32;
             break;
         default:
             zend_error(E_ERROR, "Invalid argument\n");
@@ -441,53 +413,298 @@ FFMPEG_PHP_METHOD(ffmpeg_frame, ffmpeg_frame)
 
 
 /* {{{ proto int getPresentationTimestamp()
- */
+*/
 FFMPEG_PHP_METHOD(ffmpeg_frame, getPresentationTimestamp)
 {
     ff_frame_context *ff_frame;
 
     GET_FRAME_RESOURCE(getThis(), ff_frame);
-    
+
     RETURN_DOUBLE((double)ff_frame->pts / AV_TIME_BASE);
 }
 /* }}} */
 
 
 /* {{{ proto int isKeyFrame()
- */
+*/
 FFMPEG_PHP_METHOD(ffmpeg_frame, isKeyFrame)
 {
     ff_frame_context *ff_frame;
 
     GET_FRAME_RESOURCE(getThis(), ff_frame);
-    
+
     RETURN_LONG(ff_frame->keyframe);
 }
 /* }}} */
 
 
 /* {{{ proto int getWidth()
- */
+*/
 FFMPEG_PHP_METHOD(ffmpeg_frame, getWidth)
 {
     ff_frame_context *ff_frame;
 
     GET_FRAME_RESOURCE(getThis(), ff_frame);
-    
+
     RETURN_LONG(ff_frame->width);
 }
 /* }}} */
 
 
 /* {{{ proto int getHeight()
- */
+*/
 FFMPEG_PHP_METHOD(ffmpeg_frame, getHeight)
 {
     ff_frame_context *ff_frame;
 
     GET_FRAME_RESOURCE(getThis(), ff_frame);
-    
+
     RETURN_LONG(ff_frame->height);
+}
+/* }}} */
+
+
+/* {{{ _php_resample_frame()
+*/
+int _php_resample_frame(ff_frame_context *ff_frame,
+        int wanted_width, int wanted_height, int crop_top, int crop_bottom,
+        int crop_left, int crop_right)
+{
+    AVFrame *resampled_frame;
+    phpImgReSampleContext *img_resample_ctx = NULL;
+
+    if (!ff_frame->av_frame) {
+        return -1;
+    }
+
+    /* 
+     * do nothing if width and height are the same as the frame and no 
+     * cropping was specified
+     * */
+    if (wanted_width == ff_frame->width && 
+            wanted_height == ff_frame->height &&
+            (!crop_left && !crop_right && !crop_top && !crop_bottom)) {
+        return 0;
+    }
+
+    /* convert to PIX_FMT_YUV420P required for resampling */
+    _php_convert_frame(ff_frame, PIX_FMT_YUV420P);
+
+    img_resample_ctx = phpimg_resample_full_init(
+            wanted_width, wanted_height,
+            ff_frame->width, ff_frame->height,
+            crop_top, crop_bottom, crop_left, crop_right,
+            0, 0, 0, 0);
+    if (!img_resample_ctx) {
+        return -1;
+    }
+
+    resampled_frame = avcodec_alloc_frame();
+    avpicture_alloc((AVPicture*)resampled_frame, PIX_FMT_YUV420P, 
+            wanted_width, wanted_height);
+
+    phpimg_resample(img_resample_ctx, (AVPicture*)resampled_frame, 
+            (AVPicture*)ff_frame->av_frame);
+
+    _php_free_av_frame(ff_frame->av_frame);
+
+    phpimg_resample_close(img_resample_ctx);
+
+    ff_frame->av_frame = resampled_frame;
+    ff_frame->width = wanted_width;
+    ff_frame->height = wanted_height;
+
+    return 0;
+}
+/* }}} */
+
+
+/* {{{ proto boolean resize(int width, int height [, int crop_top [, int crop_bottom [, int crop_left [, int crop_right ]]]])
+*/
+FFMPEG_PHP_METHOD(ffmpeg_frame, resize)
+{
+    zval ***argv;
+    ff_frame_context *ff_frame = NULL;
+    int wanted_width = 0, wanted_height = 0;
+    int crop_top = 0, crop_bottom = 0, crop_left = 0, crop_right = 0;
+
+    GET_FRAME_RESOURCE(getThis(), ff_frame);
+
+    /* retrieve arguments */ 
+    argv = (zval ***) safe_emalloc(sizeof(zval **), ZEND_NUM_ARGS(), 0);
+
+    if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), argv) != SUCCESS) {
+        efree(argv);
+        php_error_docref(NULL TSRMLS_CC, E_ERROR,
+                "Error parsing arguments");
+    }
+
+    switch (ZEND_NUM_ARGS()) {
+        case 6:
+            convert_to_long_ex(argv[5]);
+            crop_right = Z_LVAL_PP(argv[5]);
+
+            /* crop right must be even number for lavc cropping */
+            if (crop_right % 2) {
+                php_error_docref(NULL TSRMLS_CC, E_ERROR,
+                        "Crop right must be an even number");
+            }
+            /* fallthru */
+        case 5:
+            convert_to_long_ex(argv[4]);
+            crop_left = Z_LVAL_PP(argv[4]);
+
+            /*  crop left must be even number for lavc cropping */
+            if (crop_left % 2) {
+                php_error_docref(NULL TSRMLS_CC, E_ERROR,
+                        "Crop left must be an even number");
+            }
+
+            /* fallthru */
+        case 4:
+            convert_to_long_ex(argv[3]);
+            crop_bottom = Z_LVAL_PP(argv[3]);
+
+            /*  crop bottom must be even number for lavc cropping */
+            if (crop_bottom % 2) {
+                php_error_docref(NULL TSRMLS_CC, E_ERROR,
+                        "Crop bottom must be an even number");
+            }
+
+            /* fallthru */
+        case 3:
+            convert_to_long_ex(argv[2]);
+            crop_top = Z_LVAL_PP(argv[2]);
+
+            /*  crop top must be even number for lavc cropping */
+            if (crop_top % 2) {
+                php_error_docref(NULL TSRMLS_CC, E_ERROR,
+                        "Crop top must be an even number");
+            }
+
+            /* fallthru */
+        case 2:
+            /* height arg */
+            convert_to_long_ex(argv[1]);
+            wanted_height = Z_LVAL_PP(argv[1]);
+
+            /* bounds check wanted height */
+            if (wanted_height < 1) {
+                php_error_docref(NULL TSRMLS_CC, E_ERROR,
+                        "Frame height must be greater than zero");
+            }
+
+            /* wanted height must be even number for lavc resample */
+            if (wanted_height % 2) {
+                php_error_docref(NULL TSRMLS_CC, E_ERROR,
+                        "Frame height must be an even number");
+            }
+            /* fallthru */
+        case 1:
+            /* width arg */
+            convert_to_long_ex(argv[0]);
+            wanted_width = Z_LVAL_PP(argv[0]);
+
+            /* bounds check wanted width */
+            if (wanted_width < 1) {
+                php_error_docref(NULL TSRMLS_CC, E_ERROR,
+                        "Frame width must be greater than zero");
+            }
+
+            /* wanted width must be even number for lavc resample */
+            if (wanted_width % 2) {
+                php_error_docref(NULL TSRMLS_CC, E_ERROR,
+                        "Frame width must be an even number");
+            }
+            break;
+        default:
+            WRONG_PARAM_COUNT;
+    } 
+
+    efree(argv);
+
+    /* resize frame */
+    _php_resample_frame(ff_frame, wanted_width, wanted_height, 
+            crop_top, crop_bottom, crop_left, crop_right);
+
+    RETURN_TRUE;
+}
+/* }}} */
+
+
+/* {{{ proto boolean crop([, int crop_top [, int crop_bottom [, int crop_left [, int crop_right ]]]])
+*/
+PHP_FUNCTION(crop)
+{
+    zval ***argv;
+    ff_frame_context *ff_frame;
+    int crop_top = 0, crop_bottom = 0, crop_left = 0, crop_right = 0;
+
+    GET_FRAME_RESOURCE(getThis(), ff_frame);
+
+    /* retrieve arguments */ 
+    argv = (zval ***) safe_emalloc(sizeof(zval **), ZEND_NUM_ARGS(), 0);
+
+    if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), argv) != SUCCESS) {
+        efree(argv);
+        php_error_docref(NULL TSRMLS_CC, E_ERROR,
+                "Error parsing arguments");
+    }
+
+    switch (ZEND_NUM_ARGS()) {
+        case 4:
+            convert_to_long_ex(argv[3]);
+            crop_right = Z_LVAL_PP(argv[3]);
+
+            /* crop right must be even number for lavc cropping */
+            if (crop_right % 2) {
+                php_error_docref(NULL TSRMLS_CC, E_ERROR,
+                        "Crop right must be an even number");
+            }
+            /* fallthru */
+        case 3:
+            convert_to_long_ex(argv[2]);
+            crop_left = Z_LVAL_PP(argv[2]);
+
+            /*  crop left must be even number for lavc cropping */
+            if (crop_left % 2) {
+                php_error_docref(NULL TSRMLS_CC, E_ERROR,
+                        "Crop left must be an even number");
+            }
+
+            /* fallthru */
+        case 2:
+            convert_to_long_ex(argv[1]);
+            crop_bottom = Z_LVAL_PP(argv[1]);
+
+            /*  crop bottom must be even number for lavc cropping */
+            if (crop_bottom % 2) {
+                php_error_docref(NULL TSRMLS_CC, E_ERROR,
+                        "Crop bottom must be an even number");
+            }
+
+            /* fallthru */
+        case 1:
+            convert_to_long_ex(argv[0]);
+            crop_top = Z_LVAL_PP(argv[0]);
+
+            /*  crop top  must be even number for lavc cropping */
+            if (crop_top % 2) {
+                php_error_docref(NULL TSRMLS_CC, E_ERROR,
+                        "Crop top must be an even number");
+            }
+            break;
+        default:
+            WRONG_PARAM_COUNT;
+    } 
+
+    efree(argv);
+
+    /* resample with same dimensions */
+    _php_resample_frame(ff_frame, ff_frame->width, ff_frame->height, 
+            crop_top, crop_bottom, crop_left, crop_right);
+
+    RETURN_TRUE;
 }
 /* }}} */
 
