/* $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 #include #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) && \ defined(PJMEDIA_HAS_LIBSWSCALE) && (PJMEDIA_HAS_LIBSWSCALE != 0) #include "ffmpeg_util.h" #include 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 pj_status_t libswscale_conv_convert2( pjmedia_converter *converter, pjmedia_frame *src_frame, const pjmedia_rect_size *src_frame_size, const pjmedia_coord *src_pos, pjmedia_frame *dst_frame, const pjmedia_rect_size *dst_frame_size, const pjmedia_coord *dst_pos, pjmedia_converter_convert_setting *param); 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, &libswscale_conv_convert2 }; 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 AVPixelFormat 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 pj_status_t libswscale_conv_convert2( pjmedia_converter *converter, pjmedia_frame *src_frame, const pjmedia_rect_size *src_frame_size, const pjmedia_coord *src_pos, pjmedia_frame *dst_frame, const pjmedia_rect_size *dst_frame_size, const pjmedia_coord *dst_pos, pjmedia_converter_convert_setting *param) { struct ffmpeg_converter *fcv = (struct ffmpeg_converter*)converter; struct fmt_info *src = &fcv->src, *dst = &fcv->dst; int h; unsigned j; pjmedia_rect_size orig_src_size; pjmedia_rect_size orig_dst_size; PJ_UNUSED_ARG(param); /* Save original conversion sizes */ orig_src_size = src->apply_param.size; orig_dst_size = dst->apply_param.size; /* Set the first act buffer from src frame, and overwrite size. */ src->apply_param.buffer = src_frame->buf; src->apply_param.size = *src_frame_size; (*src->fmt_info->apply_fmt)(src->fmt_info, &src->apply_param); /* Set the last act buffer from dst frame, and overwrite size. */ dst->apply_param.buffer = dst_frame->buf; dst->apply_param.size = *dst_frame_size; (*dst->fmt_info->apply_fmt)(dst->fmt_info, &dst->apply_param); for (j = 0; j < src->fmt_info->plane_cnt; ++j) { pjmedia_video_apply_fmt_param *ap = &src->apply_param; int y = src_pos->y * ap->plane_bytes[j] / ap->strides[j] / ap->size.h; ap->planes[j] += y * ap->strides[j] + src_pos->x * ap->strides[j] / ap->size.w; } for (j = 0; j < dst->fmt_info->plane_cnt; ++j) { pjmedia_video_apply_fmt_param *ap = &dst->apply_param; int y = dst_pos->y * ap->plane_bytes[j] / ap->strides[j] / ap->size.h; ap->planes[j] += y * ap->strides[j] + dst_pos->x * ap->strides[j] / ap->size.w; } /* Return back the original conversion size */ src->apply_param.size = orig_src_size; dst->apply_param.size = orig_dst_size; 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 /* PJMEDIA_HAS_LIBSWSCALE */