diff --git a/Makefile.in b/Makefile.in index 87db025af..3210ab6a2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -170,6 +170,7 @@ OBJS = @OBJS@ \ src/video_rxtx/ultragrid_rtp.o \ src/vo_postprocess.o \ src/vo_postprocess/border.o \ + src/vo_postprocess/crop.o \ src/vo_postprocess/deinterlace.o \ src/vo_postprocess/3d-interlaced.o \ src/vo_postprocess/interlace.o \ diff --git a/src/vo_postprocess/crop.cpp b/src/vo_postprocess/crop.cpp new file mode 100644 index 000000000..175ec974b --- /dev/null +++ b/src/vo_postprocess/crop.cpp @@ -0,0 +1,194 @@ +/** + * @file vo_postprocess/crop.cpp + * @author Martin Pulec + */ +/* + * Copyright (c) 2019 CESNET, z. s. p. o. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of CESNET nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#include "config_unix.h" +#include "config_win32.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include + +#include "capture_filter.h" +#include "debug.h" +#include "lib_common.h" +#include "rang.hpp" +#include "video.h" +#include "video_display.h" +#include "vo_postprocess.h" + +using rang::fg; +using rang::style; +using std::cout; +using std::max; +using std::min; + +struct state_crop { + int width = 0; + int height = 0; + int xoff = 0; + int yoff = 0; + struct video_desc in_desc = {}; + struct video_desc out_desc = {}; + struct video_frame *in = nullptr; +}; + +static bool crop_get_property(void * /* state */, int /* property */, void * /* val */, size_t * /* len */) +{ + return false; +} + +static void * crop_init(const char *config) { + struct state_crop *s; + + if (config == nullptr) { + return new state_crop(); + } + if (strcmp(config, "help") == 0) { + cout << "crop video postprocess takes optional parameters: " + << style::bold << "width" << style::reset << ", " + << style::bold << "height" << style::reset << ", " + << style::bold << "xoff" << style::reset << " and " + << style::bold << "yoff" << style::reset << ". Example:\n"; + cout << style::bold << fg::red << "\t-p crop" << fg::reset << "[:width=][:height=][:xoff=][:yoff=]\n\n" << style::reset; + return nullptr; + } + + s = new state_crop(); + + char *tmp = strdup(config); + char *config_copy = tmp; + char *item, *save_ptr; + while ((item = strtok_r(config_copy, ":", &save_ptr))) { + if (strncasecmp(item, "width=", strlen("width=")) == 0) { + s->width = atoi(item + strlen("width=")); + } else if (strncasecmp(item, "height=", strlen("height=")) == 0) { + s->height = atoi(item + strlen("height=")); + } else if (strncasecmp(item, "xoff=", strlen("xoff=")) == 0) { + s->xoff = atoi(item + strlen("xoff=")); + } else if (strncasecmp(item, "yoff=", strlen("yoff=")) == 0) { + s->yoff = atoi(item + strlen("yoff=")); + } else { + log_msg(LOG_LEVEL_ERROR, "Wrong config: %s!\n", item); + free(tmp); + delete s; + return nullptr; + } + + config_copy = nullptr; + } + + free(tmp); + + return s; +} + +static int crop_postprocess_reconfigure(void *state, struct video_desc desc) +{ + auto s = static_cast(state); + vf_free(s->in); + + s->in_desc = desc; + s->in = vf_alloc_desc_data(desc); + + s->out_desc = desc; + s->out_desc.width = s->width ? min(s->width, desc.width) : desc.width; + s->out_desc.height = s->height ? min(s->height, desc.height) : desc.height; + + // make sure that width is divisible by pixel block size + int linesize = (int) (s->out_desc.width * get_bpp(desc.color_spec)) / get_pf_block_size(desc.color_spec) + * get_pf_block_size(desc.color_spec); + s->out_desc.width = linesize / get_bpp(desc.color_spec); + + return TRUE; +} + +static struct video_frame * crop_getf(void *state) +{ + auto s = static_cast(state); + return s->in; +} + +static bool crop_postprocess(void *state, struct video_frame *in, struct video_frame *out, int req_pitch) +{ + assert(in->tile_count == 1); + + auto s = static_cast(state); + int src_linesize = vc_get_linesize(in->tiles[0].width, in->color_spec); + int xoff = s->xoff - max(0, out->tiles[0].width + s->xoff - in->tiles[0].width); + int xoff_bytes = (int) (xoff * get_bpp(in->color_spec)) / get_pf_block_size(in->color_spec) + * get_pf_block_size(in->color_spec); + int yoff = s->yoff - max(0, out->tiles[0].height + s->yoff - in->tiles[0].height); + + for (int y = 0 ; y < (int) out->tiles[0].height; y++) { + memcpy(out->tiles[0].data + y * req_pitch, + in->tiles[0].data + (yoff + y) * src_linesize + xoff_bytes, + req_pitch); + } + + return true; +} + +static void crop_done(void *state) +{ + auto s = static_cast(state); + + vf_free(s->in); + delete s; +} + +static void crop_get_out_desc(void *state, struct video_desc *out, int *in_display_mode, int *out_frames) +{ + *out = static_cast(state)->out_desc; + + *in_display_mode = DISPLAY_PROPERTY_VIDEO_MERGED; + *out_frames = 1; +} + +static const struct vo_postprocess_info vo_pp_crop_info = { + crop_init, + crop_postprocess_reconfigure, + crop_getf, + crop_get_out_desc, + crop_get_property, + crop_postprocess, + crop_done, +}; + +REGISTER_MODULE(crop, &vo_pp_crop_info, LIBRARY_CLASS_VIDEO_POSTPROCESS, VO_PP_ABI_VERSION); +