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