diff options
Diffstat (limited to 'tinyDAV/src/video/tdav_converter_video.cxx')
-rw-r--r-- | tinyDAV/src/video/tdav_converter_video.cxx | 832 |
1 files changed, 832 insertions, 0 deletions
diff --git a/tinyDAV/src/video/tdav_converter_video.cxx b/tinyDAV/src/video/tdav_converter_video.cxx new file mode 100644 index 0000000..2195d79 --- /dev/null +++ b/tinyDAV/src/video/tdav_converter_video.cxx @@ -0,0 +1,832 @@ +/* +* Copyright (C) 2010-2015 Mamadou DIOP. +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +/**@file tdav_converter_video.c +* @brief Video converter. +* +* @author Mamadou Diop <diopmamadou(at)doubango.org> +* @author Alex Vishnev (Added support for rotation) +*/ +#include "tinydav/video/tdav_converter_video.h" + +#include "tsk_memory.h" +#include "tsk_debug.h" + +// FIXME: FFmpeg implementation do not support "scale_rotated_frames" option + +#if HAVE_LIBYUV + +#include <libyuv/libyuv.h> + +using namespace libyuv; + +typedef struct tdav_converter_video_libyuv_s +{ + TMEDIA_DECLARE_CONVERTER_VIDEO; + + enum FourCC srcFormat; + enum FourCC dstFormat; + + tsk_bool_t toI420; + tsk_bool_t fromI420; + + struct{ + uint8* ptr; + int size; + }chroma; + struct{ + uint8* ptr; + int size; + }rotate; + struct{ + uint8* ptr; + int size; + }scale; + struct{ + uint8* ptr; + int size; + }mirror; +} +tdav_converter_video_libyuv_t; + +#define TDAV_CONVERTER_VIDEO_LIBYUV(self) ((tdav_converter_video_libyuv_t*)(self)) +#define LIBYUV_INPUT_BUFFER_PADDING_SIZE 32 + +static inline tsk_bool_t _tdav_converter_video_libyuv_is_chroma_varsize(tmedia_chroma_t chroma) +{ + return chroma == tmedia_chroma_mjpeg; +} + +static inline tsk_size_t _tdav_converter_video_libyuv_get_size(tmedia_chroma_t chroma, tsk_size_t w, tsk_size_t h) +{ + switch (chroma){ + case tmedia_chroma_rgb24: + case tmedia_chroma_bgr24: + return (w * h * 3); + case tmedia_chroma_rgb565le: + return ((w * h) << 1); + case tmedia_chroma_rgb32: + return ((w * h) << 2); + case tmedia_chroma_nv21: + return ((w * h * 3) >> 1); + case tmedia_chroma_nv12: + return ((w * h * 3) >> 1); + case tmedia_chroma_yuv422p: + return ((w * h) << 1); + case tmedia_chroma_uyvy422: + case tmedia_chroma_yuyv422: + return ((w * h) << 1); + case tmedia_chroma_yuv420p: + return ((w * h * 3) >> 1); + case tmedia_chroma_mjpeg: + return 0; + default: + TSK_DEBUG_ERROR("Invalid chroma %d", (int)chroma); + return 0; + } +} + +static inline enum FourCC _tdav_converter_video_libyuv_get_pixfmt(tmedia_chroma_t chroma) +{ + switch (chroma){ + case tmedia_chroma_rgb24: + case tmedia_chroma_bgr24: + return FOURCC_24BG; + case tmedia_chroma_rgb565le: + return FOURCC_RGBP; + case tmedia_chroma_rgb32: + return FOURCC_ARGB; + case tmedia_chroma_nv21: + return FOURCC_NV21; + case tmedia_chroma_nv12: + return FOURCC_NV12; + case tmedia_chroma_yuv422p: + return FOURCC_I422; + case tmedia_chroma_uyvy422: + return FOURCC_UYVY; + case tmedia_chroma_yuyv422: + return FOURCC_YUY2; + case tmedia_chroma_yuv420p: + return FOURCC_I420; + case tmedia_chroma_mjpeg: + return FOURCC_MJPG; + default: + TSK_DEBUG_ERROR("Invalid chroma %d", (int)chroma); + return FOURCC_ANY; + } +} + +static int tdav_converter_video_libyuv_init(tmedia_converter_video_t* self, tsk_size_t srcWidth, tsk_size_t srcHeight, tmedia_chroma_t srcChroma, tsk_size_t dstWidth, tsk_size_t dstHeight, tmedia_chroma_t dstChroma) +{ + TSK_DEBUG_INFO("Initializing new LibYUV Video Converter src=(%dx%d@%d) dst=(%dx%d@%d)", (int)srcWidth, (int)srcHeight, (int)srcChroma, (int)dstWidth, (int)dstHeight, (int)dstChroma); + + if ((TDAV_CONVERTER_VIDEO_LIBYUV(self)->srcFormat = _tdav_converter_video_libyuv_get_pixfmt(srcChroma)) == FOURCC_ANY){ + TSK_DEBUG_ERROR("Invalid source chroma"); + return -2; + } + if ((TDAV_CONVERTER_VIDEO_LIBYUV(self)->dstFormat = _tdav_converter_video_libyuv_get_pixfmt(dstChroma)) == FOURCC_ANY){ + TSK_DEBUG_ERROR("Invalid destination chroma"); + return -3; + } + + TDAV_CONVERTER_VIDEO_LIBYUV(self)->toI420 = (TDAV_CONVERTER_VIDEO_LIBYUV(self)->dstFormat == FOURCC_I420); + TDAV_CONVERTER_VIDEO_LIBYUV(self)->fromI420 = (TDAV_CONVERTER_VIDEO_LIBYUV(self)->srcFormat == FOURCC_I420); + if (!TDAV_CONVERTER_VIDEO_LIBYUV(self)->toI420 && !TDAV_CONVERTER_VIDEO_LIBYUV(self)->fromI420) + { + TSK_DEBUG_ERROR("LIBYUV only support from/to I420"); + return -1; + } + return 0; +} + +static tsk_size_t tdav_converter_video_libyuv_process(tmedia_converter_video_t* _self, const void* buffer, tsk_size_t buffer_size, void** output, tsk_size_t* output_max_size) +{ +#define RESIZE_BUFFER(buff, curr_size, new_size) \ + if((int)(curr_size) < (new_size)){ \ + if(!((buff) = (uint8*)tsk_realloc((buff), (new_size)))){ \ + (curr_size) = 0; \ + TSK_DEBUG_ERROR("Failed to allocate buffer"); \ + return 0; \ + } \ + (curr_size) = (new_size); \ + } + static const int crop_x = 0; + static const int crop_y = 0; + + int ret; + tdav_converter_video_libyuv_t* self = TDAV_CONVERTER_VIDEO_LIBYUV(_self); + tsk_bool_t scale = ((_self->dstWidth != _self->srcWidth) || (_self->dstHeight != _self->srcHeight)); + int s, ls, src_y_stride, src_u_stride, src_v_stride, dst_y_stride, dst_u_stride, dst_v_stride; + int src_w, src_h, dst_w, dst_h; + uint8 *dst_y, *dst_u, *dst_v, *src_y, *src_u, *src_v; + + RotationMode rotation = kRotate0; + + switch (_self->rotation){ + case 90: rotation = kRotate90; break; + case 180: rotation = kRotate180; break; + case 270: rotation = kRotate270; break; + } + //rotation = kRotate0; + + // not square and rotaion=270/90 -> requires scaling unless disabled + if ((rotation == kRotate90 || rotation == kRotate270) && _self->scale_rotated_frames){ + scale |= (_self->dstWidth != _self->dstHeight) && (rotation == kRotate90 || rotation == kRotate270); + } + + src_w = (int)_self->srcWidth, src_h = (int)_self->srcHeight; + + if (self->toI420) { + tsk_size_t x_in_size; + // check input size + x_in_size = _tdav_converter_video_libyuv_is_chroma_varsize(_self->srcChroma) ? buffer_size : _tdav_converter_video_libyuv_get_size(_self->srcChroma, src_w, src_h); + if (x_in_size > buffer_size) { // Ignore any extra data. For example, "CVPixelBufferGetDataSize()" will return size padded with 8 extra bytes for RGB32. + TSK_DEBUG_ERROR("Invalid input size: %u>%u", (unsigned)x_in_size, (unsigned)buffer_size); + return 0; + } + + dst_w = src_w, dst_h = src_h; // because no scaling when converting to I420 + ls = src_w * src_h; + s = ((ls * 3) >> 1); + if (scale || rotation != kRotate0){ + RESIZE_BUFFER(self->chroma.ptr, self->chroma.size, s); + dst_y = self->chroma.ptr; + } + else{ + RESIZE_BUFFER((*output), (*output_max_size), s); + dst_y = (uint8*)*output; + } + dst_u = (dst_y + ls); + dst_v = dst_u + (ls >> 2); + src_y_stride = dst_y_stride = src_w; + src_u_stride = src_v_stride = dst_u_stride = dst_v_stride = ((dst_y_stride + 1) >> 1); + + // convert to I420 without scaling or rotation + ret = ConvertToI420( + (const uint8*)buffer, (int)x_in_size, + dst_y, dst_y_stride, + dst_u, dst_u_stride, + dst_v, dst_v_stride, + crop_x, crop_y, + (int)_self->srcWidth, (int)(_self->flip ? (_self->srcHeight * -1) : _self->srcHeight), // vertical flip + (int)_self->srcWidth, (int)_self->srcHeight, + kRotate0, + (uint32)self->srcFormat); + // mirror: horizontal flip (front camera video) + if (_self->mirror) { + RESIZE_BUFFER(self->mirror.ptr, self->mirror.size, s); + ret = I420Mirror( + dst_y, dst_y_stride, + dst_u, dst_u_stride, + dst_v, dst_v_stride, + self->mirror.ptr, dst_y_stride, + (self->mirror.ptr + ls), dst_u_stride, + (self->mirror.ptr + ls + (ls >> 2)), dst_v_stride, + (int)_self->srcWidth, (int)_self->srcHeight); + memcpy(dst_y, self->mirror.ptr, s); + } + + if (ret){ + TSK_DEBUG_ERROR("ConvertToI420 failed with error code = %d, in_size:%u", ret, x_in_size); + return 0; + } + + // rotate + if (rotation != kRotate0){ + dst_w = (int)((rotation == kRotate90 || rotation == kRotate270) ? _self->srcHeight : _self->srcWidth); + dst_h = (int)((rotation == kRotate90 || rotation == kRotate270) ? _self->srcWidth : _self->srcHeight); + + src_y = dst_y, src_u = dst_u, src_v = dst_v; + src_y_stride = src_y_stride, src_u_stride = src_u_stride, src_v_stride = src_v_stride; + dst_y_stride = dst_w; + dst_u_stride = dst_v_stride = ((dst_y_stride + 1) >> 1); + + if (scale){ + RESIZE_BUFFER(self->rotate.ptr, self->rotate.size, s); + dst_y = self->rotate.ptr; + } + else{// last step + RESIZE_BUFFER((*output), (*output_max_size), s); + dst_y = (uint8*)*output; + } + + dst_u = (dst_y + ls); + dst_v = dst_u + (ls >> 2); + ret = I420Rotate( + src_y, src_y_stride, + src_u, src_u_stride, + src_v, src_v_stride, + dst_y, dst_y_stride, + dst_u, dst_u_stride, + dst_v, dst_v_stride, + (int)_self->srcWidth, (int)_self->srcHeight, rotation); + if (ret){ + TSK_DEBUG_ERROR("I420Rotate failed with error code = %d", ret); + return 0; + } + + // scale to fit ratio, pad, crop then copy + if ((rotation == kRotate90 || rotation == kRotate270) && _self->scale_rotated_frames){ + int iwidth = (int)_self->srcHeight; + int iheight = (int)_self->srcWidth; + + src_y = dst_y, src_u = dst_u, src_v = dst_v; + src_w = dst_w, src_h = dst_h; + src_y_stride = dst_y_stride, src_u_stride = dst_u_stride, src_v_stride = dst_v_stride; + + if (_self->dstWidth != _self->dstHeight) { + if (iwidth * _self->srcHeight > iheight * _self->srcWidth) { + iwidth = (int)((iheight * _self->srcWidth / _self->srcHeight) & ~1); + int iwidth_offset = (int)((_self->srcHeight - iwidth) >> 1); + src_y += iwidth_offset; + src_u += iwidth_offset >> 1; + src_v += iwidth_offset >> 1; + } + else if (iwidth * _self->srcHeight < iheight * _self->srcWidth) { + iheight = (int)(iwidth * _self->srcHeight / _self->srcWidth); + int iheight_offset = (int)((_self->srcWidth - iheight) >> 2); + iheight_offset <<= 1; + src_y += iheight_offset * src_y_stride; + src_u += (iheight_offset >> 1) * src_u_stride; + src_v += (iheight_offset >> 1) * src_v_stride; + } + + src_w = iwidth, src_h = iheight; + src_y_stride = src_w; + src_u_stride = src_v_stride = ((src_y_stride + 1) >> 1); + + dst_w = (int)_self->dstWidth; + dst_h = (int)_self->dstHeight; + ls = dst_w * dst_h; + s = ((ls * 3) >> 1); + RESIZE_BUFFER((*output), (*output_max_size), s); + dst_y_stride = dst_w; + dst_u_stride = dst_v_stride = ((dst_y_stride + 1) >> 1); + uint8* dst_y = (uint8*)*output; + uint8* dst_u = (dst_y + ls); + uint8* dst_v = dst_u + (ls >> 2); + + ret = I420Scale( + src_y, src_y_stride, + src_u, src_u_stride, + src_v, src_v_stride, + src_w, src_h, + dst_y, dst_y_stride, + dst_u, dst_u_stride, + dst_v, dst_v_stride, + dst_w, dst_h, + kFilterBox); + if (ret){ + TSK_DEBUG_ERROR("I420Scale failed with error code = %d", ret); + return 0; + } + return s; + } + } + } + + // scale + if (scale){ + src_w = dst_w, src_h = dst_h; + dst_w = (int)(((rotation == kRotate90 || rotation == kRotate270) && !_self->scale_rotated_frames) ? _self->dstHeight : _self->dstWidth); + dst_h = (int)(((rotation == kRotate90 || rotation == kRotate270) && !_self->scale_rotated_frames) ? _self->dstWidth : _self->dstHeight); + src_y = dst_y, src_u = dst_u, src_v = dst_v; + src_y_stride = dst_y_stride, src_u_stride = dst_u_stride, src_v_stride = dst_v_stride; + dst_y_stride = dst_w; + dst_u_stride = dst_v_stride = ((dst_y_stride + 1) >> 1); + + ls = dst_w * dst_h; + s = ((ls * 3) >> 1); + RESIZE_BUFFER((*output), (*output_max_size), s); + dst_y = (uint8*)*output; + dst_u = (dst_y + ls); + dst_v = dst_u + (ls >> 2); + + ret = I420Scale( + src_y, src_y_stride, + src_u, src_u_stride, + src_v, src_v_stride, + src_w, src_h, + dst_y, dst_y_stride, + dst_u, dst_u_stride, + dst_v, dst_v_stride, + dst_w, dst_h, + kFilterNone); + if (ret){ + TSK_DEBUG_ERROR("I420Scale failed with error code = %d", ret); + return 0; + } + } + + return ((dst_w * dst_h * 3) >> 1); + } + else if (self->fromI420){ + static const int dst_sample_stride = 0; + + dst_w = (int)_self->dstWidth, dst_h = (int)_self->dstHeight; + src_y = (uint8*)buffer; + src_u = (src_y + (src_w * src_h)); + src_v = (src_u + ((src_w * src_h) >> 2)); + src_y_stride = src_w; + src_u_stride = src_v_stride = ((src_y_stride + 1) >> 1); + + // mirror: horizontal flip (front camera video) + if ((_self->mirror)) { + ls = src_w * src_h; + s = ((ls * 3) >> 1); + if (s < (int)buffer_size) { // security check + RESIZE_BUFFER(self->mirror.ptr, self->mirror.size, s); + ret = I420Mirror( + src_y, src_y_stride, + src_u, src_u_stride, + src_v, src_v_stride, + self->mirror.ptr, src_y_stride, + (self->mirror.ptr + ls), src_u_stride, + (self->mirror.ptr + ls + (ls >> 2)), src_v_stride, + src_w, src_h); + memcpy(src_y, self->mirror.ptr, s); + } + } + + if (scale){ + ls = dst_w * dst_h; + s = ((ls * 3) >> 1); + + RESIZE_BUFFER(self->scale.ptr, self->scale.size, s); + dst_y = self->scale.ptr; + dst_u = (dst_y + (dst_w * dst_h)); + dst_v = (dst_u + ((dst_w * dst_h) >> 2)); + dst_y_stride = dst_w; + dst_u_stride = dst_v_stride = ((dst_y_stride + 1) >> 1); + + ret = I420Scale( + src_y, src_y_stride, + src_u, src_u_stride, + src_v, src_v_stride, + src_w, src_h, + dst_y, dst_y_stride, + dst_u, dst_u_stride, + dst_v, dst_v_stride, + dst_w, dst_h, + kFilterNone); + + if (ret){ + TSK_DEBUG_ERROR("I420Scale failed with error code = %d", ret); + return 0; + } + + src_y = dst_y; + src_u = (dst_y + ls); + src_v = (dst_u + (ls >> 2)); + src_y_stride = dst_y_stride; + src_u_stride = src_v_stride = ((src_y_stride + 1) >> 1); + } + + s = (int)_tdav_converter_video_libyuv_get_size(_self->dstChroma, _self->srcWidth, _self->srcHeight); + RESIZE_BUFFER((*output), (*output_max_size), s); + + ret = ConvertFromI420( + src_y, src_y_stride, + src_u, src_u_stride, + src_v, src_v_stride, + (uint8*)*output, dst_sample_stride, + (int)_self->dstWidth, (_self->flip ? ((int)_self->dstHeight * -1) : (int)_self->dstHeight), // vertical flip + (uint32)self->dstFormat); + if (ret){ + TSK_DEBUG_ERROR("ConvertFromI420 failed with error code = %d", ret); + return 0; + } + + return s; + } + + // Must be from/to I420 + TSK_DEBUG_ERROR("Not expected code called"); + return 0; +} + +static tsk_object_t* tdav_converter_video_libyuv_ctor(tsk_object_t * self, va_list * app) +{ + tdav_converter_video_libyuv_t *converter = (tdav_converter_video_libyuv_t *)self; + if (converter){ + + } + return self; +} +static tsk_object_t* tdav_converter_video_libyuv_dtor(tsk_object_t * self) +{ + tdav_converter_video_libyuv_t *converter = (tdav_converter_video_libyuv_t *)self; + if (converter){ + TSK_FREE(converter->chroma.ptr); + TSK_FREE(converter->rotate.ptr); + TSK_FREE(converter->scale.ptr); + TSK_FREE(converter->mirror.ptr); + } + + return self; +} +static const tsk_object_def_t tdav_converter_video_libyuv_def_s = +{ + sizeof(tdav_converter_video_libyuv_t), + tdav_converter_video_libyuv_ctor, + tdav_converter_video_libyuv_dtor, + tsk_null, +}; +const tsk_object_def_t *tdav_converter_video_libyuv_def_t = &tdav_converter_video_libyuv_def_s; +static const tmedia_converter_video_plugin_def_t tdav_converter_video_libyuv_plugin_def_s = +{ + &tdav_converter_video_libyuv_def_s, + + tdav_converter_video_libyuv_init, + tdav_converter_video_libyuv_process +}; +const tmedia_converter_video_plugin_def_t *tdav_converter_video_libyuv_plugin_def_t = &tdav_converter_video_libyuv_plugin_def_s; + +#endif /* HAVE_LIBYUV */ + +#if HAVE_FFMPEG || HAVE_SWSSCALE + +#ifndef INT64_C +# define INT64_C(c) (c ## LL) +# define UINT64_C(c) (c ## ULL) +#endif + +#ifdef __cplusplus +extern "C" { +#endif +#include <libswscale/swscale.h> +#include <libavcodec/avcodec.h> +#ifdef __cplusplus +} +#endif + +typedef struct tdav_converter_video_ffmpeg_s +{ + TMEDIA_DECLARE_CONVERTER_VIDEO; + + struct SwsContext *context; + + enum PixelFormat srcFormat; + enum PixelFormat dstFormat; + + AVFrame* srcFrame; + AVFrame* dstFrame; + + struct { + struct SwsContext *context; + AVFrame* frame; + uint8_t* buffer; + tsk_size_t buffer_size; + } rot; +} +tdav_converter_video_ffmpeg_t; + +#define TDAV_CONVERTER_VIDEO_FFMPEG(self) ((tdav_converter_video_ffmpeg_t*)(self)) + +// use macro for performance reasons keep (called (15x3) times per seconds) +#define _tdav_converter_video_ffmpeg_rotate90(srcw, srch, srcdata, dstdata) \ +{ \ + register int i,j; \ + register int newx = 0; \ + for (i = 0; i < (int)(srcw); i ++ ){ \ + for( j = (int)srch-1; j >=0; j -- ){ \ + (dstdata)[newx++] = (srcdata)[j * (srcw) + i]; \ + } \ + } \ +} + +#define _tdav_converter_video_ffmpeg_flip(frame, height) \ + frame->data[0] += frame->linesize[0] * (height -1); \ + frame->data[1] += frame->linesize[1] * ((height -1)>>1); \ + frame->data[2] += frame->linesize[2] * ((height -1)>>1); \ + \ + frame->linesize[0] *= -1; \ + frame->linesize[1] *= -1; \ + frame->linesize[2] *= -1; + + +static inline enum PixelFormat _tdav_converter_video_ffmpeg_get_pixfmt(tmedia_chroma_t chroma) +{ + switch(chroma){ + case tmedia_chroma_rgb24: + return PIX_FMT_RGB24; + case tmedia_chroma_bgr24: + return PIX_FMT_BGR24; + case tmedia_chroma_rgb32: + return PIX_FMT_RGB32; + case tmedia_chroma_rgb565le: + return PIX_FMT_RGB565LE; + case tmedia_chroma_rgb565be: + return PIX_FMT_RGB565BE; + case tmedia_chroma_nv21: + return PIX_FMT_NV21; + case tmedia_chroma_nv12: + return PIX_FMT_NV12; + case tmedia_chroma_yuv422p: + return PIX_FMT_YUV422P; + case tmedia_chroma_uyvy422: + return PIX_FMT_UYVY422; + case tmedia_chroma_yuyv422: + return PIX_FMT_YUYV422; + case tmedia_chroma_yuv420p: + return PIX_FMT_YUV420P; + default: + TSK_DEBUG_ERROR("Invalid chroma %d", (int)chroma); + return PIX_FMT_NONE; + } +} + + +static int tdav_converter_video_ffmpeg_init(tmedia_converter_video_t* self, tsk_size_t srcWidth, tsk_size_t srcHeight, tmedia_chroma_t srcChroma, tsk_size_t dstWidth, tsk_size_t dstHeight, tmedia_chroma_t dstChroma) +{ + TSK_DEBUG_INFO("Initializing new FFmpeg Video Converter src=(%dx%d@%d) dst=(%dx%d@%d)", srcWidth, srcHeight, srcChroma, dstWidth, dstHeight, dstChroma); + + if((TDAV_CONVERTER_VIDEO_FFMPEG(self)->srcFormat = _tdav_converter_video_ffmpeg_get_pixfmt(srcChroma)) == PIX_FMT_NONE){ + TSK_DEBUG_ERROR("Invalid source chroma"); + return -2; + } + if((TDAV_CONVERTER_VIDEO_FFMPEG(self)->dstFormat = _tdav_converter_video_ffmpeg_get_pixfmt(dstChroma)) == PIX_FMT_NONE){ + TSK_DEBUG_ERROR("Invalid destination chroma"); + return -3; + } + + return 0; +} + +static tsk_size_t tdav_converter_video_ffmpeg_process(tmedia_converter_video_t* _self, const void* buffer, tsk_size_t buffer_size, void** output, tsk_size_t* output_max_size) +{ + int ret, size; + tsk_bool_t _rotate = tsk_false; + tdav_converter_video_ffmpeg_t* self = TDAV_CONVERTER_VIDEO_FFMPEG(_self); + + if (!self || !buffer || !output){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + + /* Pictures */ + if (!self->srcFrame){ + if (!(self->srcFrame = avcodec_alloc_frame())){ + TSK_DEBUG_ERROR("Failed to create picture"); + return 0; + } + } + if (!self->dstFrame){ + if (!(self->dstFrame = avcodec_alloc_frame())){ + TSK_DEBUG_ERROR("Failed to create picture"); + return 0; + } + } + + size = avpicture_get_size(self->dstFormat, (int)_self->dstWidth, (int)_self->dstHeight); + if ((int)*output_max_size < size){ + if (!(*output = tsk_realloc(*output, (size + FF_INPUT_BUFFER_PADDING_SIZE)))){ + *output_max_size = 0; + TSK_DEBUG_ERROR("Failed to allocate buffer"); + return 0; + } + *output_max_size = size; + } + + /* Wrap the source buffer */ + ret = avpicture_fill((AVPicture *)self->srcFrame, (uint8_t*)buffer, self->srcFormat, (int)_self->srcWidth, (int)_self->srcHeight); + /* Wrap the destination buffer */ + ret = avpicture_fill((AVPicture *)self->dstFrame, (uint8_t*)*output, self->dstFormat, (int)_self->dstWidth, (int)_self->dstHeight); + + /* === performs conversion === */ + /* Context */ + if (!self->context) { + self->context = sws_getContext( + (int)_self->srcWidth, (int)_self->srcHeight, self->srcFormat, + (int)_self->dstWidth, (int)_self->dstHeight, self->dstFormat, + SWS_FAST_BILINEAR, NULL, NULL, NULL); + + if (!self->context) { + TSK_DEBUG_ERROR("Failed to create context"); + return 0; + } + } + + /*FIXME: For now only 90\B0 rotation is supported this is why we always use libyuv on mobile devices */ + _rotate = (PIX_FMT_YUV420P == self->dstFormat) && _self->rotation == 90; + + // if no rotation then, flip while scaling othersize do it after rotation + if (!_rotate && _self->flip) { + _tdav_converter_video_ffmpeg_flip(self->dstFrame, _self->dstHeight); + } + + // chroma conversion and scaling + ret = sws_scale(self->context, (const uint8_t* const*)self->srcFrame->data, self->srcFrame->linesize, 0, (int)_self->srcHeight, + self->dstFrame->data, self->dstFrame->linesize); + if (ret < 0){ + TSK_FREE(*output); + return 0; + } + + // Rotation + if (_rotate){ + // because we rotated 90 width = original height, height = original width + int w = (int)_self->dstHeight; + int h = (int)_self->dstWidth; + + // allocation rotation frame if not already done + if (!(self->rot.frame) && !(self->rot.frame = avcodec_alloc_frame())){ + TSK_DEBUG_ERROR("failed to allocate rotation frame"); + TSK_FREE(*output); + return(0); + } + + // allocate rotation temporary buffer + size = avpicture_get_size(self->dstFormat, w, h); + if (self->rot.buffer_size != size){ + if (!(self->rot.buffer = (uint8_t *)av_realloc(self->rot.buffer, size))){ + TSK_DEBUG_ERROR("failed to allocate new buffer for the frame"); + self->rot.buffer_size = 0; + return(0); + } + self->rot.buffer_size = size; + } + + //wrap + avpicture_fill((AVPicture *)self->rot.frame, self->rot.buffer, self->dstFormat, w, h); + // rotate + _tdav_converter_video_ffmpeg_rotate90(_self->dstWidth, _self->dstHeight, self->dstFrame->data[0], self->rot.frame->data[0]); + _tdav_converter_video_ffmpeg_rotate90((_self->dstWidth >> 1), (_self->dstHeight >> 1), self->dstFrame->data[1], self->rot.frame->data[1]); + _tdav_converter_video_ffmpeg_rotate90((_self->dstWidth >> 1), (_self->dstHeight >> 1), self->dstFrame->data[2], self->rot.frame->data[2]); + // flip + if (_self->flip){ + _tdav_converter_video_ffmpeg_flip(self->rot.frame, h); + } + + { + static const int y_shift = 1; + static const int x_shift = 1; + int r_size, r_w, r_h, left_band, top_band; + int pad = ((int)_self->dstWidth - w) > ((int)_self->dstHeight - h) ? ((int)_self->dstWidth - w) : ((int)_self->dstHeight - h); + if (pad < 0){ + pad = 0; + } + r_size; + r_w = w + pad; + r_h = h + pad; + left_band = (int)((r_w - _self->dstWidth) / 2); + top_band = (int)((r_h - _self->dstHeight) / 3); + + if (!self->rot.context){ + if (!(self->rot.context = sws_getContext(w, h, self->dstFormat, r_w, r_h, self->dstFormat, SWS_FAST_BILINEAR, NULL, NULL, NULL))){ + TSK_DEBUG_ERROR("Failed to create context"); + TSK_FREE(*output); + return 0; + } + } + + r_size = avpicture_get_size(self->dstFormat, r_w, r_h); + if ((int)*output_max_size < r_size){ + if (!(*output = tsk_realloc(*output, (r_size + FF_INPUT_BUFFER_PADDING_SIZE)))){ + *output_max_size = 0; + TSK_DEBUG_ERROR("Failed to allocate buffer"); + return 0; + } + *output_max_size = r_size; + } + + // re-wrap + avpicture_fill((AVPicture *)self->dstFrame, (uint8_t*)*output, self->dstFormat, r_w, r_h); + + // pad + sws_scale(self->rot.context, (const uint8_t* const*)self->rot.frame->data, self->rot.frame->linesize, + 0, h, self->dstFrame->data, self->dstFrame->linesize); + + // crop + self->dstFrame->data[0] = self->dstFrame->data[0] + (top_band * self->dstFrame->linesize[0]) + left_band; + self->dstFrame->data[1] = self->dstFrame->data[1] + ((top_band >> y_shift) * self->dstFrame->linesize[1]) + (left_band >> x_shift); + self->dstFrame->data[2] = self->dstFrame->data[2] + ((top_band >> y_shift) * self->dstFrame->linesize[2]) + (left_band >> x_shift); + + avpicture_layout((const AVPicture*)self->dstFrame, self->dstFormat, (int)_self->dstWidth, (int)_self->dstHeight, (unsigned char *)*output, (int)*output_max_size); + } + + }//end of rotation + + return size; +} + + + +//================================================================================================= +// Video Converter object definition +// +static tsk_object_t* tdav_converter_video_ffmpeg_ctor(tsk_object_t * self, va_list * app) +{ + tdav_converter_video_ffmpeg_t *converter = (tdav_converter_video_ffmpeg_t *)self; + if(converter){ + + } + return self; +} + +static tsk_object_t* tdav_converter_video_ffmpeg_dtor(tsk_object_t * self) +{ + tdav_converter_video_ffmpeg_t *converter = (tdav_converter_video_ffmpeg_t *)self; + if(converter){ + if(converter->context){ + sws_freeContext(converter->context); + } + if(converter->srcFrame){ + av_free(converter->srcFrame); + } + if(converter->dstFrame){ + av_free(converter->dstFrame); + } + + // Rotation + if(converter->rot.context){ + sws_freeContext(converter->rot.context); + } + if(converter->rot.frame){ + av_free(converter->rot.frame); + } + if(converter->rot.buffer){ + av_free(converter->rot.buffer); + } + } + + return self; +} + +static const tsk_object_def_t tdav_converter_video_ffmpeg_def_s = +{ + sizeof(tdav_converter_video_ffmpeg_t), + tdav_converter_video_ffmpeg_ctor, + tdav_converter_video_ffmpeg_dtor, + tsk_null, +}; +const tsk_object_def_t *tdav_converter_video_ffmpeg_def_t = &tdav_converter_video_ffmpeg_def_s; + +/* plugin definition*/ +static const tmedia_converter_video_plugin_def_t tdav_converter_video_ffmpeg_plugin_def_s = +{ + &tdav_converter_video_ffmpeg_def_s, + + tdav_converter_video_ffmpeg_init, + tdav_converter_video_ffmpeg_process +}; +const tmedia_converter_video_plugin_def_t *tdav_converter_video_ffmpeg_plugin_def_t = &tdav_converter_video_ffmpeg_plugin_def_s; + +#endif /* HAVE_FFMPEG || HAVE_SWSSCALE */ + |