1 /*
2  *  import_mpeg2.c
3  *
4  *  Copyright (C) Thomas Oestreich - June 2001
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    "import_mpeg2.so"
25 #define MOD_VERSION "v0.4.0 (2003-10-02)"
26 #define MOD_CODEC   "(video) MPEG2"
27 
28 #include "transcode.h"
29 
30 static int verbose_flag = TC_QUIET;
31 static int capability_flag = TC_CAP_RGB | TC_CAP_YUV | TC_CAP_VID;
32 
33 #define MOD_PRE mpeg2
34 #include "import_def.h"
35 
36 
37 char import_cmd_buf[TC_BUF_MAX];
38 
39 typedef struct tbuf_t {
40 	int off;
41 	int len;
42 	char *d;
43 } tbuf_t;
44 
45 // m2v passthru
46 static int can_read = 1;
47 static tbuf_t tbuf;
48 static int m2v_passthru=0;
49 static FILE *f; // video fd
50 
51 
52 /* ------------------------------------------------------------
53  *
54  * open stream
55  *
56  * ------------------------------------------------------------*/
57 
58 MOD_open
59 {
60 
61   char requant_buf[256];
62   long sret;
63 
64   if(param->flag != TC_VIDEO) return(TC_IMPORT_ERROR);
65 
66   if(vob->ts_pid1==0) { // no transport stream
67 
68     switch(vob->im_v_codec) {
69 
70     case CODEC_RGB:
71 
72       sret = tc_snprintf(import_cmd_buf, TC_BUF_MAX,
73 			 "tcextract -x mpeg2 -i \"%s\" -d %d |"
74 			 " tcdecode -x mpeg2 -d %d",
75 			 vob->video_in_file, vob->verbose, vob->verbose);
76       if (sret < 0)
77 	return(TC_IMPORT_ERROR);
78 
79       break;
80 
81     case CODEC_YUV:
82 
83       sret = tc_snprintf(import_cmd_buf, TC_BUF_MAX,
84 			 "tcextract -x mpeg2 -i \"%s\" -d %d |"
85 			 " tcdecode -x mpeg2 -d %d -y yuv420p",
86 			 vob->video_in_file, vob->verbose, vob->verbose);
87       if (sret < 0)
88 	return(TC_IMPORT_ERROR);
89 
90       break;
91 
92     case CODEC_RAW:
93     case CODEC_RAW_YUV:
94 
95 	memset(requant_buf, 0, sizeof (requant_buf));
96 	if (vob->m2v_requant > M2V_REQUANT_FACTOR) {
97 	  tc_snprintf (requant_buf, 256, " | tcrequant -d %d -f %f ",
98                     vob->verbose, vob->m2v_requant);
99 	}
100 	m2v_passthru=1;
101 
102         sret = tc_snprintf(import_cmd_buf, TC_BUF_MAX,
103 			   "tcextract -x mpeg2 -i \"%s\" -d %d%s",
104 			   vob->video_in_file, vob->verbose, requant_buf);
105         if (sret < 0)
106 	  return(TC_IMPORT_ERROR);
107 
108 	break;
109     }
110 
111   } else {
112 
113     switch(vob->im_v_codec) {
114 
115     case CODEC_RGB:
116 
117       sret = tc_snprintf(import_cmd_buf, TC_BUF_MAX,
118 			 "tccat -i \"%s\" -d %d -n 0x%x |"
119 			 " tcextract -x mpeg2 -t m2v -d %d |"
120 			 " tcdecode -x mpeg2 -d %d",
121 			 vob->video_in_file, vob->verbose, vob->ts_pid1,
122 			 vob->verbose, vob->verbose);
123       if (sret < 0)
124 	return(TC_IMPORT_ERROR);
125 
126       break;
127 
128     case CODEC_YUV:
129 
130       sret = tc_snprintf(import_cmd_buf, TC_BUF_MAX,
131 			 "tccat -i \"%s\" -d %d -n 0x%x |"
132 			 " tcextract -x mpeg2 -t m2v -d %d |"
133 			 " tcdecode -x mpeg2 -d %d -y yuv420p",
134 			 vob->video_in_file, vob->verbose,vob->ts_pid1,
135 			 vob->verbose, vob->verbose);
136       if (sret < 0)
137 	return(TC_IMPORT_ERROR);
138 
139       break;
140     }
141   }
142 
143   // print out
144   if(verbose_flag) tc_log_info(MOD_NAME, "%s", import_cmd_buf);
145 
146   param->fd = NULL;
147 
148   // popen
149   if((param->fd = popen(import_cmd_buf, "r"))== NULL) {
150     tc_log_perror(MOD_NAME, "popen RGB stream");
151     return(TC_IMPORT_ERROR);
152   }
153 
154   // we handle the read;
155   if (m2v_passthru) {
156     f = param->fd;
157     param->fd = NULL;
158 
159     tbuf.d = tc_malloc (SIZE_RGB_FRAME);
160     tbuf.len = SIZE_RGB_FRAME;
161     tbuf.off = 0;
162 
163     if ((tbuf.len = fread(tbuf.d, 1, tbuf.len, f)) < 0)
164       return(TC_IMPORT_ERROR);
165 
166     // find a sync word
167     while (tbuf.off+4<tbuf.len) {
168       if (tbuf.d[tbuf.off+0]==0x0 && tbuf.d[tbuf.off+1]==0x0 &&
169 	  tbuf.d[tbuf.off+2]==0x1 &&
170 	  (unsigned char)tbuf.d[tbuf.off+3]==0xb3) break;
171       else tbuf.off++;
172     }
173     if (tbuf.off+4>=tbuf.len)  {
174       tc_log_warn(MOD_NAME, "Internal Error. No sync word");
175       return (TC_IMPORT_ERROR);
176     }
177 
178   }
179 
180   return(TC_IMPORT_OK);
181 }
182 
183 /* ------------------------------------------------------------
184  *
185  * decode  stream
186  *
187  * ------------------------------------------------------------*/
188 
189 MOD_decode{
190 
191   if(param->flag == TC_VIDEO && m2v_passthru) {
192 
193     // ---------------------------------------------------
194     // This code splits the MPEG2 elementary stream
195     // into packets. It sets the type of the packet
196     // as an frame attribute.
197     // I frames (== Key frames) are not only I frames,
198     // they also carry the sequence headers in the packet.
199     // ---------------------------------------------------
200 
201     int ID, start_seq, start_pic, pic_type;
202 
203     ID = tbuf.d[tbuf.off+3]&0xff;
204 
205     switch (ID) {
206       case 0xb3: // sequence
207 	start_seq = tbuf.off;
208 
209 	// look for pic header
210 	while (tbuf.off+6<tbuf.len) {
211 
212 	  if (tbuf.d[tbuf.off+0]==0x0 && tbuf.d[tbuf.off+1]==0x0 &&
213 	      tbuf.d[tbuf.off+2]==0x1 && tbuf.d[tbuf.off+3]==0x0 &&
214 	      ((tbuf.d[tbuf.off+5]>>3)&0x7)>1 &&
215 	      ((tbuf.d[tbuf.off+5]>>3)&0x7)<4) {
216 	    if (verbose & TC_DEBUG)
217               tc_log_warn(MOD_NAME, "Completed a sequence + I frame from %d -> %d",
218 		      start_seq, tbuf.off);
219 
220 	    param->attributes |= TC_FRAME_IS_KEYFRAME;
221 	    param->size = tbuf.off-start_seq;
222 
223 	    // spit frame out
224 	    ac_memcpy(param->buffer, tbuf.d+start_seq, param->size);
225 	    memmove(tbuf.d, tbuf.d+param->size, tbuf.len-param->size);
226 	    tbuf.off = 0;
227 	    tbuf.len -= param->size;
228 
229 	    if (verbose & TC_DEBUG)
230               tc_log_info(MOD_NAME, "%02x %02x %02x %02x",
231 			      tbuf.d[0]&0xff, tbuf.d[1]&0xff,
232                               tbuf.d[2]&0xff, tbuf.d[3]&0xff);
233 	    return TC_IMPORT_OK;
234 	  }
235 	  else tbuf.off++;
236 	}
237 
238 	// not enough data.
239 	if (tbuf.off+6 >= tbuf.len) {
240 
241 	  if (verbose & TC_DEBUG) tc_log_info(MOD_NAME, "Fetching in Sequence");
242 	  memmove (tbuf.d, tbuf.d+start_seq, tbuf.len - start_seq);
243 	  tbuf.len -= start_seq;
244 	  tbuf.off = 0;
245 
246 	  if (can_read>0) {
247 	    can_read = fread (tbuf.d+tbuf.len, SIZE_RGB_FRAME-tbuf.len, 1, f);
248 	    tbuf.len += (SIZE_RGB_FRAME-tbuf.len);
249 	  } else {
250 	    tc_log_info(MOD_NAME, "No 1 Read %d", can_read);
251 	    /* XXX: Flush buffers */
252 	    return TC_IMPORT_ERROR;
253 	  }
254 	}
255 	break;
256 
257       case 0x00: // pic header
258 
259 	start_pic = tbuf.off;
260 	pic_type = (tbuf.d[start_pic+5] >> 3) & 0x7;
261 	tbuf.off++;
262 
263 	while (tbuf.off+6<tbuf.len) {
264 	  if (tbuf.d[tbuf.off+0]==0x0 && tbuf.d[tbuf.off+1]==0x0 &&
265 	      tbuf.d[tbuf.off+2]==0x1 &&
266 	      (unsigned char)tbuf.d[tbuf.off+3]==0xb3) {
267 	    if (verbose & TC_DEBUG)
268                tc_log_info(MOD_NAME, "found a last P or B frame %d -> %d",
269                        start_pic, tbuf.off);
270 
271 	    param->size = tbuf.off - start_pic;
272 
273 	    ac_memcpy(param->buffer, tbuf.d+start_pic, param->size);
274 	    memmove(tbuf.d, tbuf.d+param->size, tbuf.len-param->size);
275 	    tbuf.off = 0;
276 	    tbuf.len -= param->size;
277 
278 	    return TC_IMPORT_OK;
279 
280 	  } else if // P or B frame
281 	    (tbuf.d[tbuf.off+0]==0x0 && tbuf.d[tbuf.off+1]==0x0 &&
282 	     tbuf.d[tbuf.off+2]==0x1 && tbuf.d[tbuf.off+3]==0x0 &&
283 	     ((tbuf.d[tbuf.off+5]>>3)&0x7)>1 &&
284 	     ((tbuf.d[tbuf.off+5]>>3)&0x7)<4) {
285 	      if (verbose & TC_DEBUG)
286                 tc_log_info(MOD_NAME, "found a P or B frame from %d -> %d",
287 		        start_pic, tbuf.off);
288 
289 	      param->size = tbuf.off - start_pic;
290 
291 	      ac_memcpy(param->buffer, tbuf.d+start_pic, param->size);
292 	      memmove(tbuf.d, tbuf.d+param->size, tbuf.len-param->size);
293 	      tbuf.off = 0;
294 	      tbuf.len -= param->size;
295 
296 	      return TC_IMPORT_OK;
297 
298 	    } else tbuf.off++;
299 
300 	  // not enough data.
301 	  if (tbuf.off+6 >= tbuf.len) {
302 
303 	    memmove (tbuf.d, tbuf.d+start_pic, tbuf.len - start_pic);
304 	    tbuf.len -= start_pic;
305 	    tbuf.off = 0;
306 
307 	    if (can_read>0) {
308 	      can_read = fread (tbuf.d+tbuf.len, SIZE_RGB_FRAME-tbuf.len, 1, f);
309 	      tbuf.len += (SIZE_RGB_FRAME-tbuf.len);
310 	    } else {
311 	      tc_log_info(MOD_NAME, "No 1 Read %d", can_read);
312 	      /* XXX: Flush buffers */
313 	      return TC_IMPORT_ERROR;
314 	    }
315 	  }
316 	}
317 	break;
318       default:
319 	// should not get here
320 	tc_log_warn(MOD_NAME, "Default case");
321 	tbuf.off++;
322 	break;
323     }
324 
325 
326   }
327   return(TC_IMPORT_OK);
328 }
329 
330 /* ------------------------------------------------------------
331  *
332  * close stream
333  *
334  * ------------------------------------------------------------*/
335 
336 MOD_close
337 {
338 
339     if(param->fd != NULL) pclose(param->fd);
340     if(f != NULL) pclose(f);
341     param->fd = f = NULL;
342 
343     return(TC_IMPORT_OK);
344 }
345