1 /*
2 * export_mpeg2enc.c
3 *
4 * Copyright (C) Gerhard Monzel - January 2002
5 *
6 * This file is part of transcode, a video stream processing tool
7 *
8 * transcode is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * transcode is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with GNU Make; see the file COPYING. If not, write to
20 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 */
23
24 #define MOD_NAME "export_mpeg2enc.so"
25 #define MOD_VERSION "v1.1.10 (2003-10-30)"
26 #define MOD_CODEC "(video) MPEG 1/2"
27
28 #include "transcode.h"
29 #include "libtcvideo/tcvideo.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36
37 #if defined(HAVE_MJPEGTOOLS_INC)
38 #include "yuv4mpeg.h"
39 #include "mpegconsts.h"
40 #else
41 #include "mjpegtools/yuv4mpeg.h"
42 #include "mjpegtools/mpegconsts.h"
43 #endif
44
45 static int verbose_flag=TC_QUIET;
46 static int capability_flag=TC_CAP_YUV|TC_CAP_RGB;
47
48 #define MOD_PRE mpeg2enc
49 #include "export_def.h"
50
51 static y4m_stream_info_t y4mstream;
52
53 static FILE *sa_ip = NULL;
54 static int sa_width = 0;
55 static int sa_height = 0;
56 static int sa_size_l = 0;
57 static int sa_size_c = 0;
58 static TCVHandle tcvhandle = 0;
59 static ImageFormat srcfmt;
60
61 #define Y4M_LINE_MAX 256
62 #define Y4M_MAGIC "YUV4MPEG2"
63 #define Y4M_FRAME_MAGIC "FRAME"
64
65
y4m_snprint_xtags(char * s,int maxn,y4m_xtag_list_t * xtags)66 static int y4m_snprint_xtags(char *s, int maxn, y4m_xtag_list_t *xtags)
67 {
68 int i, room;
69
70 for (i = 0, room = maxn - 1; i < y4m_xtag_count(xtags); i++) {
71 int n = tc_snprintf(s, room + 1, " %s", y4m_xtag_get(xtags, i));
72 if ((n < 0) || (n > room)) return Y4M_ERR_HEADER;
73 s += n;
74 room -= n;
75 }
76 s[0] = '\n'; /* finish off header with newline */
77 s[1] = '\0'; /* ...and end-of-string */
78 return Y4M_OK;
79 }
80
y4m_write_stream_header2(FILE * fd,y4m_stream_info_t * i)81 static int y4m_write_stream_header2(FILE *fd, y4m_stream_info_t *i)
82 {
83 char s[Y4M_LINE_MAX+1];
84 int n;
85 int err;
86
87 y4m_ratio_t tmpframerate = y4m_si_get_framerate(i);
88 y4m_ratio_t tmpsamplerate = y4m_si_get_sampleaspect(i);
89 y4m_ratio_reduce(&tmpframerate);
90 y4m_ratio_reduce(&tmpsamplerate);
91 n = tc_snprintf(s, sizeof(s), "%s W%d H%d F%d:%d I%s A%d:%d",
92 Y4M_MAGIC,
93 y4m_si_get_width(i),
94 y4m_si_get_height(i),
95 y4m_si_get_framerate(i).n, y4m_si_get_framerate(i).d,
96 (y4m_si_get_interlace(i) == Y4M_ILACE_NONE) ? "p" :
97 (y4m_si_get_interlace(i) == Y4M_ILACE_TOP_FIRST) ? "t" :
98 (y4m_si_get_interlace(i) == Y4M_ILACE_BOTTOM_FIRST) ? "b" : "?",
99 y4m_si_get_sampleaspect(i).n, y4m_si_get_sampleaspect(i).d);
100 if (n < 0) return Y4M_ERR_HEADER;
101 if ((err = y4m_snprint_xtags(s + n, sizeof(s) - n - 1, y4m_si_xtags(i)))
102 != Y4M_OK)
103 return err;
104 /* zero on error */
105 return (fwrite(s, strlen(s), 1, fd) ? Y4M_OK : Y4M_ERR_SYSTEM);
106
107 }
108
y4m_write_frame_header2(FILE * fd,y4m_frame_info_t * i)109 static int y4m_write_frame_header2(FILE *fd, y4m_frame_info_t *i)
110 {
111 char s[Y4M_LINE_MAX+1];
112 int n;
113 int err;
114
115 n = snprintf(s, sizeof(s), "%s", Y4M_FRAME_MAGIC);
116 if (n < 0) return Y4M_ERR_HEADER;
117 if ((err = y4m_snprint_xtags(s + n, sizeof(s) - n - 1, y4m_fi_xtags(i)))
118 != Y4M_OK)
119 return err;
120 /* zero on error */
121 return (fwrite(s, strlen(s), 1, fd) ? Y4M_OK : Y4M_ERR_SYSTEM);
122 }
123
124
125 /* ------------------------------------------------------------
126 *
127 * open outputfile
128 *
129 * ------------------------------------------------------------*/
130
131
132 MOD_open
133 {
134
135 int verb, prof=0;
136 const char *p1, *p2, *p3, *p4;
137 char bitrate[25];
138 //char dar_tag[20];
139 y4m_ratio_t framerate;
140 y4m_ratio_t dar;
141 int frc=0, asr=0;
142 char *tv_type="-n p";
143 char *pulldown="";
144 int fields = (vob->encode_fields == TC_ENCODE_FIELDS_TOP_FIRST
145 || vob->encode_fields == TC_ENCODE_FIELDS_BOTTOM_FIRST);
146
147 /* check for mpeg2enc */
148 if (tc_test_program("mpeg2enc") != 0) return (TC_EXPORT_ERROR);
149
150 if(param->flag == TC_VIDEO)
151 {
152 char buf[PATH_MAX];
153 char buf2[16];
154
155 //note: this is the real framerate of the raw stream
156 framerate = (vob->ex_frc==0) ? mpeg_conform_framerate(vob->ex_fps):mpeg_framerate(vob->ex_frc);
157 asr = (vob->ex_asr<0) ? vob->im_asr:vob->ex_asr;
158 switch (asr) {
159 case 1: dar.n = 1; dar.d = 1; break;
160 case 2: dar = y4m_dar_4_3; break;
161 case 3: dar = y4m_dar_16_9; break;
162 case 4: dar = y4m_dar_221_100; break;
163 case 0: default: dar.n=0; dar.d=0; break;
164 }
165
166 y4m_init_stream_info(&y4mstream);
167 y4m_si_set_framerate(&y4mstream,framerate);
168 if (vob->encode_fields == TC_ENCODE_FIELDS_TOP_FIRST) {
169 y4m_si_set_interlace(&y4mstream, Y4M_ILACE_TOP_FIRST);
170 } else if (vob->encode_fields == TC_ENCODE_FIELDS_BOTTOM_FIRST) {
171 y4m_si_set_interlace(&y4mstream, Y4M_ILACE_BOTTOM_FIRST);
172 } else if (vob->encode_fields == TC_ENCODE_FIELDS_PROGRESSIVE) {
173 y4m_si_set_interlace(&y4mstream, Y4M_ILACE_NONE);
174 }
175 y4m_si_set_sampleaspect(&y4mstream, y4m_guess_sar(vob->ex_v_width, vob->ex_v_height, dar));
176 /*
177 tc_snprintf( dar_tag, 19, "XM2AR%03d", asr );
178 y4m_xtag_add( y4m_si_xtags(&y4mstream), dar_tag );
179 */
180 y4m_si_set_height(&y4mstream, vob->ex_v_height);
181 y4m_si_set_width(&y4mstream, vob->ex_v_width);
182
183 verb = (verbose & TC_DEBUG) ? 2:0;
184
185 //base profile support and coustom setting
186 //-- -F "<base-profile>[,<options_string>]"
187 //-- parameter 1 (base profile) --
188
189 p1 = vob->ex_v_fcc;
190 p2 = vob->ex_a_fcc;
191 p3 = vob->ex_profile_name; //unsupported
192
193 if(verbose_flag & TC_DEBUG) tc_log_info(MOD_NAME, "P1=%s, P2=%s, P3=%s", p1, p2, p3);
194
195 prof = (p1==NULL || strlen(p1) == 0) ? 0:atoi(p1);
196
197
198 //-- adjust frame rate stuff --
199 //-----------------------------
200 if (vob->ex_frc) { // use specified output frame rate code
201 frc = vob->ex_frc;
202 } else { // otherwise we guess based on the frame rate
203 if ((int)(vob->ex_fps*100.0 + 0.01) == (int)(29.97*100.0)) {
204 frc=4;
205 } else if ((int)(vob->ex_fps*100.0 + 0.01) == (int)(23.97*100.0)) {
206 frc=1;
207 } else if ((int)(vob->ex_fps*100.0 + 0.01) == (int)(24.00*100.0)) {
208 frc=2;
209 } else {
210 frc=3; // default is PAL framerate code
211 }
212 }
213 // now set the stream type to either NTSC or PAL based on the
214 // frame rate code
215 if ((frc == 4) || (frc == 1) || (frc == 2)) {
216 tv_type = "-n n";
217 } else {
218 tv_type = "-n p"; // default is PAL
219 }
220
221 //ThOe pulldown?
222 if(vob->pulldown) pulldown="-p";
223
224 //ThOe collect additional parameter
225 if(asr>0)
226 tc_snprintf(buf2, sizeof(buf2), "%s %s -a %d", tv_type, pulldown, asr);
227 else
228 tc_snprintf(buf2, sizeof(buf2), "%s %s", tv_type, pulldown);
229
230 if (p2==NULL) p2="";
231
232 // additional commandline arguments
233 if (vob->ex_v_string==NULL) p4="";
234 else p4=vob->ex_v_string;
235
236 // constant quantizer encoding?
237 if (vob->divxmultipass == 3) {
238 if (vob->video_max_bitrate != 0) {
239 tc_snprintf(bitrate, sizeof(bitrate), "-q %d -b %d", vob->divxbitrate, vob->video_max_bitrate);
240 } else {
241 tc_snprintf(bitrate, sizeof(bitrate), "-q %d", vob->divxbitrate);
242 }
243 } else {
244 tc_snprintf(bitrate, sizeof(bitrate), "-b %d", vob->divxbitrate);
245 }
246
247
248 switch(prof) {
249
250 case 1:
251
252 //Standard VCD. An MPEG1 profile
253 //exactly to the VCD2.0 specification.
254
255 tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -f 1 -F %d %s %s -o \"%s\" %s", verb, fields, frc, buf2, p4, vob->video_out_file, p2);
256 break;
257
258 case 2:
259
260 //User VCD
261
262 tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -q 3 -f 2 -4 2 -2 3 %s -F %d %s -o \"%s\" %s %s", verb, fields, bitrate, frc, buf2, vob->video_out_file, p2, p4);
263 break;
264
265 case 3:
266
267 //Generic MPEG2
268
269 tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -q 3 -f 3 -4 2 -2 3 %s -s -F %d %s -o \"%s\" %s %s", verb, fields, bitrate, frc, buf2, vob->video_out_file, p2, p4);
270 break;
271
272 case 4:
273
274 //Standard SVCD. An MPEG-2 profile
275 //exactly to the SVCD2.0 specification
276
277 if ( !(vob->export_attributes & TC_EXPORT_ATTRIBUTE_VBITRATE) )
278 tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -f 4 -F %d %s -o \"%s\" %s %s", verb, fields, frc, buf2, vob->video_out_file, p2, p4);
279 else
280 tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -f 4 %s -F %d %s -o \"%s\" %s %s", verb, fields, bitrate, frc, buf2, vob->video_out_file, p2, p4);
281 break;
282
283 case 5:
284
285 //User SVCD
286
287 tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -q 3 -f 5 -4 2 -2 3 %s -F %d %s -V 230 -o \"%s\" %s %s", verb, fields, bitrate, frc, buf2, vob->video_out_file, p2, p4);
288 break;
289
290 case 6:
291
292 // Manual parameter mode.
293
294 tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d %s -o \"%s\" %s %s", verb, fields, bitrate, vob->video_out_file, p2?p2:"", p4);
295 break;
296
297 case 8:
298
299 //DVD
300
301 if ( !(vob->export_attributes & TC_EXPORT_ATTRIBUTE_VBITRATE) )
302 tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -f 8 -F %d %s -o \"%s\" %s %s", verb, fields, frc, buf2, vob->video_out_file, p2, p4);
303 else
304 tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -f 8 %s -F %d %s -o \"%s\" %s %s", verb, fields, bitrate, frc, buf2, vob->video_out_file, p2, p4);
305
306 break;
307
308
309 case 0:
310 default:
311
312 //Generic MPEG1
313
314 tc_snprintf(buf, sizeof(buf), "mpeg2enc -v %d -I %d -q 3 -f 0 -4 2 -2 3 %s -F %d %s -o \"%s\" %s %s", verb, fields, bitrate, frc, buf2, vob->video_out_file, p2, p4);
315 break;
316 }
317
318 tc_log_info(MOD_NAME, "%s", buf);
319
320 sa_ip = popen(buf, "w");
321 if (!sa_ip) return(TC_EXPORT_ERROR);
322
323 if( y4m_write_stream_header2( sa_ip, &y4mstream ) != Y4M_OK ){
324 tc_log_perror(MOD_NAME, "write stream header");
325 return(TC_EXPORT_ERROR);
326 }
327
328 // tc_snprintf(buf, sizeof(buf), MENC_HDR, sa_width, sa_height);
329 //fwrite(buf, strlen(buf), 1, sa_ip);
330
331 return(0);
332 }
333
334 if(param->flag == TC_AUDIO) return(0);
335
336 // invalid flag
337 return(TC_EXPORT_ERROR);
338 }
339
340
341 /* ------------------------------------------------------------
342 *
343 * init codec
344 *
345 * ------------------------------------------------------------*/
346
347 MOD_init
348 {
349
350 if(param->flag == TC_VIDEO)
351 {
352 int prof = 0;
353
354 sa_width = vob->ex_v_width;
355 sa_height = vob->ex_v_height;
356 sa_size_l = sa_width * sa_height;
357 sa_size_c = sa_size_l/4;
358
359 if (vob->im_v_codec == CODEC_YUV) {
360 srcfmt = IMG_YUV_DEFAULT;
361 } else if (vob->im_v_codec == CODEC_YUV422) {
362 srcfmt = IMG_YUV422P;
363 } else if (vob->im_v_codec == CODEC_RGB) {
364 srcfmt = IMG_RGB_DEFAULT;
365 } else {
366 tc_log_warn(MOD_NAME, "unsupported video format %d",
367 vob->im_v_codec);
368 return(TC_EXPORT_ERROR);
369 }
370 if (!(tcvhandle = tcv_init())) {
371 tc_log_warn(MOD_NAME, "image conversion init failed");
372 return(TC_EXPORT_ERROR);
373 }
374
375 if (vob->ex_v_fcc) prof = atoi(vob->ex_v_fcc);
376
377 return(0);
378 }
379
380 if(param->flag == TC_AUDIO) return(0);
381
382 // invalid flag
383 return(TC_EXPORT_ERROR);
384 }
385
386 /* ------------------------------------------------------------
387 *
388 * encode and export frame
389 *
390 * ------------------------------------------------------------*/
391
392
393 MOD_encode
394 {
395 y4m_frame_info_t info;
396
397 if(param->flag == TC_VIDEO)
398 {
399 vob_t *vob = tc_get_vob();
400
401 if (!tcv_convert(tcvhandle, param->buffer, param->buffer,
402 vob->ex_v_width, vob->ex_v_height,
403 srcfmt, IMG_YUV420P)) {
404 tc_log_warn(MOD_NAME, "image format conversion failed");
405 return(TC_EXPORT_ERROR);
406 }
407
408 y4m_init_frame_info(&info);
409
410 if( y4m_write_frame_header2( sa_ip, &info ) != Y4M_OK ){
411 tc_log_perror(MOD_NAME, "write stream header");
412 return(TC_EXPORT_ERROR);
413 }
414
415 fwrite(param->buffer, sa_size_l, 1, sa_ip);
416 fwrite(param->buffer + sa_size_l, sa_size_c, 1, sa_ip);
417 fwrite(param->buffer + sa_size_l + sa_size_c, sa_size_c, 1, sa_ip);
418
419 return (0);
420 }
421
422 if(param->flag == TC_AUDIO) return(0);
423
424 // invalid flag
425 return(TC_EXPORT_ERROR);
426 }
427
428 /* ------------------------------------------------------------
429 *
430 * stop encoder
431 *
432 * ------------------------------------------------------------*/
433
434 MOD_stop
435 {
436 if(param->flag == TC_VIDEO) {
437 return (0);
438 }
439
440 if(param->flag == TC_AUDIO) return (0);
441 return(TC_EXPORT_ERROR);
442 }
443
444 /* ------------------------------------------------------------
445 *
446 * close codec
447 *
448 * ------------------------------------------------------------*/
449
450 MOD_close
451 {
452
453 if(param->flag == TC_AUDIO) return (0);
454
455 if(param->flag == TC_VIDEO)
456 {
457 if (sa_ip) pclose(sa_ip);
458 sa_ip = NULL;
459
460 tcv_free(tcvhandle);
461 tcvhandle = 0;
462
463 return(0);
464 }
465
466 return(TC_EXPORT_ERROR);
467 }
468
469