1 /* lav2yuv - stream any lav input file to stdout as YUV4MPEG data */
2 
3 /* Copyright (C) 2000, Rainer Johanni, Andrew Stevens */
4 /* - added scene change detection code 2001, pHilipp Zabel */
5 /* - broke some common code out to lav_common.c and lav_common.h,
6  *   July 2001, Shawn Sulma <lavtools@athos.cx>.  In doing this, I
7  *   repackaged the numerous globals into three structs that get passed into
8  *   the relevant functions.  See lav_common.h for a bit more information.
9  *   Helpful feedback is welcome.
10  */
11 /* - removed a lot of subsumed functionality and unnecessary cruft
12  *   March 2002, Matthew Marjanovic <maddog@mir.com>.
13  */
14 
15 /*
16    This program is free software; you can redistribute it and/or modify
17    it under the terms of the GNU General Public License as published by
18    the Free Software Foundation; either version 2 of the License, or
19    (at your option) any later version.
20 
21    This program is distributed in the hope that it will be useful,
22    but WITHOUT ANY WARRANTY; without even the implied warranty of
23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24    GNU General Public License for more details.
25 
26    You should have received a copy of the GNU General Public License
27    along with this program; if not, write to the Free Software
28    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 */
30 
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 
35 #include "lav_common.h"
36 #include <stdio.h>
37 
38 void error(char *text);
39 void Usage(char *str);
40 void streamout(void);
41 
42 int verbose = 1;
43 
44 EditList el;
45 
Usage(char * str)46 void Usage(char *str)
47 {
48    fprintf(stderr,
49    "Usage: %s [params] inputfiles\n"
50    "   where possible params are:\n"
51    "   -m         Force mono-chrome\n"
52    "   -S list.el Output a scene list with scene detection\n"
53    "   -T num     Set scene detection threshold to num (default: 4)\n"
54    "   -D num     Width decimation to use for scene detection (default: 2)\n"
55    "   -A w:h     Set output sample aspect ratio\n"
56    "              (default:  read from DV files, or guess for MJPEG)\n"
57    "   -P w:h     Declare the intended display aspect ratio (used to guess\n"
58    "              the sample aspect ratio).  Common values are 4:3 and 16:9.\n"
59    "              (default:  read from DV files, or assume 4:3 for MJPEG)\n"
60    "   -C chroma  Set output chroma (default: '420jpeg')\n"
61    "              '420jpeg', '420mpeg2', '420paldv', '422', '411' are available\n"
62    "   -o num     Frame offset - skip num frames in the beginning\n"
63    "              if num is negative, all but the last num frames are skipped\n"
64    "   -f num     Only num frames are written to stdout (0 means all frames)\n"
65    "   -c         Conceal corrupt jpeg frames by repeating previous frame\n"
66    "   -x         Exchange fields\n",
67   str);
68    exit(0);
69 }
70 
71 static int lum_mean;
72 static int last_lum_mean;
73 static int delta_lum;
74 static int scene_num;
75 static int scene_start;
76 
77 LavParam param;
78 uint8_t *frame_bufs[6];
79 
80 static int conceal_errframes;
81 static int altbuf;
82 
streamout(void)83 void streamout(void)
84 {
85 
86 	int framenum, movie_num=0, index[MAX_EDIT_LIST_FILES];
87 	int concealnum = 0;
88 	long int oldframe=N_EL_FRAME(el.frame_list[0])-1;
89 	FILE *fd=NULL;
90 
91 	int fd_out = 1; /* stdout. */
92 
93 	char temp[32];
94 
95 	y4m_stream_info_t streaminfo;
96 	y4m_frame_info_t frameinfo;
97 
98 	y4m_init_stream_info(&streaminfo);
99 	y4m_init_frame_info(&frameinfo);
100 
101 
102 	if (!param.scenefile)
103 		writeoutYUV4MPEGheader(fd_out, &param, el, &streaminfo);
104 	scene_num = scene_start = 0;
105 	if (param.scenefile)
106 	{
107 		int num_files;
108 		int i;
109 
110 		param.output_width =
111 			param.output_width / param.scene_detection_decimation;
112 
113 		/*  Output file */
114 
115 		unlink(param.scenefile);
116 		fd = fopen(param.scenefile,"w");
117 		if(fd==0)
118 		{
119 			mjpeg_error_exit1("Can not open %s - no edit list written!",param.scenefile);
120 		}
121 		fprintf(fd,"LAV Edit List\n");
122 		fprintf(fd,"%s\n",el.video_norm=='n'?"NTSC":"PAL");
123 		for(i=0;i<MAX_EDIT_LIST_FILES;i++) index[i] = -1;
124 		for(i=0;i<el.video_frames;i++) index[N_EL_FILE(el.frame_list[i])] = 1;
125 		num_files = 0;
126 		for(i=0;i<MAX_EDIT_LIST_FILES;i++)
127 			if(index[i]==1)
128 				index[i] = num_files++;
129 		fprintf(fd,"%d\n",num_files);
130 		for(i=0;i<MAX_EDIT_LIST_FILES;i++)
131 			if(index[i]>=0)
132 				fprintf(fd,"%s\n",el.video_file_list[i]);
133 		sprintf(temp,"%d %ld",
134 				index[N_EL_FILE(el.frame_list[0])],
135 				N_EL_FRAME(el.frame_list[0]));
136 	}
137 
138 	for (framenum = param.offset; framenum < (param.offset + param.frames); ++framenum)
139 	{
140 		int rf;
141 		uint8_t **read_buf;
142 		uint8_t **frame_buf;
143 		if(conceal_errframes)
144 			read_buf = &frame_bufs[4 * (altbuf ^ 1)];
145 		else
146 			read_buf = &frame_bufs[0];
147 
148 		rf = readframe(framenum, read_buf, &param, el);
149 		if(conceal_errframes) {
150 			if(rf == 2) {  // corrupt frame; repeat previous
151 				mjpeg_debug("corrupt jpeg data in frame %d; repeating previous frame.", framenum);
152 				frame_buf = &frame_bufs[4 * altbuf];
153 				concealnum++;
154 			} else {  // use new frame
155 				frame_buf = read_buf;
156 				altbuf ^= 1;
157 			}
158 		} else {
159 			if(rf == 2)
160 				mjpeg_debug("corrupt jpeg data in frame %d", framenum);
161 			frame_buf = &frame_bufs[0];
162 		}
163 
164 		if (param.scenefile)
165 		{
166 			lum_mean =
167 				luminance_mean(frame_buf,
168 							   param.output_width, param.output_height );
169 			if (framenum == 0)
170 			{
171 				delta_lum = 0;
172 				movie_num = N_EL_FILE(el.frame_list[0]);
173 			}
174 			else
175 				delta_lum = abs(lum_mean - last_lum_mean);
176 			last_lum_mean = lum_mean;
177 
178 			mjpeg_debug( "frame %d/%ld, lum_mean %d, delta_lum %d        ", framenum,
179 						 el.video_frames, lum_mean, delta_lum);
180 
181 			if (delta_lum > param.delta_lum_threshold || index[N_EL_FILE(el.frame_list[framenum])] != movie_num ||
182 				oldframe+1 != N_EL_FRAME(el.frame_list[framenum])) {
183 				if (delta_lum <= param.delta_lum_threshold)
184 					fprintf(fd,"%c",'+'); /* Same scene, new file */
185 				fprintf(fd,"%s %ld\n", temp, N_EL_FRAME(el.frame_list[framenum-1]));
186 				sprintf(temp,"%d %ld",index[N_EL_FILE(el.frame_list[framenum])], N_EL_FRAME(el.frame_list[framenum]));
187 				scene_start = framenum;
188 				scene_num++;
189 			}
190 
191 			oldframe = N_EL_FRAME(el.frame_list[framenum]);
192 			movie_num = N_EL_FILE(el.frame_list[framenum]);
193 
194 		}
195 		else
196 		{
197 		  int i;
198 		  i = y4m_write_frame(fd_out,
199 				      &streaminfo, &frameinfo, frame_buf);
200 		  if (i != Y4M_OK)
201 		    mjpeg_error("Failed to write frame: %s", y4m_strerr(i));
202 		}
203 	}
204 	mjpeg_info( "Repeated frames (for error concealment): %d", concealnum);
205 	if (param.scenefile)
206 	{
207 		fprintf(fd, "%s %ld\n", temp, N_EL_FRAME(el.video_frames-1));
208 		fclose(fd);
209 		mjpeg_info( "Editlist written to %s", param.scenefile);
210 	}
211 
212 y4m_fini_frame_info(&frameinfo);
213 }
214 
main(argc,argv)215 int main(argc, argv)
216 	int argc;
217 	char *argv[];
218 {
219 	int n, nerr = 0;
220 	int exchange_fields = 0;
221 
222 	y4m_accept_extensions(1);
223 
224 	param.offset = 0;
225 	param.frames = 0;
226 	param.mono = 0;
227 	param.scenefile = NULL;
228 	param.delta_lum_threshold = 4;
229 	param.scene_detection_decimation = 2;
230 	param.output_width = 0;
231 	param.output_height = 0;
232 	param.interlace = -1;
233 	param.sar = y4m_sar_UNKNOWN;
234 	param.dar = y4m_dar_4_3;
235 	param.chroma = Y4M_UNKNOWN;
236 
237 	while ((n = getopt(argc, argv, "xmYv:S:T:D:o:f:P:A:C:c")) != EOF) {
238 		switch (n) {
239 
240 		case 'v':
241 			verbose = atoi(optarg);
242 			if (verbose < 0 ||verbose > 2) {
243 				mjpeg_error( "-v option requires arg 0, 1, or 2");
244 				nerr++;
245 			}
246 			break;
247 		case 'm':
248 			param.mono = 1;
249 			break;
250 		case 'x':
251 			exchange_fields = 1;
252 			break;
253 		case 'c':
254 			conceal_errframes = 1;
255 			break;
256 		case 'S':
257 			param.scenefile = optarg;
258 			break;
259 		case 'T':
260 			param.delta_lum_threshold = atoi(optarg);
261 			break;
262 		case 'D':
263 			param.scene_detection_decimation = atoi(optarg);
264 			break;
265 		case 'o':
266 			param.offset = atoi(optarg);
267 			break;
268 		case 'f':
269 			param.frames = atoi(optarg);
270 			break;
271 
272 		case 'A':
273 		  if (y4m_parse_ratio(&(param.sar), optarg)) {
274 		    mjpeg_error("Couldn't parse ratio '%s'", optarg);
275 		    nerr++;
276 		  }
277 		  break;
278 		case 'P':
279 		  if (y4m_parse_ratio(&(param.dar), optarg)) {
280 		    mjpeg_error("Couldn't parse ratio '%s'", optarg);
281 		    nerr++;
282 		  }
283 			break;
284 		case 'C':
285 		  param.chroma = y4m_chroma_parse_keyword(optarg);
286 		  switch (param.chroma) {
287 		  case Y4M_CHROMA_420JPEG:
288 		  case Y4M_CHROMA_420MPEG2:
289 		  case Y4M_CHROMA_420PALDV:
290 		  case Y4M_CHROMA_422:
291 		  case Y4M_CHROMA_411:
292 		    break;
293 		  default:
294 		    mjpeg_error("Unsupported chroma '%s'", optarg);
295 		    nerr++;
296 		    break;
297 		  }
298 		  break;
299 		default:
300 			nerr++;
301 		}
302 	}
303 
304 	if (optind >= argc)
305 		nerr++;
306 
307 	if (nerr)
308 		Usage(argv[0]);
309 
310 	(void)mjpeg_default_handler_verbosity(verbose);
311 
312 	/* Open editlist */
313 
314 	read_video_files(argv + optind, argc - optind, &el,0);
315 
316 	param.output_width = el.video_width;
317 	param.output_height = el.video_height;
318 	if(exchange_fields) {
319 	  if(el.video_inter == Y4M_ILACE_BOTTOM_FIRST) {
320 		mjpeg_info("Exchange from BOTTOM_FIRST to TOP_FIRST");
321 	  	el.video_inter = Y4M_ILACE_TOP_FIRST;
322 	  }
323 	  else if(el.video_inter == Y4M_ILACE_TOP_FIRST) {
324 		mjpeg_info("Exchange from TOP_FIRST to BOTTOM_FIRST");
325 	  	el.video_inter = Y4M_ILACE_BOTTOM_FIRST;
326 	  }
327 	  else {
328 		mjpeg_warn("Video NOT INTERLACED! Could not exchange fields");
329 	  }
330 	}
331 
332 	if (param.offset < 0) {
333 		param.offset = el.video_frames + param.offset;
334 	}
335 	if (param.offset >= el.video_frames) {
336 		mjpeg_error_exit1("offset greater than # of frames in input");
337 	}
338 	if ((param.offset + param.frames) > el.video_frames) {
339 		mjpeg_warn("input too short for -f %d", param.frames);
340 		param.frames = el.video_frames - param.offset;
341 	}
342 	if (param.frames == 0) {
343 		param.frames = el.video_frames - param.offset;
344 	}
345 
346 	param.interlace = el.video_inter;
347 
348 	init(&param, &frame_bufs[0] /*&buffer*/);
349 	if(conceal_errframes)
350 		init(&param, &frame_bufs[4] /*&buffer*/);
351 
352 #ifdef HAVE_LIBDV
353 	lav_init_dv_decoder();
354 #endif
355 	if (param.delta_lum_threshold != -1)
356 	{
357 		streamout();
358 	}
359 	else
360 	{
361 		write_edit_list(param.scenefile, 0, el.video_frames, &el);
362 	}
363 
364 	return 0;
365 }
366