diff options
Diffstat (limited to 'debian/transcode/transcode-1.1.7/import/import_vob.c')
| -rw-r--r-- | debian/transcode/transcode-1.1.7/import/import_vob.c | 676 |
1 files changed, 676 insertions, 0 deletions
diff --git a/debian/transcode/transcode-1.1.7/import/import_vob.c b/debian/transcode/transcode-1.1.7/import/import_vob.c new file mode 100644 index 00000000..27c964ca --- /dev/null +++ b/debian/transcode/transcode-1.1.7/import/import_vob.c @@ -0,0 +1,676 @@ +/* + * import_vob.c + * + * Copyright (C) Thomas Oestreich - June 2001 + * + * This file is part of transcode, a video stream processing tool + * + * transcode is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * transcode is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#define MOD_NAME "import_vob.so" +#define MOD_VERSION "v0.6.1 (2006-05-02)" +#define MOD_CODEC "(video) MPEG-2 | (audio) MPEG/AC3/PCM | (subtitle)" + +#include "transcode.h" + +#include "libtc/libtc.h" +#include "libtc/optstr.h" + +/*%* + *%* DESCRIPTION + *%* This module imports audio/video from VOB files. If you need direct + *%* DVD access, use import_dvd module. + *%* + *%* #BUILD-DEPENDS + *%* + *%* #DEPENDS + *%* + *%* PROCESSING + *%* import/demuxer + *%* + *%* MEDIA + *%* video, audio + *%* + *%* #INPUT + *%* + *%* OUTPUT + *%* YUV420P*, RGB24, PCM, AC3 + *%* + *%* OPTION + *%* nodemux (flag) + *%* skip demuxing processing stage. This sometimes improves A/V sync. + *%*/ + +static int verbose_flag = TC_QUIET; +static int capability_flag = TC_CAP_VID | TC_CAP_RGB | TC_CAP_YUV | TC_CAP_PCM | TC_CAP_AC3; + +#define MOD_PRE vob +#include "import_def.h" + +#include "ac3scan.h" +#include "demuxer.h" +#include "clone.h" + + + +typedef struct tbuf_t { + int off; + int len; + char *d; +} tbuf_t; + +// m2v passthru +static int can_read = 1; +static tbuf_t tbuf; +static int m2v_passthru=0; +static FILE *f; // video fd + +static int codec, syncf=0; +static int pseudo_frame_size=0, real_frame_size=0, effective_frame_size=0; +static int ac3_bytes_to_go=0; +static FILE *fd; + +/* ------------------------------------------------------------ + * + * open stream + * + * ------------------------------------------------------------*/ + +#define CMD_BUF 256 +MOD_open +{ + const char *logfile="sync.log"; + /* command buffers */ + char seq_buf[CMD_BUF]; + char demux_buf[CMD_BUF]; + char input_buf[TC_BUF_MAX]; + char import_cmd_buf[TC_BUF_MAX]; + + int off=0x80; + + /* common both to all audio and video pipelines */ + if(vob->ps_seq1 != 0 || vob->ps_seq2 != TC_FRAME_LAST) { + tc_snprintf(seq_buf, sizeof(seq_buf), "-S %d,%d-%d", + vob->ps_unit, vob->ps_seq1, vob->ps_seq2); + } else { + strlcpy(seq_buf, "-S 0", 256); + } + + if(param->flag == TC_AUDIO) { + /* common part */ + if(tc_snprintf(input_buf, sizeof(input_buf), + "tccat -i \"%s\" -t vob -d %d -S %d", + vob->audio_in_file, vob->verbose, vob->vob_offset) < 0) { + tc_log_perror(MOD_NAME, "command buffer overflow (input)"); + return(TC_IMPORT_ERROR); + } + + if(vob->demuxer == TC_DEMUX_OFF + || (vob->im_a_string && optstr_lookup(vob->im_a_string, "nodemux"))) { + demux_buf[0] = '\0'; + } else { /* build tcdemux part of pipeline */ + const char *codec = "raw"; + /* select demuxer codec. Ugh. */ + if (vob->im_a_codec == CODEC_AC3) { + codec = "ac3"; + } else { /* vob->im_a_codec == CODEC_PCM */ + if (vob->a_codec_flag == CODEC_AC3) { + codec = "ac3"; + } else if (vob->a_codec_flag == CODEC_MP3 + || vob->a_codec_flag == CODEC_MP2) { + codec = "mp3"; + } else if (vob->a_codec_flag == CODEC_PCM + || vob->a_codec_flag == CODEC_LPCM) { + codec = "pcm"; + } + } + if(tc_snprintf(demux_buf, sizeof(demux_buf), + "| tcdemux -M %d -a %d -x %s %s -d %d", + vob->demuxer, vob->a_track, codec, seq_buf, + vob->verbose) < 0) { + tc_log_perror(MOD_NAME, "command buffer overflow (demux)"); + return(TC_IMPORT_ERROR); + } + } + + codec = vob->im_a_codec; + syncf = vob->sync; + + switch(codec) { + case CODEC_AC3: + if (tc_snprintf(import_cmd_buf, sizeof(import_cmd_buf), + "%s %s" + " | tcextract -t vob -a %d -x ac3 -d %d" + " | tcextract -t raw -x ac3 -d %d", + input_buf, demux_buf, + vob->verbose, vob->a_track, vob->verbose, + vob->verbose) < 0) { + tc_log_perror(MOD_NAME, "command buffer overflow"); + return(TC_IMPORT_ERROR); + } + if(verbose_flag & TC_DEBUG) tc_log_info(MOD_NAME, "AC3->AC3"); + break; + + case CODEC_PCM: + if(vob->a_codec_flag==CODEC_AC3) { + if(tc_snprintf(import_cmd_buf, sizeof(import_cmd_buf), + "%s %s" + " | tcextract -t vob -a %d -x ac3 -d %d" + " | tcdecode -x ac3 -d %d -s %f,%f,%f -A %d", + input_buf, demux_buf, + vob->a_track, vob->verbose, + vob->verbose, + vob->ac3_gain[0], vob->ac3_gain[1], vob->ac3_gain[2], + vob->a52_mode) < 0) { + tc_log_perror(MOD_NAME, "command buffer overflow"); + return(TC_IMPORT_ERROR); + } + if(verbose_flag & TC_DEBUG) tc_log_info(MOD_NAME, "AC3->PCM"); + } + + if(vob->a_codec_flag==CODEC_MP3) { + if(tc_snprintf(import_cmd_buf, sizeof(import_cmd_buf), + "%s %s" + " | tcextract -t vob -a %d -x mp3 -d %d" + " | tcdecode -x mp3 -d %d", + input_buf, demux_buf, + vob->a_track, vob->verbose, + vob->verbose) < 0) { + tc_log_perror(MOD_NAME, "command buffer overflow"); + return(TC_IMPORT_ERROR); + } + if(verbose_flag & TC_DEBUG) tc_log_info(MOD_NAME, "MP3->PCM"); + } + + if(vob->a_codec_flag==CODEC_MP2) { + if(tc_snprintf(import_cmd_buf, sizeof(import_cmd_buf), + "%s %s" + " | tcextract -t vob -a %d -x mp2 -d %d" + " | tcdecode -x mp2 -d %d", + input_buf, demux_buf, + vob->a_track, vob->verbose, + vob->verbose) < 0) { + tc_log_perror(MOD_NAME, "command buffer overflow"); + return(TC_IMPORT_ERROR); + } + if(verbose_flag & TC_DEBUG) tc_log_info(MOD_NAME, "MP2->PCM"); + } + + if(vob->a_codec_flag==CODEC_PCM || vob->a_codec_flag==CODEC_LPCM) { + if(tc_snprintf(import_cmd_buf, sizeof(import_cmd_buf), + "%s %s" + " | tcextract -t vob -a %d -x pcm -d %d", + input_buf, demux_buf, + vob->a_track, vob->verbose) < 0) { + tc_log_perror(MOD_NAME, "command buffer overflow"); + return(TC_IMPORT_ERROR); + } + if(verbose_flag & TC_DEBUG) tc_log_info(MOD_NAME, "LPCM->PCM"); + } + break; + + default: + tc_log_warn(MOD_NAME, "invalid import codec request 0x%x", codec); + return(TC_IMPORT_ERROR); + } + + // print out + if(verbose_flag) tc_log_info(MOD_NAME, "%s", import_cmd_buf); + + // set to NULL if we handle read + param->fd = NULL; + + // popen + if((fd = popen(import_cmd_buf, "r"))== NULL) { + tc_log_perror(MOD_NAME, "popen PCM stream"); + return(TC_IMPORT_ERROR); + } + return(TC_IMPORT_OK); + } + + if(param->flag == TC_SUBEX) { + + tc_snprintf(demux_buf, sizeof(demux_buf), "-M %d", vob->demuxer); + + codec = vob->im_a_codec; + syncf = vob->sync; + + if(tc_snprintf(import_cmd_buf, TC_BUF_MAX, "tccat -i \"%s\" -t vob -d %d -S %d | tcdemux -a %d -x ps1 %s %s -d %d | tcextract -t vob -a 0x%x -x ps1 -d %d", vob->audio_in_file, vob->verbose, vob->vob_offset, vob->s_track, seq_buf, demux_buf, vob->verbose, (vob->s_track+0x20), vob->verbose) < 0) { + tc_log_perror(MOD_NAME, "command buffer overflow"); + return(TC_IMPORT_ERROR); + } + + if(verbose_flag & TC_DEBUG) tc_log_info(MOD_NAME, "subtitle extraction"); + + // print out + if(verbose_flag) tc_log_info(MOD_NAME, "%s", import_cmd_buf); + + // popen + if((param->fd = popen(import_cmd_buf, "r"))== NULL) { + tc_log_perror(MOD_NAME, "popen subtitle stream"); + return(TC_IMPORT_ERROR); + } + + return(0); + } + + if(param->flag == TC_VIDEO) { + + char requant_buf[256]; + + if (vob->demuxer==TC_DEMUX_SEQ_FSYNC || vob->demuxer==TC_DEMUX_SEQ_FSYNC2) { + + if((logfile=clone_fifo())==NULL) { + tc_log_warn(MOD_NAME, "failed to create a temporary pipe"); + return(TC_IMPORT_ERROR); + } + tc_snprintf(demux_buf, sizeof(demux_buf), "-M %d -f %f -P %s %s %s", vob->demuxer, vob->fps, logfile, ((vob->vob_chunk==0)? "": "-O"), + ((vob->hard_fps_flag==1)?"-H":"")); + } else tc_snprintf(demux_buf, sizeof(demux_buf), "-M %d", vob->demuxer); + + //determine subtream id for sync adjustment + //default is off=0x80 + + off=0x80; + + if(vob->a_codec_flag==CODEC_PCM || vob->a_codec_flag==CODEC_LPCM) + off=0xA0; + if(vob->a_codec_flag==CODEC_MP3 || vob->a_codec_flag==CODEC_MP2) + off=0xC0; + + switch(vob->im_v_codec) { + + case CODEC_RAW: + case CODEC_RAW_YUV: + + memset(requant_buf, 0, sizeof (requant_buf)); + if (vob->m2v_requant > M2V_REQUANT_FACTOR) { + tc_snprintf (requant_buf, 256, " | tcrequant -d %d -f %f ", vob->verbose, vob->m2v_requant); + } + m2v_passthru=1; + + if (tc_snprintf(import_cmd_buf, TC_BUF_MAX, + "tccat -i \"%s\" -t vob -d %d -S %d" + " | tcdemux -s 0x%x -x mpeg2 %s %s -d %d" + " | tcextract -t vob -a %d -x mpeg2 -d %d" + "%s", + vob->video_in_file, vob->verbose, vob->vob_offset, + (vob->a_track+off), seq_buf, demux_buf, vob->verbose, + vob->v_track, vob->verbose, + requant_buf) < 0) { + tc_log_perror(MOD_NAME, "command buffer overflow"); + return(TC_IMPORT_ERROR); + } + break; + case CODEC_RGB: + + if (tc_snprintf(import_cmd_buf, TC_BUF_MAX, "tccat -i \"%s\" -t vob -d %d -S %d | tcdemux -s 0x%x -x mpeg2 %s %s -d %d | tcextract -t vob -a %d -x mpeg2 -d %d | tcdecode -x mpeg2 -d %d", vob->video_in_file, vob->verbose, vob->vob_offset, (vob->a_track+off), seq_buf, demux_buf, vob->verbose, vob->v_track, vob->verbose, vob->verbose) < 0) { + tc_log_perror(MOD_NAME, "command buffer overflow"); + return(TC_IMPORT_ERROR); + } + + break; + + case CODEC_YUV: + + if (tc_snprintf(import_cmd_buf, TC_BUF_MAX, "tccat -i \"%s\" -t vob -d %d -S %d | tcdemux -s 0x%x -x mpeg2 %s %s -d %d | tcextract -t vob -a %d -x mpeg2 -d %d | tcdecode -x mpeg2 -d %d -y yuv420p", vob->video_in_file, vob->verbose, vob->vob_offset, (vob->a_track+off), seq_buf, demux_buf, vob->verbose, vob->v_track, vob->verbose, vob->verbose) < 0) { + tc_log_perror(MOD_NAME, "command buffer overflow"); + return(TC_IMPORT_ERROR); + } + + break; + + default: + + tc_log_warn(MOD_NAME, "Don't know anything about Codec 0x%x", vob->im_v_codec); + if (tc_snprintf(import_cmd_buf, TC_BUF_MAX, "cat /dev/null") < 0) { + tc_log_perror(MOD_NAME, "command buffer overflow"); + return(TC_IMPORT_ERROR); + } + + } + + // print out + if(verbose_flag) tc_log_info(MOD_NAME, "%s", import_cmd_buf); + + param->fd = NULL; + + // popen + if((param->fd = popen(import_cmd_buf, "r"))== NULL) { + tc_log_perror(MOD_NAME, "popen RGB stream"); + return(TC_IMPORT_ERROR); + } + + if (!m2v_passthru && + (vob->demuxer==TC_DEMUX_SEQ_FSYNC || vob->demuxer==TC_DEMUX_SEQ_FSYNC2)) { + + if(clone_init(param->fd)<0) { + if(verbose_flag) tc_log_warn(MOD_NAME, "failed to init stream sync mode"); + return(TC_IMPORT_ERROR); + } else param->fd = NULL; + } + + // we handle the read; + if (m2v_passthru) { + f = param->fd; + param->fd = NULL; + + tbuf.d = tc_malloc (SIZE_RGB_FRAME); + tbuf.len = SIZE_RGB_FRAME; + tbuf.off = 0; + + if ( (tbuf.len = fread(tbuf.d, 1, tbuf.len, f))<0) return -1; + + // find a sync word + while (tbuf.off+4<tbuf.len) { + if (tbuf.d[tbuf.off+0]==0x0 && tbuf.d[tbuf.off+1]==0x0 && + tbuf.d[tbuf.off+2]==0x1 && + (unsigned char)tbuf.d[tbuf.off+3]==0xb3) break; + else tbuf.off++; + } + if (tbuf.off+4>=tbuf.len) { + tc_log_warn(MOD_NAME, "Internal Error. No sync word"); + return (TC_IMPORT_ERROR); + } + + } + + return(0); + } + + return(TC_IMPORT_ERROR); + +} + +/* ------------------------------------------------------------ + * + * decode stream + * + * ------------------------------------------------------------*/ + +MOD_decode +{ + + int ac_bytes=0, ac_off=0; + int num_frames; + + if(param->flag == TC_VIDEO) { + + if (!m2v_passthru && (vob->demuxer==TC_DEMUX_SEQ_FSYNC || vob->demuxer==TC_DEMUX_SEQ_FSYNC2)) { + + if(clone_frame(param->buffer, param->size)<0) { + if(verbose_flag & TC_DEBUG) tc_log_warn(MOD_NAME, "end of stream - failed to sync video frame"); + return(TC_IMPORT_ERROR); + } + } + + // --------------------------------------------------- + // This code splits the MPEG2 elementary stream + // into packets. It sets the type of the packet + // as an frame attribute. + // I frames (== Key frames) are not only I frames, + // they also carry the sequence headers in the packet. + // --------------------------------------------------- + + if (m2v_passthru) { + int ID, start_seq, start_pic, pic_type; + + ID = tbuf.d[tbuf.off+3]&0xff; + + switch (ID) { + case 0xb3: // sequence + start_seq = tbuf.off; + + // look for pic header + while (tbuf.off+6<tbuf.len) { + + if (tbuf.d[tbuf.off+0]==0x0 && tbuf.d[tbuf.off+1]==0x0 && + tbuf.d[tbuf.off+2]==0x1 && tbuf.d[tbuf.off+3]==0x0 && + ((tbuf.d[tbuf.off+5]>>3)&0x7)>1 && + ((tbuf.d[tbuf.off+5]>>3)&0x7)<4) { + if (verbose & TC_DEBUG) + tc_log_info(MOD_NAME, "Completed a sequence + I frame from %d -> %d", + start_seq, tbuf.off); + + param->attributes |= TC_FRAME_IS_KEYFRAME; + param->size = tbuf.off-start_seq; + + // spit frame out + ac_memcpy(param->buffer, tbuf.d+start_seq, param->size); + memmove(tbuf.d, tbuf.d+param->size, tbuf.len-param->size); + tbuf.off = 0; + tbuf.len -= param->size; + + if (verbose & TC_DEBUG) + tc_log_info(MOD_NAME, "%02x %02x %02x %02x", + tbuf.d[0]&0xff, tbuf.d[1]&0xff, tbuf.d[2]&0xff, tbuf.d[3]&0xff); + return TC_IMPORT_OK; + } + else tbuf.off++; + } + + // not enough data. + if (tbuf.off+6 >= tbuf.len) { + + if (verbose & TC_DEBUG) tc_log_info(MOD_NAME, "Fetching in Sequence"); + memmove (tbuf.d, tbuf.d+start_seq, tbuf.len - start_seq); + tbuf.len -= start_seq; + tbuf.off = 0; + + if (can_read>0) { + can_read = fread (tbuf.d+tbuf.len, SIZE_RGB_FRAME-tbuf.len, 1, f); + tbuf.len += (SIZE_RGB_FRAME-tbuf.len); + } else { + tc_log_info(MOD_NAME, "No 1 Read %d", can_read); + /* XXX: Flush buffers */ + return TC_IMPORT_ERROR; + } + } + break; + + case 0x00: // pic header + + start_pic = tbuf.off; + pic_type = (tbuf.d[start_pic+5] >> 3) & 0x7; + tbuf.off++; + + while (tbuf.off+6<tbuf.len) { + if (tbuf.d[tbuf.off+0]==0x0 && tbuf.d[tbuf.off+1]==0x0 && + tbuf.d[tbuf.off+2]==0x1 && + (unsigned char)tbuf.d[tbuf.off+3]==0xb3) { + if (verbose & TC_DEBUG) + tc_log_info(MOD_NAME, "found a last P or B frame %d -> %d", + start_pic, tbuf.off); + + param->size = tbuf.off - start_pic; + + ac_memcpy(param->buffer, tbuf.d+start_pic, param->size); + memmove(tbuf.d, tbuf.d+param->size, tbuf.len-param->size); + tbuf.off = 0; + tbuf.len -= param->size; + + return TC_IMPORT_OK; + + } else if // P or B frame + (tbuf.d[tbuf.off+0]==0x0 && tbuf.d[tbuf.off+1]==0x0 && + tbuf.d[tbuf.off+2]==0x1 && tbuf.d[tbuf.off+3]==0x0 && + ((tbuf.d[tbuf.off+5]>>3)&0x7)>1 && + ((tbuf.d[tbuf.off+5]>>3)&0x7)<4) { + if (verbose & TC_DEBUG) + tc_log_info(MOD_NAME, "found a P or B frame from %d -> %d", + start_pic, tbuf.off); + + param->size = tbuf.off - start_pic; + + ac_memcpy(param->buffer, tbuf.d+start_pic, param->size); + memmove(tbuf.d, tbuf.d+param->size, tbuf.len-param->size); + tbuf.off = 0; + tbuf.len -= param->size; + + return TC_IMPORT_OK; + + } else tbuf.off++; + + // not enough data. + if (tbuf.off+6 >= tbuf.len) { + + memmove (tbuf.d, tbuf.d+start_pic, tbuf.len - start_pic); + tbuf.len -= start_pic; + tbuf.off = 0; + + if (can_read>0) { + can_read = fread (tbuf.d+tbuf.len, SIZE_RGB_FRAME-tbuf.len, 1, f); + tbuf.len += (SIZE_RGB_FRAME-tbuf.len); + } else { + tc_log_info(MOD_NAME, "No 1 Read %d", can_read); + /* XXX: Flush buffers */ + return TC_IMPORT_ERROR; + } + } + } + break; + default: + // should not get here + tc_log_warn(MOD_NAME, "Default case"); + tbuf.off++; + break; + } + + + } + + return(0); + } + + if(param->flag == TC_SUBEX) return(0); + + if(param->flag == TC_AUDIO) { + + switch(codec) { + + case CODEC_AC3: + + // determine frame size at the very beginning of the stream + + if(pseudo_frame_size==0) { + + if(ac3scan(fd, param->buffer, param->size, &ac_off, &ac_bytes, &pseudo_frame_size, &real_frame_size, verbose)!=0) return(TC_IMPORT_ERROR); + + } else { + ac_off = 0; + ac_bytes = pseudo_frame_size; + } + + + // switch to entire frames: + // bytes_to_go is the difference between requested bytes and + // delivered bytes + // + // pseudo_frame_size = average bytes per audio frame + // real_frame_size = real AC3 frame size in bytes + + num_frames = (ac_bytes + ac3_bytes_to_go) / real_frame_size; + + effective_frame_size = num_frames * real_frame_size; + ac3_bytes_to_go = ac_bytes + ac3_bytes_to_go - effective_frame_size; + + // return effective_frame_size as physical size of audio data + param->size = effective_frame_size; + + if(verbose_flag & TC_STATS) + tc_log_info(MOD_NAME, "pseudo=%d, real=%d, frames=%d, effective=%d offset=%d", + ac_bytes, real_frame_size, num_frames, effective_frame_size, ac_off); + + // adjust + ac_bytes=effective_frame_size; + +#if 0 + if(syncf>0) { + //dump an ac3 frame, instead of a pcm frame + ac_bytes = real_frame_size-ac_off; + param->size = real_frame_size; + --syncf; + } +#endif + + break; + + case CODEC_PCM: + + //default: + ac_off = 0; + ac_bytes = param->size; + break; + + default: + tc_log_warn(MOD_NAME, "invalid import codec request 0x%x",codec); + return(TC_IMPORT_ERROR); + + } + + if (fread(param->buffer+ac_off, ac_bytes-ac_off, 1, fd) !=1) + return(TC_IMPORT_ERROR); + + + return(0); + } + return(TC_IMPORT_ERROR); +} + +/* ------------------------------------------------------------ + * + * close stream + * + * ------------------------------------------------------------*/ + +MOD_close +{ + + if(param->fd) { + pclose(param->fd); + } + param->fd = NULL; + + if (f) { + pclose (f); + } + f = NULL; + + syncf = 0; + + if(param->flag == TC_VIDEO) { + + //safe + clone_close(); + + return(0); + } + + if(param->flag == TC_SUBEX) return(0); + + if(param->flag == TC_AUDIO) { + + if(fd) pclose(fd); + fd=NULL; + + return(0); + + } + return(TC_IMPORT_ERROR); +} + + |
