diff options
| author | Michele Calgaro <michele.calgaro@yahoo.it> | 2020-09-11 14:38:47 +0900 |
|---|---|---|
| committer | Michele Calgaro <michele.calgaro@yahoo.it> | 2020-09-11 14:38:47 +0900 |
| commit | 884c8093d63402a1ad0b502244b791e3c6782be3 (patch) | |
| tree | a600d4ab0d431a2bdfe4c15b70df43c14fbd8dd0 /debian/transcode/transcode-1.1.7/src/video_trans.c | |
| parent | 14e1aa2006796f147f3f4811fb908a6b01e79253 (diff) | |
| download | extra-dependencies-884c8093d63402a1ad0b502244b791e3c6782be3.tar.gz extra-dependencies-884c8093d63402a1ad0b502244b791e3c6782be3.zip | |
Added debian extra dependency packages.
Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
Diffstat (limited to 'debian/transcode/transcode-1.1.7/src/video_trans.c')
| -rw-r--r-- | debian/transcode/transcode-1.1.7/src/video_trans.c | 607 |
1 files changed, 607 insertions, 0 deletions
diff --git a/debian/transcode/transcode-1.1.7/src/video_trans.c b/debian/transcode/transcode-1.1.7/src/video_trans.c new file mode 100644 index 00000000..bee77ef1 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/video_trans.c @@ -0,0 +1,607 @@ +/* + * video_trans.c - video frame transformation routines + * Written by Andrew Church <achurch@achurch.org> + * Based on code written by Thomas Oestreich. + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#include "transcode.h" +#include "framebuffer.h" +#include "video_trans.h" +#include "libtcvideo/tcvideo.h" + +/*************************************************************************/ + +/* Structure that holds video frame information for passing around to + * processing routines. Since this is used only locally, we don't add + * the fields to vframe_list_t itself. */ + +typedef struct { + vframe_list_t *ptr; + int preadj_w, preadj_h; // width and height used for secondary buffer + int Bpp; // BYTES (not bits) per pixel + int nplanes; // number of planes + uint8_t *planes[3]; // pointer to start of each plane + uint8_t *tmpplanes[3]; // same, for secondary buffer + int width_div[3]; // width divisors for each plane + int height_div[3]; // height divisors for each plane + uint8_t black_pixel[3]; // "black" value for each plane (e.g. 128 for U/V) +} video_trans_data_t; + +/* Macro to perform a transformation on a frame. `vtd' is a pointer to a + * video_trans_data_t; the given function `func' will be called for each + * plane `i' as: + * func(handle, vtd->planes[i], vtd->tmpplanes[i], vtd->ptr->v_width, + * vtd->ptr->v_height, vtd->Bpp, args) + * where `args' are all arguments to this macro (if any) following `vtd'. + * swap_buffers(vtd) is called after the processing is complete. + */ +#define PROCESS_FRAME(func,vtd,args...) do { \ + int i; \ + for (i = 0; i < (vtd)->nplanes; i++) { \ + func(handle, (vtd)->planes[i], (vtd)->tmpplanes[i], \ + (vtd)->ptr->v_width / (vtd)->width_div[i], \ + (vtd)->ptr->v_height / (vtd)->height_div[i], \ + (vtd)->Bpp , ## args); \ + } \ + swap_buffers(vtd); \ +} while (0) + +/* Handle for calling tcvideo functions. */ +static TCVHandle handle = 0; + +/*************************************************************************/ +/*************************** Internal routines ***************************/ +/*************************************************************************/ + +/** + * set_vtd: Initialize the given vtd structure from the given + * vframe_list_t, and update ptr->video_size. + * + * Parameters: + * vtd: Pointer to video frame data to be initialized. + * ptr: Pointer to video frame buffer. + * Return value: + * None. + */ + +static void set_vtd(video_trans_data_t *vtd, vframe_list_t *ptr) +{ + int i; + + vtd->ptr = ptr; + vtd->preadj_w = 0; + vtd->preadj_h = 0; + /* Set some defaults */ + vtd->Bpp = 1; + vtd->nplanes = 1; + vtd->planes[0] = ptr->video_buf; + vtd->tmpplanes[0] = ptr->video_buf_Y[ptr->free]; + vtd->width_div[0] = 1; + vtd->height_div[0] = 1; + vtd->black_pixel[0] = 0; + /* Now set parameters based on image format */ + if (ptr->v_codec == CODEC_YUV) { + vtd->nplanes = 3; + vtd->Bpp = 1; + vtd->width_div[1] = 2; + vtd->width_div[2] = 2; + vtd->height_div[1] = 2; + vtd->height_div[2] = 2; + vtd->black_pixel[1] = 128; + vtd->black_pixel[2] = 128; + } else if (vtd->ptr->v_codec == CODEC_YUV422) { + vtd->nplanes = 3; + vtd->Bpp = 1; + vtd->width_div[1] = 2; + vtd->width_div[2] = 2; + vtd->height_div[1] = 1; + vtd->height_div[2] = 1; + vtd->black_pixel[1] = 128; + vtd->black_pixel[2] = 128; + } else if (vtd->ptr->v_codec == CODEC_RGB) { + vtd->Bpp = 3; + } + ptr->video_size = 0; + for (i = 0; i < vtd->nplanes; i++) { + int planesize = (ptr->v_width/vtd->width_div[i]) + * (ptr->v_height/vtd->height_div[i]) + * vtd->Bpp; + ptr->video_size += planesize; + if (i < vtd->nplanes-1) { + vtd->planes[i+1] = vtd->planes[i] + planesize; + vtd->tmpplanes[i+1] = vtd->tmpplanes[i] + planesize; + } + } +} + +/*************************************************************************/ + +/** + * preadjust_frame_size: Prepare for an operation that will change the + * frame size, setting up the secondary buffer plane pointers with the new + * size. Calling swap_buffers() will store the new size in the + * vframe_list_t structure. + * + * Parameters: + * vtd: Pointer to video frame data. + * new_w: New frame width. + * new_h: New frame height. + * Return value: + * None. + */ + +static void preadjust_frame_size(video_trans_data_t *vtd, int new_w, int new_h) +{ + int i; + + vtd->preadj_w = new_w; + vtd->preadj_h = new_h; + for (i = 0; i < vtd->nplanes-1; i++) { + int planesize = (new_w/vtd->width_div[i]) * (new_h/vtd->height_div[i]) + * vtd->Bpp; + vtd->tmpplanes[i+1] = vtd->tmpplanes[i] + planesize; + } +} + +/*************************************************************************/ + +/** + * swap_buffers: Swap current video frame buffer with free buffer. Also + * updates frame size if preadjust_frame_size() has been called. + * + * Parameters: + * vtd: Pointer to video frame data. + * Return value: + * None. + */ + +static void swap_buffers(video_trans_data_t *vtd) +{ + vtd->ptr->video_buf = vtd->ptr->video_buf_Y[vtd->ptr->free]; + vtd->ptr->free = (vtd->ptr->free==0) ? 1 : 0; + /* Install new width/height if preadjust_frame_size() was called */ + if (vtd->preadj_w && vtd->preadj_h) { + vtd->ptr->v_width = vtd->preadj_w; + vtd->ptr->v_height = vtd->preadj_h; + vtd->preadj_w = 0; + vtd->preadj_h = 0; + } + /* Set up plane pointers again */ + set_vtd(vtd, vtd->ptr); +} + +/*************************************************************************/ +/*************************************************************************/ + +/** + * do_process_frame: Perform video frame transformations based on global + * transcoding settings (derived from command-line arguments). + * + * Parameters: + * vob: Global data pointer. + * ptr: Pointer to video frame buffer. + * Return value: + * 0 on success, -1 on failure. + */ + +static int do_process_frame(vob_t *vob, vframe_list_t *ptr) +{ + video_trans_data_t vtd; /* for passing to subroutines */ + + + /**** Sanity check and initialization ****/ + + if (ptr->video_buf_Y[0] == ptr->video_buf_Y[1]) { + tc_log_error(__FILE__, "video frame has no temporary buffer!"); + return -1; + } + if (ptr->video_buf == ptr->video_buf_Y[ptr->free]) { + static int warned = 0; + if (!warned) { + tc_log_warn(__FILE__, "ptr->free points to wrong buffer" + " (BUG in transcode or modules)"); + warned = 1; + } + ptr->free = !ptr->free; + } + set_vtd(&vtd, ptr); + + /**** -j: clip frame (import) ****/ + + if (im_clip) { + preadjust_frame_size(&vtd, + ptr->v_width - vob->im_clip_left - vob->im_clip_right, + ptr->v_height - vob->im_clip_top - vob->im_clip_bottom); + PROCESS_FRAME(tcv_clip, &vtd, + vob->im_clip_left / vtd.width_div[i], + vob->im_clip_right / vtd.width_div[i], + vob->im_clip_top / vtd.height_div[i], + vob->im_clip_bottom / vtd.height_div[i], + vtd.black_pixel[i]); + } + + /**** -I: deinterlace video frame ****/ + + if (vob->deinterlace > 0 + || ((ptr->attributes & TC_FRAME_IS_INTERLACED) && ptr->deinter_flag > 0) + ) { + int mode = (vob->deinterlace>0 ? vob->deinterlace : ptr->deinter_flag); + if (mode == 1) { + /* Simple linear interpolation */ + /* Note that for YUV, we can just leave U and V alone, since + * they already cover pairs of lines; thus instead of using + * PROCESS_FRAME, we just call tcv_deinterlace() on the Y/RGB + * plane, then copy the other two planes and swap_buffers(). */ + int i; + tcv_deinterlace(handle, vtd.planes[0], vtd.tmpplanes[0], + ptr->v_width, ptr->v_height, vtd.Bpp, + TCV_DEINTERLACE_INTERPOLATE); + for (i = 1; i < vtd.nplanes; i++) { + ac_memcpy(vtd.tmpplanes[i], vtd.planes[i], + (ptr->v_width/vtd.width_div[i]) + * (ptr->v_height/vtd.height_div[i])); + } + swap_buffers(&vtd); + } else if (mode == 3 || mode == 4) { + /* Drop every other line (and zoom back out in mode 3) */ + preadjust_frame_size(&vtd, ptr->v_width, ptr->v_height/2); + /* Drop the top or the bottom field? (Does it matter?) */ + PROCESS_FRAME(tcv_deinterlace, &vtd, + TCV_DEINTERLACE_DROP_FIELD_BOTTOM); + if (mode == 3) { + int w = ptr->v_width, h = ptr->v_height*2; + preadjust_frame_size(&vtd, w, h); + PROCESS_FRAME(tcv_zoom, &vtd, w / vtd.width_div[i], + h / vtd.height_div[i], vob->zoom_filter); + } + } else if (mode == 5) { + /* Linear blend; as for -I 1, only Y is processed in YUV mode */ + int i; + tcv_deinterlace(handle, vtd.planes[0], vtd.tmpplanes[0], + ptr->v_width, ptr->v_height, vtd.Bpp, + TCV_DEINTERLACE_LINEAR_BLEND); + for (i = 1; i < vtd.nplanes; i++) { + ac_memcpy(vtd.tmpplanes[i], vtd.planes[i], + (ptr->v_width/vtd.width_div[i]) + * (ptr->v_height/vtd.height_div[i])); + } + swap_buffers(&vtd); + } + /* else mode 2 (handled by encoder) or unknown: do nothing */ + ptr->attributes &= ~TC_FRAME_IS_INTERLACED; + } + + /**** -X: fast resize (up) ****/ + /**** -B: fast resize (down) ****/ + + if (resize1 || resize2) { + int width = ptr->v_width, height = ptr->v_height; + int resize_w = vob->hori_resize2 - vob->hori_resize1; + int resize_h = vob->vert_resize2 - vob->vert_resize1; + if (resize_h) { + preadjust_frame_size(&vtd, width, height+resize_h*8); + PROCESS_FRAME(tcv_resize, &vtd, 0, resize_h, 8/vtd.width_div[i], + 8/vtd.height_div[i]); + height += resize_h * 8; + } + if (resize_w) { + preadjust_frame_size(&vtd, width+resize_w*8, height); + PROCESS_FRAME(tcv_resize, &vtd, resize_w, 0, 8/vtd.width_div[i], + 8/vtd.height_div[i]); + } + } + + /**** -Z: zoom frame (slow resize) ****/ + + if (zoom) { + preadjust_frame_size(&vtd, vob->zoom_width, vob->zoom_height); + if (vob->zoom_interlaced) { + /* In YUV mode, only handle the first place as interlaced; + * the U and V planes are shared between both fields */ + int i; + tcv_zoom(handle, vtd.planes[0], vtd.tmpplanes[0], + ptr->v_width, ptr->v_height, vtd.Bpp, + vob->zoom_width, -vob->zoom_height, vob->zoom_filter); + for (i = 1; i < vtd.nplanes; i++) { + tcv_zoom(handle, vtd.planes[i], vtd.tmpplanes[i], + ptr->v_width / vtd.width_div[i], + ptr->v_height / vtd.height_div[i], vtd.Bpp, + vob->zoom_width / vtd.width_div[i], + vob->zoom_height / vtd.height_div[i], + vob->zoom_filter); + } + swap_buffers(&vtd); + } else { + PROCESS_FRAME(tcv_zoom, &vtd, vob->zoom_width / vtd.width_div[i], + vob->zoom_height / vtd.height_div[i], + vob->zoom_filter); + } + } + + /**** -Y: clip frame (export) ****/ + + if (ex_clip) { + preadjust_frame_size(&vtd, + ptr->v_width - vob->ex_clip_left-vob->ex_clip_right, + ptr->v_height - vob->ex_clip_top - vob->ex_clip_bottom); + PROCESS_FRAME(tcv_clip, &vtd, + vob->ex_clip_left / vtd.width_div[i], + vob->ex_clip_right / vtd.width_div[i], + vob->ex_clip_top / vtd.height_div[i], + vob->ex_clip_bottom / vtd.height_div[i], + vtd.black_pixel[i]); + } + + /**** -r: rescale video frame ****/ + + if (rescale) { + preadjust_frame_size(&vtd, ptr->v_width / vob->reduce_w, + ptr->v_height / vob->reduce_h); + PROCESS_FRAME(tcv_reduce, &vtd, vob->reduce_w, vob->reduce_h); + } + + /**** -z: flip frame vertically ****/ + + if (flip) { + PROCESS_FRAME(tcv_flip_v, &vtd); + } + + /**** -l: flip flame horizontally (mirror) ****/ + + if (mirror) { + PROCESS_FRAME(tcv_flip_h, &vtd); + } + + /**** -k: red/blue swap ****/ + + if (rgbswap) { + if (ptr->v_codec == CODEC_RGB) { + int i; + for (i = 0; i < ptr->v_width * ptr->v_height; i++) { + uint8_t tmp = vtd.planes[0][i*3]; + vtd.planes[0][i*3] = vtd.planes[0][i*3+2]; + vtd.planes[0][i*3+2] = tmp; + } + } else { + int UVsize = (ptr->v_width / vtd.width_div[1]) + * (ptr->v_height / vtd.height_div[1]) * vtd.Bpp; + ac_memcpy(vtd.tmpplanes[1], vtd.planes[1], UVsize); /* tmp<-U */ + ac_memcpy(vtd.planes[1], vtd.planes[2], UVsize); /* U<-V */ + ac_memcpy(vtd.planes[2], vtd.tmpplanes[1], UVsize); /* V<-tmp */ + } + } + + /**** -K: grayscale ****/ + + if (decolor) { + if (ptr->v_codec == CODEC_RGB) { + /* Convert to 8-bit grayscale, then back to RGB24. Just + * averaging the values won't give us the right intensity. */ + tcv_convert(handle, vtd.planes[0], vtd.tmpplanes[0], + ptr->v_width, ptr->v_height, IMG_RGB24, IMG_GRAY8); + tcv_convert(handle, vtd.tmpplanes[0], vtd.planes[0], + ptr->v_width, ptr->v_height, IMG_GRAY8, IMG_RGB24); + } else { + /* YUV is easy: just set U and V to 128 */ + int UVsize = (ptr->v_width / vtd.width_div[1]) + * (ptr->v_height / vtd.height_div[1]) * vtd.Bpp; + memset(vtd.planes[1], 128, UVsize); + memset(vtd.planes[2], 128, UVsize); + } + } + + /**** -G: gamma correction ****/ + + if (dgamma) { + /* Only process the first plane (Y) for YUV; for RGB it's all in + * one plane anyway */ + tcv_gamma_correct(handle, ptr->video_buf, ptr->video_buf, + ptr->v_width, ptr->v_height, vtd.Bpp, vob->gamma); + } + + /**** -C: antialiasing ****/ + + if (vob->antialias) { + /* Only Y is antialiased; U and V remain the same */ + tcv_antialias(handle, vtd.planes[0], vtd.tmpplanes[0], + ptr->v_width, ptr->v_height, vtd.Bpp, + vob->aa_weight, vob->aa_bias); + if (ptr->v_codec != CODEC_RGB) { + int UVsize = (ptr->v_width / vtd.width_div[1]) + * (ptr->v_height / vtd.height_div[1]) * vtd.Bpp; + ac_memcpy(vtd.tmpplanes[1], vtd.planes[1], UVsize); + ac_memcpy(vtd.tmpplanes[2], vtd.planes[2], UVsize); + } + swap_buffers(&vtd); + } + + /**** End of processing ****/ + + return 0; +} + +/*************************************************************************/ +/*************************** Exported routines ***************************/ +/*************************************************************************/ + +/** + * process_vid_frame: Main video frame processing routine. The image is + * passed in ptr->video_buf; this can be updated as needed, e.g. to point + * to the secondary buffer after transformations. + * + * Parameters: + * vob: Global data pointer. + * ptr: Pointer to video frame buffer. + * Return value: + * 0 on success, -1 on failure. + */ + +int process_vid_frame(vob_t *vob, vframe_list_t *ptr) +{ + /* Check parameter validity */ + if (!vob || !ptr) + return -1; + + /* Check for pass-through mode or skipped frames */ + if (vob->pass_flag & TC_VIDEO) + return 0; + if (ptr->attributes & TC_FRAME_IS_SKIPPED) + return 0; + + /* It's a valid frame, check the colorspace for validity and process it */ + if (vob->im_v_codec == CODEC_RGB + || vob->im_v_codec == CODEC_YUV + || vob->im_v_codec == CODEC_YUV422 + ) { + ptr->v_codec = vob->im_v_codec; + return do_process_frame(vob, ptr); + } + + /* Invalid colorspace, bail out */ + tc_error("Oops, invalid colorspace video frame data"); + return -1; +} + +/*************************************************************************/ + +/** + * preprocess_vid_frame: Frame preprocessing routine. Checks for frame + * out of -c range and performs early clipping. + * + * Parameters: + * vob: Global data pointer. + * ptr: Pointer to video frame buffer. + * Return value: + * 0 on success, -1 on failure. + */ + +int preprocess_vid_frame(vob_t *vob, vframe_list_t *ptr) +{ + /* Check parameter validity */ + if (!vob || !ptr) + return -1; + + /* Allocate tcvideo handle if necessary */ + if (!handle) { + handle = tcv_init(); + if (!handle) { + tc_log_error(PACKAGE, "video_trans.c: tcv_init() failed!"); + return -1; + } + } + + /* Check for pass-through mode */ + if (vob->pass_flag & TC_VIDEO) + return 0; + + /* Check frame colorspace */ + if (vob->im_v_codec != CODEC_RGB + && vob->im_v_codec != CODEC_YUV + && vob->im_v_codec != CODEC_YUV422 + ) { + tc_error("Oops, invalid colorspace video frame data"); + return -1; + } + + /* Perform early clipping */ + if (pre_im_clip) { + video_trans_data_t vtd; + ptr->v_codec = vob->im_v_codec; + set_vtd(&vtd, ptr); + preadjust_frame_size(&vtd, + ptr->v_width - vob->pre_im_clip_left - vob->pre_im_clip_right, + ptr->v_height - vob->pre_im_clip_top - vob->pre_im_clip_bottom); + PROCESS_FRAME(tcv_clip, &vtd, + vob->pre_im_clip_left / vtd.width_div[i], + vob->pre_im_clip_right / vtd.width_div[i], + vob->pre_im_clip_top / vtd.height_div[i], + vob->pre_im_clip_bottom / vtd.height_div[i], + vtd.black_pixel[i]); + } + + /* Finished with preprocessing */ + return 0; +} + +/*************************************************************************/ + +/** + * postprocess_vid_frame: Frame postprocessing routine. Performs final + * clipping and sanity checks. + * + * Parameters: + * vob: Global data pointer. + * ptr: Pointer to video frame buffer. + * Return value: + * 0 on success, -1 on failure. + */ + +/* Frame postprocessing routine. Performs final clipping and sanity + * checks. Parameters are as for process_vid_frame(). + */ + +int postprocess_vid_frame(vob_t *vob, vframe_list_t *ptr) +{ + /* Check parameter validity */ + if (!vob || !ptr) + return -1; + + /* Check for pass-through mode or skipped frames */ + if (vob->pass_flag & TC_VIDEO) + return 0; + if (ptr->attributes & TC_FRAME_IS_SKIPPED) + return 0; + + /* Check frame colorspace */ + if (vob->im_v_codec != CODEC_RGB + && vob->im_v_codec != CODEC_YUV + && vob->im_v_codec != CODEC_YUV422 + ) { + tc_error("Oops, invalid colorspace video frame data"); + return -1; + } + + /* Perform final clipping, if this isn't a cloned frame */ + if (post_ex_clip && !(ptr->attributes & TC_FRAME_WAS_CLONED)) { + video_trans_data_t vtd; + ptr->v_codec = vob->im_v_codec; + set_vtd(&vtd, ptr); + preadjust_frame_size(&vtd, + ptr->v_width - vob->post_ex_clip_left - vob->post_ex_clip_right, + ptr->v_height - vob->post_ex_clip_top - vob->post_ex_clip_bottom); + PROCESS_FRAME(tcv_clip, &vtd, + vob->post_ex_clip_left / vtd.width_div[i], + vob->post_ex_clip_right / vtd.width_div[i], + vob->post_ex_clip_top / vtd.height_div[i], + vob->post_ex_clip_bottom / vtd.height_div[i], + vtd.black_pixel[i]); + } + + /* Sanity check: make sure the frame size is what we're expecting */ + if (ptr->v_width != vob->ex_v_width || ptr->v_height != vob->ex_v_height) { + tc_log_msg(__FILE__, "width %d %d | height %d %d", + ptr->v_width, vob->ex_v_width, + ptr->v_height, vob->ex_v_height); + tc_error("Oops, frame parameter mismatch detected"); + } + + /* Finished with postprocessing */ + return 0; +} + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ |
