blob: f74b12699eb8b3c2420449aa7dd699ff539108c3 [file] [log] [blame]
/* $Id$ */
/*
* Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com)
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjmedia/converter.h>
#include <pj/errno.h>
#if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL
#include "ffmpeg_util.h"
#include <libswscale/swscale.h>
static pj_status_t factory_create_converter(pjmedia_converter_factory *cf,
pj_pool_t *pool,
const pjmedia_conversion_param*prm,
pjmedia_converter **p_cv);
static void factory_destroy_factory(pjmedia_converter_factory *cf);
static pj_status_t libswscale_conv_convert(pjmedia_converter *converter,
pjmedia_frame *src_frame,
pjmedia_frame *dst_frame);
static void libswscale_conv_destroy(pjmedia_converter *converter);
struct fmt_info
{
const pjmedia_video_format_info *fmt_info;
pjmedia_video_apply_fmt_param apply_param;
};
struct ffmpeg_converter
{
pjmedia_converter base;
struct SwsContext *sws_ctx;
struct fmt_info src,
dst;
};
static pjmedia_converter_factory_op libswscale_factory_op =
{
&factory_create_converter,
&factory_destroy_factory
};
static pjmedia_converter_op liswscale_converter_op =
{
&libswscale_conv_convert,
&libswscale_conv_destroy
};
static pj_status_t factory_create_converter(pjmedia_converter_factory *cf,
pj_pool_t *pool,
const pjmedia_conversion_param *prm,
pjmedia_converter **p_cv)
{
enum PixelFormat srcFormat, dstFormat;
const pjmedia_video_format_detail *src_detail, *dst_detail;
const pjmedia_video_format_info *src_fmt_info, *dst_fmt_info;
struct SwsContext *sws_ctx;
struct ffmpeg_converter *fcv;
pj_status_t status;
PJ_UNUSED_ARG(cf);
/* Only supports video */
if (prm->src.type != PJMEDIA_TYPE_VIDEO ||
prm->dst.type != prm->src.type ||
prm->src.detail_type != PJMEDIA_FORMAT_DETAIL_VIDEO ||
prm->dst.detail_type != prm->src.detail_type)
{
return PJ_ENOTSUP;
}
/* lookup source format info */
src_fmt_info = pjmedia_get_video_format_info(
pjmedia_video_format_mgr_instance(),
prm->src.id);
if (!src_fmt_info)
return PJ_ENOTSUP;
/* lookup destination format info */
dst_fmt_info = pjmedia_get_video_format_info(
pjmedia_video_format_mgr_instance(),
prm->dst.id);
if (!dst_fmt_info)
return PJ_ENOTSUP;
src_detail = pjmedia_format_get_video_format_detail(&prm->src, PJ_TRUE);
dst_detail = pjmedia_format_get_video_format_detail(&prm->dst, PJ_TRUE);
status = pjmedia_format_id_to_PixelFormat(prm->src.id, &srcFormat);
if (status != PJ_SUCCESS)
return PJ_ENOTSUP;
status = pjmedia_format_id_to_PixelFormat(prm->dst.id, &dstFormat);
if (status != PJ_SUCCESS)
return PJ_ENOTSUP;
sws_ctx = sws_getContext(src_detail->size.w, src_detail->size.h, srcFormat,
dst_detail->size.w, dst_detail->size.h, dstFormat,
SWS_BICUBIC,
NULL, NULL, NULL);
if (sws_ctx == NULL)
return PJ_ENOTSUP;
fcv = PJ_POOL_ZALLOC_T(pool, struct ffmpeg_converter);
fcv->base.op = &liswscale_converter_op;
fcv->sws_ctx = sws_ctx;
fcv->src.apply_param.size = src_detail->size;
fcv->src.fmt_info = src_fmt_info;
fcv->dst.apply_param.size = dst_detail->size;
fcv->dst.fmt_info = dst_fmt_info;
*p_cv = &fcv->base;
return PJ_SUCCESS;
}
static void factory_destroy_factory(pjmedia_converter_factory *cf)
{
PJ_UNUSED_ARG(cf);
}
static pj_status_t libswscale_conv_convert(pjmedia_converter *converter,
pjmedia_frame *src_frame,
pjmedia_frame *dst_frame)
{
struct ffmpeg_converter *fcv = (struct ffmpeg_converter*)converter;
struct fmt_info *src = &fcv->src,
*dst = &fcv->dst;
int h;
src->apply_param.buffer = src_frame->buf;
(*src->fmt_info->apply_fmt)(src->fmt_info, &src->apply_param);
dst->apply_param.buffer = dst_frame->buf;
(*dst->fmt_info->apply_fmt)(dst->fmt_info, &dst->apply_param);
h = sws_scale(fcv->sws_ctx,
(const uint8_t* const *)src->apply_param.planes,
src->apply_param.strides,
0, src->apply_param.size.h,
dst->apply_param.planes, dst->apply_param.strides);
//sws_scale() return value can't be trusted? There are cases when
//sws_scale() returns zero but conversion seems to work okay.
//return h==(int)dst->apply_param.size.h ? PJ_SUCCESS : PJ_EUNKNOWN;
PJ_UNUSED_ARG(h);
return PJ_SUCCESS;
}
static void libswscale_conv_destroy(pjmedia_converter *converter)
{
struct ffmpeg_converter *fcv = (struct ffmpeg_converter*)converter;
if (fcv->sws_ctx) {
struct SwsContext *tmp = fcv->sws_ctx;
fcv->sws_ctx = NULL;
sws_freeContext(tmp);
}
}
static pjmedia_converter_factory libswscale_factory =
{
NULL, NULL, /* list */
"libswscale", /* name */
PJMEDIA_CONVERTER_PRIORITY_NORMAL+1, /* priority */
NULL /* op will be init-ed later */
};
PJ_DEF(pj_status_t)
pjmedia_libswscale_converter_init(pjmedia_converter_mgr *mgr)
{
libswscale_factory.op = &libswscale_factory_op;
pjmedia_ffmpeg_add_ref();
return pjmedia_converter_mgr_register_factory(mgr, &libswscale_factory);
}
PJ_DEF(pj_status_t)
pjmedia_libswscale_converter_shutdown(pjmedia_converter_mgr *mgr,
pj_pool_t *pool)
{
PJ_UNUSED_ARG(pool);
pjmedia_ffmpeg_dec_ref();
return pjmedia_converter_mgr_unregister_factory(mgr, &libswscale_factory,
PJ_TRUE);
}
#ifdef _MSC_VER
# pragma comment( lib, "avutil.lib")
# pragma comment( lib, "swscale.lib")
#endif
#endif /* #if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL */