Skip to content

Commit

Permalink
cropping images with different scales to not distort them
Browse files Browse the repository at this point in the history
  • Loading branch information
wandenberg committed Dec 1, 2011
1 parent 7a63df7 commit 7495437
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 6 deletions.
2 changes: 1 addition & 1 deletion config
@@ -1,5 +1,5 @@
ngx_addon_name=ngx_http_video_thumbextractor_module
ngx_feature_libs="-lavformat -lavcodec -lswscale -ljpeg"
ngx_feature_libs="-lavformat -lavcodec -lswscale -ljpeg -lMagickWand"
HTTP_MODULES="$HTTP_MODULES ngx_http_video_thumbextractor_module"
CORE_INCS="$CORE_INCS \
$ngx_addon_dir/src \
Expand Down
78 changes: 73 additions & 5 deletions src/ngx_http_video_thumbextractor_module_utils.c
Expand Up @@ -2,8 +2,10 @@
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <jpeglib.h>
#include <wand/magick_wand.h>

#define NGX_HTTP_VIDEO_THUMBEXTRACTOR_MEMORY_STEP 1024
#define NGX_HTTP_VIDEO_THUMBEXTRACTOR_RGB "RGB"

static uint32_t ngx_http_video_thumbextractor_jpeg_compress(ngx_http_video_thumbextractor_loc_conf_t *cf, uint8_t * buffer, int in_width, int in_height, int out_width, int out_height, caddr_t *out_buffer, size_t *out_len, size_t uncompressed_size, ngx_pool_t *temp_pool);
static void ngx_http_video_thumbextractor_jpeg_memory_dest (j_compress_ptr cinfo, caddr_t *out_buf, size_t *out_size, size_t uncompressed_size, ngx_pool_t *temp_pool);
Expand Down Expand Up @@ -33,6 +35,11 @@ ngx_http_video_thumbextractor_get_thumb(ngx_http_video_thumbextractor_loc_conf_t
uint8_t *buffer = NULL;
AVPacket packet;
size_t uncompressed_size;
float scale = 0.0, new_scale = 0.0, scale_sws = 0.0, scale_w = 0.0, scale_h = 0.0;
int sws_width = 0, sws_height = 0;
ngx_flag_t needs_crop = 0;
MagickWand *m_wand = NULL;
MagickBooleanType mrc;

// Open video file
if ((rc = av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)) != 0) {
Expand Down Expand Up @@ -93,9 +100,32 @@ ngx_http_video_thumbextractor_get_thumb(ngx_http_video_thumbextractor_loc_conf_t
} else if (width == 0) {
// calculate width related with original aspect
width = height * pCodecCtx->width / pCodecCtx->height;
width = ((width + 8) / 16) * 16;
}

if ((width < 16) || (height < 16)) {
ngx_log_error(NGX_LOG_ERR, log, 0, "video thumb extractor module: Very small size requested, %d x %d", width, height);
rc = NGX_ERROR;
goto exit;
}

scale = (float) pCodecCtx->width / pCodecCtx->height;
new_scale = (float) width / height;

sws_width = width;
sws_height = height;

if (scale != new_scale) {
scale_w = (float) width / pCodecCtx->width;
scale_h = (float) height / pCodecCtx->height;
scale_sws = (scale_w > scale_h) ? scale_w : scale_h;

sws_width = pCodecCtx->width * scale_sws + 0.5;
sws_height = pCodecCtx->height * scale_sws + 0.5;

needs_crop = 1;
}


// Allocate video frame
pFrame = avcodec_alloc_frame();

Expand All @@ -108,12 +138,12 @@ ngx_http_video_thumbextractor_get_thumb(ngx_http_video_thumbextractor_loc_conf_t
}

// Determine required buffer size and allocate buffer
uncompressed_size = avpicture_get_size(PIX_FMT_RGB24, width, height) * sizeof(uint8_t);
uncompressed_size = avpicture_get_size(PIX_FMT_RGB24, sws_width, sws_height) * sizeof(uint8_t);
buffer = (uint8_t *) av_malloc(uncompressed_size);

// Assign appropriate parts of buffer to image planes in pFrameRGB
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset of AVPicture
avpicture_fill((AVPicture *) pFrameRGB, buffer, PIX_FMT_RGB24, width, height);
avpicture_fill((AVPicture *) pFrameRGB, buffer, PIX_FMT_RGB24, sws_width, sws_height);

if ((rc = av_seek_frame(pFormatCtx, -1, second * AV_TIME_BASE, 0)) < 0) {
ngx_log_error(NGX_LOG_ERR, log, 0, "video thumb extractor module: Seek to an invalid time, error: %d", rc);
Expand All @@ -131,11 +161,48 @@ ngx_http_video_thumbextractor_get_thumb(ngx_http_video_thumbextractor_loc_conf_t
if (frameFinished) {
// Convert the image from its native format to RGB
struct SwsContext *img_resample_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
width, height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
sws_width, sws_height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);

sws_scale(img_resample_ctx, (const uint8_t * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
sws_scale(img_resample_ctx, (const uint8_t * const *) pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
sws_freeContext(img_resample_ctx);


if (needs_crop) {
MagickWandGenesis();

if ((m_wand = NewMagickWand()) == NULL){
ngx_log_error(NGX_LOG_ERR, log, 0, "video thumb extractor module: Could not alloc MagickWand memory");
rc = NGX_ERROR;
goto exit;
}

mrc = MagickConstituteImage(m_wand, sws_width, sws_height, NGX_HTTP_VIDEO_THUMBEXTRACTOR_RGB, CharPixel, pFrameRGB->data[0]);
if (mrc == MagickTrue) {
mrc = MagickSetImageGravity(m_wand, CenterGravity);
}

if (mrc == MagickTrue) {
mrc = MagickCropImage(m_wand, width, height, (sws_width-width)/2, (sws_height-height)/2);
}

if (mrc == MagickTrue) {
mrc = MagickExportImagePixels(m_wand, 0, 0, width, height, NGX_HTTP_VIDEO_THUMBEXTRACTOR_RGB, CharPixel, pFrameRGB->data[0]);
}

/* Clean up */
if (m_wand) {
m_wand = DestroyMagickWand(m_wand);
}

MagickWandTerminus();

if (mrc == MagickTrue) {
ngx_log_error(NGX_LOG_ERR, log, 0, "video thumb extractor module: Error cropping image");
rc = NGX_ERROR;
goto exit;
}
}

// Compress to jpeg
if (ngx_http_video_thumbextractor_jpeg_compress(cf, pFrameRGB->data[0], pCodecCtx->width, pCodecCtx->height, width, height, out_buffer, out_len, uncompressed_size, temp_pool) == 0) {
rc = NGX_OK;
Expand Down Expand Up @@ -174,6 +241,7 @@ ngx_http_video_thumbextractor_init_libraries(void)
{
// Register all formats and codecs
av_register_all();
av_log_set_level(AV_LOG_ERROR);
}


Expand Down

0 comments on commit 7495437

Please sign in to comment.