You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
999 lines
24 KiB
999 lines
24 KiB
4 years ago
|
/*
|
||
|
* demuxer.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.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "transcode.h"
|
||
|
#include "libtc/libtc.h"
|
||
|
#include "tcinfo.h"
|
||
|
|
||
|
#include "ioaux.h"
|
||
|
#include "aux_pes.h"
|
||
|
#include "seqinfo.h"
|
||
|
#include "demuxer.h"
|
||
|
#include "packets.h"
|
||
|
|
||
|
#include <math.h>
|
||
|
|
||
|
static int demux_mode=TC_DEMUX_SEQ_ADJUST;
|
||
|
|
||
|
int gop, gop_pts, gop_cnt;
|
||
|
|
||
|
typedef struct timecode_struc /* Time_code Struktur laut MPEG */
|
||
|
{ unsigned long msb; /* fuer SCR, DTS, PTS */
|
||
|
unsigned long lsb;
|
||
|
unsigned long reference_ext;
|
||
|
unsigned long negative; /* for delays when doing multiple files */
|
||
|
} Timecode_struc;
|
||
|
|
||
|
#define MAX_FFFFFFFF 4294967295.0 /* = 0xffffffff in hex. */
|
||
|
|
||
|
/* ------------------------------------------------------------
|
||
|
*
|
||
|
* support code (scr_rewrite()) moved from aux_pes.c
|
||
|
*
|
||
|
* ------------------------------------------------------------*/
|
||
|
|
||
|
static void make_timecode (double timestamp, Timecode_struc *pointer)
|
||
|
{
|
||
|
double temp_ts;
|
||
|
|
||
|
if (timestamp < 0.0) {
|
||
|
pointer->negative = 1;
|
||
|
timestamp = -timestamp;
|
||
|
} else
|
||
|
pointer->negative = 0;
|
||
|
|
||
|
temp_ts = floor(timestamp / 300.0);
|
||
|
|
||
|
if (temp_ts > MAX_FFFFFFFF) {
|
||
|
pointer->msb=1;
|
||
|
temp_ts -= MAX_FFFFFFFF;
|
||
|
pointer->lsb=(unsigned long)temp_ts;
|
||
|
} else {
|
||
|
pointer->msb=0;
|
||
|
pointer->lsb=(unsigned long)temp_ts;
|
||
|
}
|
||
|
|
||
|
pointer->reference_ext = (unsigned long)(timestamp - (floor(timestamp / 300.0) * 300.0));
|
||
|
|
||
|
}
|
||
|
|
||
|
#define MPEG2_MARKER_SCR 1 /* MPEG2 Marker SCR */
|
||
|
|
||
|
/*************************************************************************
|
||
|
Kopiert einen TimeCode in einen Bytebuffer. Dabei wird er nach
|
||
|
MPEG-Verfahren in bits aufgesplittet.
|
||
|
|
||
|
Makes a Copy of a TimeCode in a Buffer, splitting it into bitfields
|
||
|
according to MPEG-System
|
||
|
*************************************************************************/
|
||
|
|
||
|
static void buffer_timecode_scr (Timecode_struc *pointer, unsigned char **buffer)
|
||
|
{
|
||
|
|
||
|
unsigned char temp;
|
||
|
unsigned char marker=MPEG2_MARKER_SCR;
|
||
|
|
||
|
|
||
|
temp = (marker << 6) | (pointer->msb << 5) |
|
||
|
((pointer->lsb >> 27) & 0x18) | 0x4 | ((pointer->lsb >> 28) & 0x3);
|
||
|
*((*buffer)++)=temp;
|
||
|
temp = (pointer->lsb & 0x0ff00000) >> 20;
|
||
|
*((*buffer)++)=temp;
|
||
|
temp = ((pointer->lsb & 0x000f8000) >> 12) | 0x4 |
|
||
|
((pointer->lsb & 0x00006000) >> 13);
|
||
|
*((*buffer)++)=temp;
|
||
|
temp = (pointer->lsb & 0x00001fe0) >> 5;
|
||
|
*((*buffer)++)=temp;
|
||
|
temp = ((pointer->lsb & 0x0000001f) << 3) | 0x4 |
|
||
|
((pointer->reference_ext & 0x00000180) >> 7);
|
||
|
*((*buffer)++)=temp;
|
||
|
temp = ((pointer->reference_ext & 0x0000007F) << 1) | 1;
|
||
|
*((*buffer)++)=temp;
|
||
|
|
||
|
}
|
||
|
|
||
|
static void scr_rewrite(char *buf, uint32_t pts)
|
||
|
{
|
||
|
Timecode_struc timecode;
|
||
|
unsigned char * ucbuf = (unsigned char *)buf;
|
||
|
|
||
|
timecode.msb = 0;
|
||
|
timecode.lsb = 0;
|
||
|
timecode.reference_ext = 0;
|
||
|
timecode.negative = 0;
|
||
|
|
||
|
make_timecode((double) pts, &timecode);
|
||
|
|
||
|
buffer_timecode_scr(&timecode, &ucbuf);
|
||
|
}
|
||
|
|
||
|
/* ------------------------------------------------------------
|
||
|
*
|
||
|
* demuxer / synchronization thread
|
||
|
*
|
||
|
* ------------------------------------------------------------*/
|
||
|
|
||
|
void tcdemux_thread(info_t *ipipe)
|
||
|
{
|
||
|
|
||
|
int k, id=0, zz=0;
|
||
|
|
||
|
int j, i, bytes, filesize;
|
||
|
char *buffer=NULL;
|
||
|
|
||
|
int payload_id=0, select=PACKAGE_ALL;
|
||
|
|
||
|
double pts=0.0f, ref_pts=0.0f, resync_pts=-1.0f, pts_diff=0.0f, track_initial_pts=0.0f;
|
||
|
double av_fine_pts1=-1.0f, av_fine_pts2=-1.0f, av_fine_diff=0.0f;
|
||
|
|
||
|
uint32_t frame_based_lpts=0;
|
||
|
|
||
|
int unit_seek=0, unit, track=0, is_track=0;
|
||
|
|
||
|
int resync_seq1=0, resync_seq2=INT_MAX, seq_dump, seq_seek;
|
||
|
int keep_seq = 0;
|
||
|
int hard_fps = 0;
|
||
|
|
||
|
int has_pts_dts=0, demux_video=0, demux_audio=0;
|
||
|
|
||
|
int flag_flush = 0;
|
||
|
int flag_force = 0;
|
||
|
int flag_eos = 0;
|
||
|
int flag_has_audio = 0;
|
||
|
int flag_append_audio = 0;
|
||
|
int flag_notify = 1;
|
||
|
int flag_avsync = 1;
|
||
|
int flag_skip = 0;
|
||
|
int flag_sync_reset = 0;
|
||
|
int flag_sync_active = 0;
|
||
|
int flag_loop_all = 0;
|
||
|
int flag_av_fine_tune = 0;
|
||
|
int flag_rewrite_scr = 0;
|
||
|
int flag_field_encoded= 0;
|
||
|
|
||
|
//for demux_mode=2
|
||
|
int seq_picture_ctr=0, pack_picture_ctr=0, sequence_ctr=0, packet_ctr=0;
|
||
|
|
||
|
char buf[256];
|
||
|
const char *logfile;
|
||
|
unsigned long i_pts, i_dts;
|
||
|
unsigned int packet_size=VOB_PACKET_SIZE;
|
||
|
seq_list_t *ptr=NULL;
|
||
|
double fps;
|
||
|
|
||
|
|
||
|
// allocate space
|
||
|
if((buffer = tc_zalloc(packet_size))==NULL) {
|
||
|
tc_log_perror(__FILE__, "out of memory");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
// copy info parameter to local variables
|
||
|
|
||
|
unit_seek = ipipe->ps_unit;
|
||
|
resync_seq1 = ipipe->ps_seq1;
|
||
|
resync_seq2 = ipipe->ps_seq2;
|
||
|
track = ipipe->track;
|
||
|
|
||
|
//map track on substream id
|
||
|
|
||
|
switch(ipipe->codec) {
|
||
|
|
||
|
case TC_CODEC_SUB:
|
||
|
track+=0x20;
|
||
|
flag_rewrite_scr=1;
|
||
|
break;
|
||
|
case TC_CODEC_AC3:
|
||
|
track+=0x80;
|
||
|
break;
|
||
|
case TC_CODEC_PCM:
|
||
|
track+=0xA0;
|
||
|
break;
|
||
|
case TC_CODEC_MP3:
|
||
|
track+=0xC0;
|
||
|
break;
|
||
|
case TC_CODEC_MPEG2:
|
||
|
// for MPEG2 video, use this substream id for sync adjustment
|
||
|
track=ipipe->subid;
|
||
|
demux_video=1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//0.6.0pre4
|
||
|
if(demux_video==0) {
|
||
|
|
||
|
if(ipipe->demux == TC_DEMUX_SEQ_FSYNC)
|
||
|
ipipe->demux=TC_DEMUX_SEQ_ADJUST;
|
||
|
|
||
|
if(ipipe->demux == TC_DEMUX_SEQ_FSYNC2)
|
||
|
ipipe->demux=TC_DEMUX_SEQ_ADJUST2;
|
||
|
|
||
|
//substream synchonized with video in "force frame rate" mode
|
||
|
demux_audio=1;
|
||
|
}
|
||
|
|
||
|
if(ipipe->demux == TC_DEMUX_SEQ_FSYNC || ipipe->demux == TC_DEMUX_SEQ_FSYNC2 || ipipe->demux == TC_DEMUX_SEQ_LIST) {
|
||
|
|
||
|
//allocate buffer
|
||
|
if(flush_buffer_init(ipipe->fd_out, ipipe->verbose)<0) {
|
||
|
tc_log_error(__FILE__, "flush buffer facility init failed");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
//need to open the logfile
|
||
|
if(seq_init(ipipe->name, ipipe->fd_log, ipipe->fps, ipipe->verbose)<0) {
|
||
|
tc_log_error(__FILE__, "sync mode init failed");
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//new default behaving:
|
||
|
// if(unit_seek==0 && resync_seq1==0 && resync_seq2 == INT_MAX) flag_loop_all=1;
|
||
|
//changes 0.6.0pre3:
|
||
|
//tcprobe selects start unit --> switch to flag_loop_all always true
|
||
|
// for any given unit
|
||
|
if(resync_seq1==0 && resync_seq2 == INT_MAX) flag_loop_all=1;
|
||
|
|
||
|
demux_mode = ipipe->demux;
|
||
|
select = ipipe->select;
|
||
|
fps = ipipe->fps;
|
||
|
logfile = ipipe->name;
|
||
|
keep_seq = ipipe->keep_seq;
|
||
|
hard_fps = ipipe->hard_fps_flag;
|
||
|
|
||
|
j=0; //packet counter
|
||
|
i=0; //skipped packets counter
|
||
|
k=0; //unit counter
|
||
|
|
||
|
// will be switched on as soon start of sequences to flush is reached
|
||
|
flag_flush=0;
|
||
|
|
||
|
flag_notify=1;
|
||
|
|
||
|
flag_avsync=0;
|
||
|
flag_append_audio=0;
|
||
|
|
||
|
if(keep_seq) flag_sync_active=1;
|
||
|
|
||
|
unit=unit_seek;
|
||
|
|
||
|
seq_seek = resync_seq1;
|
||
|
seq_dump = resync_seq2 - resync_seq1;
|
||
|
|
||
|
++unit_seek;
|
||
|
++seq_seek;
|
||
|
|
||
|
if(!flag_loop_all) {
|
||
|
tc_log_msg(__FILE__, "seeking to sequence %d:%d ...", unit, resync_seq1);
|
||
|
}
|
||
|
|
||
|
filesize = 0;
|
||
|
for(;;) {
|
||
|
|
||
|
/* ------------------------------------------------------------
|
||
|
*
|
||
|
* (I) read a 2048k block
|
||
|
*
|
||
|
* ------------------------------------------------------------*/
|
||
|
|
||
|
if((bytes=tc_pread(ipipe->fd_in, buffer, packet_size)) != packet_size) {
|
||
|
|
||
|
//program end code?
|
||
|
if(bytes==4) {
|
||
|
if(scan_pack_header(buffer, MPEG_PROGRAM_END_CODE)) {
|
||
|
if(ipipe->verbose & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "(pid=%d) program stream end code detected",
|
||
|
getpid());
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(bytes)
|
||
|
tc_log_warn(__FILE__, "invalid program stream packet size (%d/%d)",
|
||
|
bytes, packet_size);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
filesize += bytes;
|
||
|
|
||
|
// do not make any tests in pass-through mode
|
||
|
if(demux_mode==TC_DEMUX_OFF) goto flush_packet;
|
||
|
|
||
|
/* ------------------------------------------------------------
|
||
|
*
|
||
|
* (II) packet header ok?
|
||
|
*
|
||
|
* ------------------------------------------------------------*/
|
||
|
|
||
|
|
||
|
if(!scan_pack_header(buffer, TC_MAGIC_VOB)) {
|
||
|
|
||
|
if(flag_notify && (ipipe->verbose & TC_DEBUG))
|
||
|
tc_log_warn(__FILE__, "(pid=%d) invalid packet header detected",
|
||
|
getpid());
|
||
|
|
||
|
// something else?
|
||
|
|
||
|
if(scan_pack_header(buffer, MPEG_VIDEO) | scan_pack_header(buffer, MPEG_AUDIO)) {
|
||
|
|
||
|
if(ipipe->verbose & TC_STATS)
|
||
|
tc_log_msg(__FILE__, "(pid=%d) MPEG system stream detected",
|
||
|
getpid());
|
||
|
|
||
|
if(scan_pack_header(buffer, MPEG_VIDEO)) payload_id=PACKAGE_VIDEO;
|
||
|
if(scan_pack_header(buffer, MPEG_AUDIO)) payload_id=PACKAGE_AUDIO_MP3;
|
||
|
|
||
|
// no further processing
|
||
|
goto flush_packet;
|
||
|
} else {
|
||
|
|
||
|
tc_log_warn(__FILE__, "(pid=%d) '0x%02x%02x%02x%02x' not yet supported",
|
||
|
getpid(), buffer[0] & 0xff, buffer[1] & 0xff,
|
||
|
buffer[2] & 0xff, buffer[3] & 0xff);
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
|
||
|
//MPEG1?
|
||
|
if ((buffer[4] & 0xf0) == 0x20) {
|
||
|
|
||
|
payload_id=PACKAGE_MPEG1;
|
||
|
flag_flush=1;
|
||
|
|
||
|
if(ipipe->verbose & TC_STATS)
|
||
|
tc_log_msg(__FILE__, "(pid=%d) MPEG-1 video stream detected",
|
||
|
getpid());
|
||
|
|
||
|
// no further processing
|
||
|
goto flush_packet;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ------------------------------------------------------------
|
||
|
*
|
||
|
* (III) analyze packet contents
|
||
|
*
|
||
|
* ------------------------------------------------------------*/
|
||
|
|
||
|
|
||
|
// proceed with a valid package, assume defaults
|
||
|
|
||
|
flag_skip=0; //do not skip
|
||
|
has_pts_dts=0; //no pts_dts stamp
|
||
|
payload_id=0; //payload unknown
|
||
|
flag_sync_reset=0; //no reset of syncinfo
|
||
|
|
||
|
id = buffer[17] & 0xff; //payload id byte
|
||
|
|
||
|
//MPEG 2?
|
||
|
if ((buffer[4] & 0xc0) == 0x40) {
|
||
|
|
||
|
// do not change any flags
|
||
|
|
||
|
if(ipipe->verbose & TC_STATS) {
|
||
|
//display info only once
|
||
|
tc_log_msg(__FILE__, "(pid=%d) MPEG-2 video stream detected",
|
||
|
getpid());
|
||
|
}
|
||
|
} else {
|
||
|
|
||
|
//MPEG1
|
||
|
if ((buffer[4] & 0xf0) == 0x20) {
|
||
|
|
||
|
payload_id=PACKAGE_MPEG1;
|
||
|
|
||
|
if(ipipe->verbose & TC_STATS) {
|
||
|
//display info only once
|
||
|
tc_log_msg(__FILE__, "(pid=%d) MPEG-1 video stream detected",
|
||
|
getpid());
|
||
|
}
|
||
|
} else {
|
||
|
|
||
|
payload_id=PACKAGE_PASS;
|
||
|
|
||
|
if(ipipe->verbose & TC_DEBUG)
|
||
|
tc_log_warn(__FILE__, "(pid=%d) unknown stream packet id detected",
|
||
|
getpid());
|
||
|
}
|
||
|
|
||
|
//flush all MPEG1 stuff
|
||
|
goto flush_packet;
|
||
|
}
|
||
|
|
||
|
/* ------------------------------------------------------------
|
||
|
*
|
||
|
* (IV) audio payload
|
||
|
*
|
||
|
* ------------------------------------------------------------*/
|
||
|
|
||
|
// check payload id
|
||
|
// process this audio packet?
|
||
|
|
||
|
// sync to AC3 audio mode?
|
||
|
if(id == P_ID_AC3) payload_id = PACKAGE_PRIVATE_STREAM;
|
||
|
|
||
|
// sync to MP3 audio mode?
|
||
|
if(id >= 0xc0 && id <= 0xdf) payload_id = PACKAGE_AUDIO_MP3;
|
||
|
|
||
|
// are we dealing with the right track?
|
||
|
// check here:
|
||
|
|
||
|
is_track=0;
|
||
|
|
||
|
if(payload_id == PACKAGE_PRIVATE_STREAM) {
|
||
|
|
||
|
//position of track code
|
||
|
uint8_t *_buf=buffer+14;
|
||
|
uint8_t *_tmp=_buf + 9 + _buf[8];
|
||
|
|
||
|
is_track = ((*_tmp) == track) ? 1:0;
|
||
|
|
||
|
if(ipipe->verbose & TC_STATS)
|
||
|
tc_log_msg(__FILE__, "substream [0x%x] %d", *_tmp, is_track);
|
||
|
|
||
|
if(is_track==0) {
|
||
|
flag_skip=1; //drop this packet
|
||
|
} else {
|
||
|
flag_skip=0;
|
||
|
goto sync_track;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(payload_id & PACKAGE_AUDIO_MP3) {
|
||
|
|
||
|
is_track = ((id) == track) ? 1:0;
|
||
|
if(is_track==0) flag_skip=1; //drop this packet
|
||
|
|
||
|
if(ipipe->verbose & TC_STATS)
|
||
|
tc_log_msg(__FILE__, "MPEG audio track [0x%x] %d", id, is_track);
|
||
|
}
|
||
|
|
||
|
sync_track:
|
||
|
|
||
|
if(is_track) {
|
||
|
|
||
|
|
||
|
if(flag_sync_active==0) {
|
||
|
|
||
|
//first valid audio packet!
|
||
|
|
||
|
// get pts time stamp:
|
||
|
ac_memcpy(buf, &buffer[20], 16);
|
||
|
has_pts_dts=get_pts_dts(buf, &i_pts, &i_dts);
|
||
|
|
||
|
if(has_pts_dts) {
|
||
|
track_initial_pts=(double)i_pts/90000.;
|
||
|
} else {
|
||
|
//fallback to scr time stamp:
|
||
|
ac_memcpy(buf, &buffer[4], 6);
|
||
|
track_initial_pts = read_time_stamp(buf);
|
||
|
}
|
||
|
|
||
|
if(resync_pts<0) {
|
||
|
pts_diff = track_initial_pts;
|
||
|
} else {
|
||
|
pts_diff = track_initial_pts - resync_pts;
|
||
|
}
|
||
|
|
||
|
//pts_diff<0 is OK, the packets will be dropped to establish sync
|
||
|
//pts_diff>0 is not so simple, since we need to drop video
|
||
|
//packets, already submitted to flush buffer
|
||
|
|
||
|
//enable sync mode, check for PTS giant leap????
|
||
|
if(pts_diff < TC_DEMUX_MIN_PTS || pts_diff > TC_DEMUX_CRIT_PTS)
|
||
|
flag_sync_active = 1;
|
||
|
|
||
|
//0.6.0pre5: frame dropping handled by transcode
|
||
|
flag_sync_active=1;
|
||
|
|
||
|
//unless this flag is on, the video sequence is dropped
|
||
|
//in TC_DEMUX_SEQ_FSYNC mode
|
||
|
}
|
||
|
|
||
|
|
||
|
// sync now, if requested:
|
||
|
|
||
|
if(!unit_seek) flag_has_audio=1; //unit has audio packets
|
||
|
|
||
|
|
||
|
// need to find the time difference of two audio packets
|
||
|
// for fine-tuning AV sync.
|
||
|
|
||
|
if(flag_av_fine_tune == 0) {
|
||
|
// get pts time stamp:
|
||
|
ac_memcpy(buf, &buffer[20], 16);
|
||
|
has_pts_dts=get_pts_dts(buf, &i_pts, &i_dts);
|
||
|
|
||
|
if(av_fine_pts1<0) {
|
||
|
av_fine_pts1 = (double)i_pts/90000.;
|
||
|
} else {
|
||
|
av_fine_pts2 = (double)i_pts/90000.;
|
||
|
flag_av_fine_tune=1;
|
||
|
}
|
||
|
|
||
|
//diff:
|
||
|
if(flag_av_fine_tune==1) {
|
||
|
av_fine_diff=av_fine_pts2-av_fine_pts1;
|
||
|
if(ipipe->verbose & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "AV fine-tuning: %d ms",
|
||
|
(int)(av_fine_diff*1000));
|
||
|
}
|
||
|
|
||
|
//sanity check:
|
||
|
if(av_fine_diff<0) av_fine_diff=0.0f;
|
||
|
|
||
|
}
|
||
|
|
||
|
//Pre-processing: check if we need to re-sync?
|
||
|
|
||
|
if(demux_mode == TC_DEMUX_SEQ_FSYNC2 || demux_mode == TC_DEMUX_SEQ_ADJUST2) {
|
||
|
//new demux modes let transcode handle audio sync shift
|
||
|
flag_avsync=0;
|
||
|
flag_skip=0;
|
||
|
}
|
||
|
|
||
|
if(flag_avsync) {
|
||
|
|
||
|
// get pts time stamp:
|
||
|
ac_memcpy(buf, &buffer[20], 16);
|
||
|
has_pts_dts=get_pts_dts(buf, &i_pts, &i_dts);
|
||
|
|
||
|
if(has_pts_dts) {
|
||
|
pts=(double)i_pts/90000.;
|
||
|
} else {
|
||
|
//fallback to scr time stamp:
|
||
|
ac_memcpy(buf, &buffer[4], 6);
|
||
|
pts = read_time_stamp(buf);
|
||
|
}
|
||
|
|
||
|
pts_diff = pts - resync_pts;
|
||
|
|
||
|
//correction
|
||
|
//FIXME
|
||
|
pts_diff += av_fine_diff;
|
||
|
|
||
|
if(pts_diff<0) {
|
||
|
flag_skip=1;
|
||
|
|
||
|
if(ipipe->verbose & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "(pid=%d) audio packet %06d for PU [%d] skipped (%.4f)",
|
||
|
getpid(), j, ((k==0)? 0:k-1), pts-resync_pts);
|
||
|
} else {
|
||
|
//reset
|
||
|
flag_skip=0;
|
||
|
flag_avsync=0;
|
||
|
if(ipipe->verbose)
|
||
|
tc_log_msg(__FILE__, "(pid=%d) AV sync established for PU [%d] at PTS=%.4f (%.4f)",
|
||
|
getpid(), k-1, pts, pts-resync_pts);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Post-processing: more audio packets?
|
||
|
|
||
|
if(flag_append_audio) {
|
||
|
|
||
|
//need to flush a few audio packets, if video ahead
|
||
|
|
||
|
// get pts time stamp:
|
||
|
ac_memcpy(buf, &buffer[20], 16);
|
||
|
has_pts_dts=get_pts_dts(buf, &i_pts, &i_dts);
|
||
|
|
||
|
if(has_pts_dts) {
|
||
|
pts=(double)i_pts/90000.;
|
||
|
} else {
|
||
|
//fallback to scr time stamp:
|
||
|
ac_memcpy(buf, &buffer[4], 6);
|
||
|
pts = read_time_stamp(buf);
|
||
|
}
|
||
|
|
||
|
pts_diff = pts - resync_pts;
|
||
|
|
||
|
if(pts_diff<0) {
|
||
|
|
||
|
//append this packet
|
||
|
flag_skip=0;
|
||
|
|
||
|
if(ipipe->verbose & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "(pid=%d) audio packet %06d for PU [%d] appended (%.4f)",
|
||
|
getpid(), j, ((k==0)? 0:k-1), pts-resync_pts);
|
||
|
} else {
|
||
|
//abort - all done
|
||
|
flag_eos=1;
|
||
|
if(ipipe->verbose)
|
||
|
tc_log_msg(__FILE__, "(pid=%d) AV sync abandoned for PU [%d] at PTS=%.4f (%.4f)",
|
||
|
getpid(), k-1, pts, pts-resync_pts);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//only go for audio in this phase
|
||
|
if(flag_append_audio) goto flush_packet;
|
||
|
|
||
|
/* ------------------------------------------------------------
|
||
|
*
|
||
|
* (V) misc payload
|
||
|
*
|
||
|
* ------------------------------------------------------------*/
|
||
|
|
||
|
|
||
|
if(id == P_ID_PROG || id == P_ID_PADD) {
|
||
|
|
||
|
payload_id=PACKAGE_NAV;
|
||
|
|
||
|
// get pts time stamp:
|
||
|
ac_memcpy(buf, &buffer[20], 16);
|
||
|
has_pts_dts=get_pts_dts(buf, &i_pts, &i_dts);
|
||
|
|
||
|
if(has_pts_dts) {
|
||
|
pts=(double)i_pts/90000.;
|
||
|
} else {
|
||
|
//fallback to scr time stamp:
|
||
|
ac_memcpy(buf, &buffer[4], 6);
|
||
|
pts = read_time_stamp(buf);
|
||
|
}
|
||
|
|
||
|
//do not dump this packet
|
||
|
flag_skip=1;
|
||
|
}
|
||
|
|
||
|
/* ------------------------------------------------------------
|
||
|
*
|
||
|
* (VI) video payload
|
||
|
*
|
||
|
* ------------------------------------------------------------*/
|
||
|
|
||
|
|
||
|
if(id == P_ID_MPEG) {
|
||
|
|
||
|
payload_id = PACKAGE_VIDEO;
|
||
|
|
||
|
// get pts time stamp:
|
||
|
ac_memcpy(buf, &buffer[4], 6);
|
||
|
pts = read_time_stamp(buf);
|
||
|
|
||
|
//read full packet header
|
||
|
ac_memcpy(buf, &buffer[20], 16);
|
||
|
has_pts_dts=get_pts_dts(buf, &i_pts, &i_dts);
|
||
|
|
||
|
//need frame/field encoding information
|
||
|
zz=scan_pack_ext(buffer);
|
||
|
if(zz>0) flag_field_encoded=zz;
|
||
|
|
||
|
//need precise number of pics in this sequence
|
||
|
pack_picture_ctr = scan_pack_pics(buffer);
|
||
|
|
||
|
seq_picture_ctr += pack_picture_ctr;
|
||
|
|
||
|
frame_based_lpts = (seq_picture_ctr-1);
|
||
|
|
||
|
//need this pack for subtitle PTS information
|
||
|
if(flag_rewrite_scr && has_pts_dts) flag_force=1;
|
||
|
|
||
|
// only process packets with pts/dts time stamp, since
|
||
|
// they (all?) have a sequence start code
|
||
|
|
||
|
if(has_pts_dts) {
|
||
|
|
||
|
if (ipipe->verbose & TC_STATS)
|
||
|
tc_log_msg(__FILE__, "(pid=%d) PTS-DTS detected in packet [%06d]",
|
||
|
getpid(), j);
|
||
|
|
||
|
// default first(=0) unit ?
|
||
|
if(k==0) {
|
||
|
|
||
|
--unit_seek;
|
||
|
flag_sync_reset=1;
|
||
|
|
||
|
if(ipipe->verbose & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "(pid=%d) MPEG sequence start code in packet %06d for PU [0]",
|
||
|
getpid(), j);
|
||
|
|
||
|
k++;
|
||
|
}
|
||
|
|
||
|
|
||
|
if(pts<ref_pts) {
|
||
|
|
||
|
--unit_seek;
|
||
|
flag_sync_reset=1;
|
||
|
|
||
|
// past next unit - abort
|
||
|
// or process all following units?
|
||
|
|
||
|
if(unit_seek<0 && flag_loop_all==0) flag_eos=1;
|
||
|
|
||
|
//experimental: try to resync
|
||
|
//flag_avsync = 1;
|
||
|
|
||
|
if(ipipe->verbose & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "(pid=%d) PTS reset (%.3f->%.3f) in packet %06d for PU [%d]",
|
||
|
getpid(), ref_pts, pts, j, k);
|
||
|
|
||
|
k++;
|
||
|
}
|
||
|
|
||
|
|
||
|
// only decrement sequence counter in right unit, i.e.
|
||
|
// unit_seek=0;
|
||
|
if(!unit_seek) --seq_seek;
|
||
|
|
||
|
// flush all sequences until the end of the unit
|
||
|
// or end of stream
|
||
|
// or resync_seq2 is reached
|
||
|
|
||
|
if(seq_seek==0) {
|
||
|
|
||
|
//re read packet header
|
||
|
ac_memcpy(buf, &buffer[20], 16);
|
||
|
get_pts_dts(buf, &i_pts, &i_dts);
|
||
|
|
||
|
resync_pts = (double) i_pts / 90000;
|
||
|
|
||
|
if(!flag_flush) {
|
||
|
|
||
|
// need to dump requested seq_dump sequences
|
||
|
seq_seek=seq_dump;
|
||
|
flag_flush = 1;
|
||
|
|
||
|
flag_avsync = 1;
|
||
|
|
||
|
// may be a useful info for the user
|
||
|
if(ipipe->verbose)
|
||
|
tc_log_msg(__FILE__, "(pid=%d) processing PU [%d], on at PTS=%.4f sec",
|
||
|
getpid(), k-1, resync_pts);
|
||
|
|
||
|
} else {
|
||
|
// finished, all sequences flushed, switch
|
||
|
// to audio packets post processing
|
||
|
flag_append_audio = 1;
|
||
|
flag_skip = 1; //flush mode on, but do not write this one
|
||
|
|
||
|
// may be a useful info for the user
|
||
|
if(ipipe->verbose)
|
||
|
tc_log_msg(__FILE__, "(pid=%d) processing PU [%d], off at PTS=%.4f sec",
|
||
|
getpid(), k-1, resync_pts);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------
|
||
|
//gather information on the sequences before flushing
|
||
|
//---------------------------------------------------
|
||
|
|
||
|
if((demux_mode == TC_DEMUX_SEQ_FSYNC || demux_mode == TC_DEMUX_SEQ_FSYNC2) && flag_flush) {
|
||
|
|
||
|
ptr = seq_register(sequence_ctr);
|
||
|
|
||
|
zz=(flag_field_encoded==3)?(seq_picture_ctr-pack_picture_ctr):
|
||
|
(seq_picture_ctr-pack_picture_ctr)/2;
|
||
|
|
||
|
if(sequence_ctr) seq_update(ptr->prev, i_pts, zz, packet_ctr, flag_sync_active, hard_fps);
|
||
|
|
||
|
//init sequence information structure for current sequence
|
||
|
|
||
|
ptr->pts = i_pts;
|
||
|
ptr->dts = i_dts;
|
||
|
ptr->pics_first_packet = pack_picture_ctr;
|
||
|
ptr->sync_reset = flag_sync_reset;
|
||
|
|
||
|
//reset/update
|
||
|
seq_picture_ctr = 0;
|
||
|
packet_ctr = 0;
|
||
|
++sequence_ctr;
|
||
|
|
||
|
//shift resync_pts, since sequence is dropped
|
||
|
if(flag_sync_active==0) resync_pts=(double) i_pts / 90000;
|
||
|
|
||
|
} //end TC_DEMUX_SEQ_FSYNC mode
|
||
|
|
||
|
//---------------------------------------------------
|
||
|
//print out sequence information for frame navigation
|
||
|
//---------------------------------------------------
|
||
|
|
||
|
if((demux_mode == TC_DEMUX_SEQ_LIST) && flag_flush) {
|
||
|
|
||
|
ptr = seq_register(sequence_ctr);
|
||
|
|
||
|
zz=(flag_field_encoded==3)?(seq_picture_ctr-pack_picture_ctr):
|
||
|
(seq_picture_ctr-pack_picture_ctr)/2;
|
||
|
|
||
|
if(sequence_ctr) seq_list(ptr->prev, i_pts, zz, packet_ctr, flag_sync_active);
|
||
|
|
||
|
//init sequence information structure for current sequence
|
||
|
|
||
|
ptr->pts = i_pts;
|
||
|
ptr->dts = i_dts;
|
||
|
ptr->pics_first_packet = pack_picture_ctr;
|
||
|
ptr->sync_reset = flag_sync_reset;
|
||
|
ptr->packet_ctr=j;
|
||
|
|
||
|
//reset/update
|
||
|
seq_picture_ctr = 0;
|
||
|
packet_ctr = 0;
|
||
|
++sequence_ctr;
|
||
|
|
||
|
//shift resync_pts, since sequence is dropped
|
||
|
if(flag_sync_active==0) resync_pts=(double) i_pts / 90000;
|
||
|
|
||
|
} //end TC_DEMUX_SEQ_LIST mode
|
||
|
|
||
|
//reset sync pts, if no audio/substream has been found (0.6.0pre4)
|
||
|
if(flag_sync_active==0 && demux_audio) {
|
||
|
resync_pts=(double) i_pts / 90000;
|
||
|
if(ipipe->verbose & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "new initial PTS=%f", resync_pts);
|
||
|
}
|
||
|
|
||
|
}// PTS-DTS flag yes
|
||
|
|
||
|
ref_pts=pts;
|
||
|
|
||
|
}// MPEG video packet
|
||
|
|
||
|
/* ------------------------------------------------------------
|
||
|
*
|
||
|
* (VII) evaluate scan results - flush packet
|
||
|
*
|
||
|
* ------------------------------------------------------------*/
|
||
|
|
||
|
flush_packet:
|
||
|
|
||
|
|
||
|
if(ipipe->verbose & TC_STATS)
|
||
|
tc_log_msg(__FILE__, "INFO: j=%05d, i=%05d, skip=%d, flush=%d, force=%d, pay=%3d, sid=0x%02x, eos=%d",
|
||
|
j, i, flag_skip, flag_flush, flag_force, payload_id, id, flag_eos);
|
||
|
|
||
|
//need to rewrite SCR pack header entry based on
|
||
|
//frame_based_pts information for transcode:
|
||
|
if(flag_rewrite_scr) scr_rewrite(&buffer[4], ((flag_field_encoded==3)?frame_based_lpts:frame_based_lpts/2));
|
||
|
|
||
|
//flush here:
|
||
|
|
||
|
switch(demux_mode) {
|
||
|
|
||
|
case TC_DEMUX_DEBUG:
|
||
|
if((flag_flush && !flag_skip && (payload_id & select))
|
||
|
|| flag_force) scan_pack_payload(buffer, packet_size, j, ipipe->verbose);
|
||
|
break;
|
||
|
|
||
|
case TC_DEMUX_DEBUG_ALL:
|
||
|
scan_pack_payload(buffer, packet_size, j, ipipe->verbose);
|
||
|
break;
|
||
|
|
||
|
case TC_DEMUX_SEQ_FSYNC:
|
||
|
case TC_DEMUX_SEQ_FSYNC2:
|
||
|
|
||
|
if((flag_flush && !flag_skip && (payload_id & select)) || flag_force) {
|
||
|
|
||
|
//count current sequence packets to be flushed
|
||
|
++packet_ctr;
|
||
|
|
||
|
if(ipipe->verbose & TC_STATS)
|
||
|
tc_log_msg(__FILE__, "flushing packet (%d/%d)", sequence_ctr, j);
|
||
|
|
||
|
if(flush_buffer_write(ipipe->fd_out, buffer, packet_size) != packet_size) {
|
||
|
tc_log_perror(__FILE__, "write program stream packet");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
//reset
|
||
|
flag_force=0;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case TC_DEMUX_SEQ_ADJUST:
|
||
|
case TC_DEMUX_SEQ_ADJUST2:
|
||
|
|
||
|
if((flag_flush && !flag_skip && (payload_id & select)) || flag_force) {
|
||
|
|
||
|
if(tc_pwrite(ipipe->fd_out, buffer, packet_size) != packet_size) {
|
||
|
tc_log_perror(__FILE__, "write program stream packet");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
//reset
|
||
|
flag_force=0;
|
||
|
|
||
|
if(ipipe->verbose & TC_STATS)
|
||
|
tc_log_msg(__FILE__, "writing packet %d", j);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
++i;
|
||
|
if(ipipe->verbose & TC_STATS)
|
||
|
tc_log_msg(__FILE__, "skipping packet %d", j);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
|
||
|
case TC_DEMUX_SEQ_LIST:
|
||
|
|
||
|
//count packs
|
||
|
++packet_ctr;
|
||
|
|
||
|
// nothing to do
|
||
|
break;
|
||
|
|
||
|
case TC_DEMUX_OFF:
|
||
|
|
||
|
if(tc_pwrite(ipipe->fd_out, buffer, packet_size) != packet_size) {
|
||
|
tc_log_perror(__FILE__, "write program stream packet");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
if(ipipe->verbose & TC_STATS)
|
||
|
tc_log_msg(__FILE__, "writing packet %d", j);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//aborting?
|
||
|
if(flag_eos) break;
|
||
|
|
||
|
//total packs (2k each) counter
|
||
|
++j;
|
||
|
|
||
|
} // process next packet/block
|
||
|
|
||
|
if(ipipe->verbose & TC_SYNC)
|
||
|
tc_log_msg(__FILE__, "EOS - flushing packet buffer");
|
||
|
|
||
|
//post processing
|
||
|
|
||
|
if(demux_mode == TC_DEMUX_SEQ_FSYNC || demux_mode == TC_DEMUX_SEQ_FSYNC2) {
|
||
|
seq_close();
|
||
|
flush_buffer_close();
|
||
|
}
|
||
|
|
||
|
if(demux_mode == TC_DEMUX_SEQ_LIST) {
|
||
|
|
||
|
ptr = seq_register(sequence_ctr);
|
||
|
|
||
|
zz=(flag_field_encoded==3)?(seq_picture_ctr-pack_picture_ctr):
|
||
|
(seq_picture_ctr-pack_picture_ctr)/2;
|
||
|
|
||
|
if(ptr!=NULL && ptr->id) seq_list(ptr->prev, ref_pts, zz, packet_ctr, flag_sync_active);
|
||
|
|
||
|
fflush(stdout);
|
||
|
|
||
|
//summary
|
||
|
seq_list_frames();
|
||
|
|
||
|
seq_close();
|
||
|
flush_buffer_close();
|
||
|
|
||
|
}
|
||
|
|
||
|
//summary
|
||
|
if(ipipe->verbose & TC_DEBUG)
|
||
|
tc_log_msg(__FILE__, "(pid=%d) %d/%d packets discarded", getpid(), i, j);
|
||
|
|
||
|
if(buffer!=NULL) free(buffer);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|