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