1 
2 /*
3  * $Id: y4mshift.c,v 1.4 2009/09/19 18:57:00 sms00 Exp $
4  *
5  * written by Steven M. Schultz <sms@2BSD.COM>
6  *
7  * 2003/5/8 - added the ability to place a black border around the frame.
8  *
9  * Simple program to shift the data an even number of pixels.   The shift count
10  * is positive for shifting to the right and negative for a left shift.
11  *
12  * Usage: y4mshift -n N [ -b xoffset,yoffset,xsize,ysize ]
13  *
14  * No file arguments are needed since this is a filter only program.
15 */
16 
17 #include "config.h"
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include "yuv4mpeg.h"
24 
25 #define HALFSHIFT (shiftnum / SS_H)
26 
27 	void	black_border(u_char **, char *, int, int, int, int);
28 	void	vertical_shift(u_char **, int, int, int, int, int, int);
29 static  void    usage(char *);
30 
main(int argc,char ** argv)31 int main(int argc, char **argv)
32         {
33         int     i, c, width, height, frames, err, chroma_ss, ilace_factor;
34         int     shiftnum = 0, shiftY = 0, rightshiftY, rightshiftUV;
35         int     vshift = 0, vshiftY = 0, monochrome = 0;
36         int     verbose = 0, fdin;
37         int     SS_H = 2, SS_V = 2;
38         u_char  *yuv[3], *line;
39 	char	*borderarg = NULL;
40         y4m_stream_info_t istream, ostream;
41         y4m_frame_info_t iframe;
42 
43         fdin = fileno(stdin);
44 
45 	y4m_accept_extensions(1);
46 
47         opterr = 0;
48         while   ((c = getopt(argc, argv, "hvn:N:b:My:Y:")) != EOF)
49                 {
50                 switch  (c)
51                         {
52 			case	'M':
53 				monochrome = 1;
54 				break;
55 			case	'b':
56 				borderarg = strdup(optarg);
57 				break;
58                         case    'n':
59                                 shiftnum = atoi(optarg);
60                                 break;
61                         case    'y':
62                                 shiftY = atoi(optarg);
63                                 break;
64                         case    'Y':
65                                 vshiftY = atoi(optarg);
66                                 break;
67 			case	'N':
68 				vshift = atoi(optarg);
69 				break;
70                         case    'v':
71                                 verbose++;
72                                 break;
73                         case    '?':
74                         case    'h':
75                         default:
76                                 usage(argv[0]);
77                         }
78                 }
79 
80 	if      ( shiftnum + shiftY > 0 )
81 		rightshiftY = 1;
82 	else
83 		rightshiftY = 0;
84 	shiftY = abs(shiftnum + shiftY);  /* now shiftY is total Y shift */
85 
86         if      (shiftnum > 0)
87                 rightshiftUV = 1;
88         else
89                 {
90                 rightshiftUV = 0;
91                 shiftnum = abs(shiftnum);
92                 }
93 
94         y4m_init_stream_info(&istream);
95         y4m_init_frame_info(&iframe);
96 
97         err = y4m_read_stream_header(fdin, &istream);
98         if      (err != Y4M_OK)
99                 mjpeg_error_exit1("Input stream error: %s\n", y4m_strerr(err));
100 
101 	if	(y4m_si_get_plane_count(&istream) != 3)
102 		mjpeg_error_exit1("Only 3 plane formats supported");
103 
104 	chroma_ss = y4m_si_get_chroma(&istream);
105 	SS_H = y4m_chroma_ss_x_ratio(chroma_ss).d;
106 	SS_V = y4m_chroma_ss_y_ratio(chroma_ss).d;
107 
108 	if	(y4m_si_get_interlace(&istream) == Y4M_ILACE_NONE)
109 		ilace_factor = 1;
110 	else
111 		ilace_factor = 2;
112 
113         if      ((shiftnum % SS_H) != 0)
114                 usage(argv[0]);
115 
116         if      ((vshift % (ilace_factor * SS_V)) != 0)
117                 usage(argv[0]);
118 
119         width = y4m_si_get_width(&istream);
120         height = y4m_si_get_height(&istream);
121 
122         if      (shiftnum > width / 2)
123                 mjpeg_error_exit1("nonsense to shift %d out of %d",
124                         shiftnum, width);
125         if      (shiftY > width / 2)
126                 mjpeg_error_exit1("nonsense to shift %d out of %d\n",
127                         shiftY, width);
128 
129         y4m_init_stream_info(&ostream);
130         y4m_copy_stream_info(&ostream, &istream);
131         y4m_write_stream_header(fileno(stdout), &ostream);
132 
133         yuv[0] = malloc(y4m_si_get_plane_length(&istream, 0));
134         if      (yuv[0] == NULL)
135                 mjpeg_error_exit1("malloc() failed for plane 0");
136         yuv[1] = malloc(y4m_si_get_plane_length(&istream, 1));
137         if      (yuv[1] == NULL)
138                 mjpeg_error_exit1("malloc() failed for plane 1");
139         yuv[2] = malloc(y4m_si_get_plane_length(&istream, 2));
140         if      (yuv[2] == NULL)
141                 mjpeg_error_exit1("malloc() failed for plane 2");
142 
143         frames = 0;
144         for     (;y4m_read_frame(fdin,&istream,&iframe,yuv) == Y4M_OK; frames++)
145                 {
146                 if      (shiftnum == 0 && shiftY == 0)
147                         goto outputframe;
148                 for     (i = 0; i < height; i++)
149                         {
150 /*
151  * Y
152 */
153                         line = &yuv[0][i * width];
154                         if      (rightshiftY)
155                                 {
156                                 bcopy(line, line + shiftY, width - shiftY);
157                                 memset(line, 16, shiftY); /* black */
158                                 }
159                         else
160                                 {
161                                 bcopy(line + shiftY, line, width - shiftY);
162                                 memset(line + width - shiftY, 16, shiftY);
163                                 }
164                         }
165 /*
166  * U
167 */
168                 for     (i = 0; i < height / SS_V; i++)
169                         {
170                         line = &yuv[1][i * (width / SS_H)];
171                         if      (rightshiftUV)
172                                 {
173                                 bcopy(line, line+HALFSHIFT, (width-shiftnum)/SS_H);
174                                 memset(line, 128, HALFSHIFT); /* black */
175                                 }
176                         else
177                                 {
178                                 bcopy(line+HALFSHIFT, line, (width-shiftnum)/SS_H);
179                                 memset(line+(width-shiftnum)/SS_H, 128, HALFSHIFT);
180                                 }
181                         }
182 /*
183  * V
184 */
185                 for     (i = 0; i < height / SS_V; i++)
186                         {
187                         line = &yuv[2][i  * (width / SS_H)];
188                         if      (rightshiftUV)
189                                 {
190                                 bcopy(line, line+HALFSHIFT, (width-shiftnum)/SS_H);
191                                 memset(line, 128, HALFSHIFT); /* black */
192                                 }
193                         else
194                                 {
195                                 bcopy(line+HALFSHIFT, line, (width-shiftnum)/SS_H);
196                                 memset(line+(width-shiftnum)/SS_H, 128, HALFSHIFT);
197                                 }
198                         }
199 outputframe:
200 		if	(vshift)
201 			vertical_shift(yuv, vshift, vshiftY, width, height, SS_H, SS_V);
202 		if	(borderarg)
203 			black_border(yuv, borderarg, width, height, SS_H, SS_V);
204 		if	(monochrome)
205 			{
206 			memset(&yuv[1][0], 128, (width / SS_H) * (height / SS_V));
207 			memset(&yuv[2][0], 128, (width / SS_H) * (height / SS_V));
208 			}
209                 y4m_write_frame(fileno(stdout), &ostream, &iframe, yuv);
210                 }
211         y4m_fini_frame_info(&iframe);
212         y4m_fini_stream_info(&istream);
213         y4m_fini_stream_info(&ostream);
214 
215         exit(0);
216         }
217 
218 /*
219  * -b Xoff,Yoff,Xsize,YSize
220 */
221 
black_border(u_char * yuv[],char * borderstring,int W,int H,int SS_H,int SS_V)222 void black_border (u_char *yuv[], char *borderstring, int W, int H, int SS_H, int SS_V)
223 	{
224 	static	int parsed = -1;
225 	static	int BX0, BX1;	/* Left, Right border columns */
226 	static	int BY0, BY1;	/* Top, Bottom border rows */
227 	int	i1, i2, i, dy, W2, H2;
228 
229 	if	(parsed == -1)
230 		{
231 		parsed = 0;
232 		i = sscanf(borderstring, "%d,%d,%d,%d", &BX0, &BY0, &i1, &i2);
233 		if	(i != 4 || (BX0 % SS_H) || (BY0 % (2*SS_V)) || i1 < 0 || i2 < 0 ||
234 			 (BX0 + i1 > W) || (BY0 + i2 > H))
235 			{
236 			mjpeg_warn(" border args invalid - ignored");
237 			return;
238 			}
239 		BX1 = BX0 + i1;
240 		BY1 = BY0 + i2;
241 		parsed = 1;
242 		}
243 /*
244  * If a borderstring was seen but declared invalid then it
245  * is being ignored so just return.
246 */
247 	if	(parsed == 0)
248 		return;
249 
250 	W2 = W / SS_H;
251 	H2 = H / SS_V;
252 
253 /*
254  * Yoff Lines at the top.   If the vertical offset is 0 then no top border
255  * is being requested and there is nothing to do.
256 */
257 	if	(BY0 != 0)
258 		{
259 		memset(yuv[0], 16,  W  * BY0);
260 		memset(yuv[1], 128, W2 * BY0/SS_V);
261 		memset(yuv[2], 128, W2 * BY0/SS_V);
262 		}
263 /*
264  * Height - (Ysize + Yoff) lines at bottom.   If the bottom coincides with
265  * the frame size then there is nothing to do.
266 */
267 	if	(H != BY1)
268 		{
269 		memset(&yuv[0][BY1 * W], 16, W  * (H - BY1));
270 		memset(&yuv[1][(BY1 / SS_V) * W2], 128, W2 * (H - BY1)/SS_V);
271 		memset(&yuv[2][(BY1 / SS_V) * W2], 128, W2 * (H - BY1)/SS_V);
272 		}
273 /*
274  * Now the partial lines in the middle.   Go from rows BY0 thru BY1 because
275  * the whole rows (0 thru BY0) and (BY1 thru H) have been done above.
276 */
277 	for	(dy = BY0; dy < BY1; dy++)
278 		{
279 /*
280  * First the columns on the left (x = 0 thru BX0).   If the X offset is 0
281  * then there is nothing to do.
282 */
283 		if	(BX0 != 0)
284 			{
285 			memset(&yuv[0][dy * W], 16, BX0);
286 			memset(&yuv[1][dy / SS_V * W2], 128, BX0 / SS_H);
287 			memset(&yuv[2][dy / SS_V * W2], 128, BX0 / SS_H);
288 			}
289 /*
290  * Then the columns on the right (x = BX1 thru W).   If the right border
291  * start coincides with the frame size then there is nothing to do.
292 */
293 		if	(W != BX1)
294 			{
295 			memset(&yuv[0][(dy * W) + BX1], 16, W - BX1);
296 			memset(&yuv[1][(dy/SS_V * W2) + BX1/SS_H], 128, (W - BX1)/SS_H);
297 			memset(&yuv[2][(dy/SS_V * W2) + BX1/SS_H], 128, (W - BX1)/SS_H);
298 			}
299 		}
300 
301 	}
302 
vertical_shift(u_char ** yuv,int vshift,int vshiftY,int width,int height,int SS_H,int SS_V)303 void vertical_shift(u_char **yuv, int vshift, int vshiftY, int width, int height, int SS_H, int SS_V)
304 	{
305 	int	downshiftY, downshiftUV, w2 = width / SS_H, v2;
306 
307 	if      ( vshift + vshiftY > 0 )
308 		downshiftY = 1;
309 	else
310 		downshiftY = 0;
311 	vshiftY = abs(vshift + vshiftY);  /* now shiftY is total Y shift */
312 
313 	if	(vshift > 0)
314 		downshiftUV = 1;
315 	else
316 		{
317 		downshiftUV = 0;
318 		vshift = abs(vshift);
319 		}
320 	v2 = vshift / SS_V;
321 
322 	if	(downshiftY)
323 		{
324 		memmove(&yuv[0][vshiftY * width], &yuv[0][0],
325 			(height - vshiftY) * width);
326 		memset(&yuv[0][0], 16, vshiftY * width);
327 		}
328 	else
329 		{
330 		memmove(&yuv[0][0], &yuv[0][vshiftY * width],
331 			(height - vshiftY) * width);
332 		memset(&yuv[0][(height - vshiftY) * width], 16, vshiftY * width);
333 		}
334 
335 	if      (downshiftUV)
336 	        {
337 		memmove( &yuv[1][v2 * w2], &yuv[1][0],
338 			(height - vshift) / SS_V * w2);
339 		memmove( &yuv[2][v2 * w2], &yuv[2][0],
340 			(height - vshift) / SS_V * w2);
341 
342 		memset(&yuv[1][0], 128, v2 * w2);
343 		memset(&yuv[2][0], 128, v2 * w2);
344 		}
345 	else
346 		{
347 		memmove(&yuv[1][0], &yuv[1][v2 * w2],
348 			(height - vshift) / SS_V * w2);
349 		memmove(&yuv[2][0], &yuv[2][v2 * w2],
350 			(height - vshift) / SS_V * w2);
351 
352 		memset(&yuv[1][((height - vshift) / SS_V) * w2], 128, v2 * w2);
353 		memset(&yuv[2][((height - vshift) / SS_V) * w2], 128, v2 * w2);
354 		}
355 	}
356 
usage(char * pgm)357 static void usage(char *pgm)
358         {
359 
360         fprintf(stderr, "%s: usage: [-v] [-h] [-M] [-b xoff,yoff,xsize,ysize] [-y num] [-Y num] [-N num] -n N\n", pgm);
361 	fprintf(stderr, "%s:\t-M = monochrome output\n", pgm);
362         fprintf(stderr, "%s:\t-n N = horizontal shift count - must be multiple of 2 for 4:2:0, multiple of 4 for 4:1:1!\n", pgm);
363         fprintf(stderr, "%s:\t-y num = Y-only horizontal shift count - need not be even\n", pgm);
364         fprintf(stderr, "%s:\t\tpositive count shifts right\n",pgm);
365         fprintf(stderr, "%s:\t\t0 passes the data thru unchanged\n",pgm);
366         fprintf(stderr, "%s:\t\tnegative count shifts left\n", pgm);
367 	fprintf(stderr, "%s:\t-N num = vertical shift count - must be multiple of 4 for 4:2:0, multiple of 2 for 4:1:1!\n", pgm);
368 	fprintf(stderr, "%s:\t-Y num = Y-only vertical shift count - multiple of 2 for interlaced material\n", pgm);
369 	fprintf(stderr, "%s:\t\tnegative count shifts up\n", pgm);
370 	fprintf(stderr, "%s:\t\t0 does no vertical shift (is ignored)\n", pgm);
371 	fprintf(stderr, "%s:\t\tpositive count shifts down\n", pgm);
372 	fprintf(stderr, "%s:\t-b creates black border\n", pgm);
373 	fprintf(stderr, "%s:\tShifting is done before border creation\n", pgm);
374         fprintf(stderr, "%s:\t-v print input stream info\n", pgm);
375         fprintf(stderr, "%s:\t-h print this usage summary\n", pgm);
376         exit(1);
377         }
378