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