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.
1101 lines
28 KiB
1101 lines
28 KiB
//
|
|
// C++ Interface: k9vamps
|
|
//
|
|
// Description: A transcription from Vamps in C++
|
|
//
|
|
//
|
|
// Author: Jean-Michel PETIT <k9copy@free.fr>, (C) 2006
|
|
//
|
|
// Copyright: See COPYING file that comes with this distribution
|
|
//
|
|
//
|
|
|
|
#include "k9vamps.h"
|
|
#include <tqapplication.h>
|
|
#include "ac.h"
|
|
|
|
|
|
void k9vamps::setNoData() {
|
|
noData=true;
|
|
wDataRead.wakeAll();
|
|
wDataReady.wakeAll();
|
|
}
|
|
|
|
void k9vamps::addData(uchar *data,uint size) {
|
|
while (1) {
|
|
if (m_fifo.freespace()>=size) {
|
|
m_fifo.enqueue(data,size);
|
|
wDataReady.wakeAll();
|
|
break;
|
|
} else
|
|
wDataRead.wait();
|
|
}
|
|
}
|
|
|
|
|
|
int k9vamps::readData(uchar * data,uint size) {
|
|
uint size2=size;
|
|
uint32_t readSize=0,s=0;
|
|
|
|
while (1) {
|
|
// is there data in the buffer?
|
|
if (m_fifo.count() >0) {
|
|
// s= size of data that we will read (maximum = size)
|
|
s=(m_fifo.count()) <size2 ? (m_fifo.count()) : size2;
|
|
// increments the number of readen bytes
|
|
readSize+=s;
|
|
// decrements the number of max bytes to read
|
|
size2-=s;
|
|
//moves bytes from buffer to output
|
|
m_fifo.dequeue(data,s);
|
|
//moves the position of output buffer to receive next bytes
|
|
data+=s;
|
|
//there's now free space in input buffer, we can wake the injection thread
|
|
wDataRead.wakeAll();
|
|
}
|
|
// break the loop if injection thread terminated or we got what we want (size bytes)
|
|
// otherwise, we're waiting for datas
|
|
if(noData || (m_fifo.count() >=size2)) {
|
|
break;
|
|
} else
|
|
wDataReady.wait();
|
|
}
|
|
// if there's datas in input buffer and we did not get all what we wanted, we take them.
|
|
s= (m_fifo.count()) <size2 ? (m_fifo.count()) : size2;
|
|
readSize+=s;
|
|
if (s>0 )
|
|
m_fifo.dequeue(data,s);
|
|
|
|
wDataRead.wakeAll();
|
|
return readSize;
|
|
}
|
|
|
|
void k9vamps::addSubpicture(uint id) {
|
|
int cpt=1;
|
|
for (uint i=0;i<32;i++)
|
|
if (spu_track_map[i]!=0) cpt++;
|
|
spu_track_map[id-1]=cpt;
|
|
}
|
|
|
|
void k9vamps::addAudio(uint id) {
|
|
int cpt=1;
|
|
for (uint i=0;i <8;i++)
|
|
if (audio_track_map[i] !=0) cpt++;
|
|
|
|
audio_track_map[id-1]=cpt;
|
|
}
|
|
|
|
void k9vamps::addAudio(uint id,uint newId) {
|
|
if (newId==0)
|
|
addAudio(id);
|
|
else
|
|
audio_track_map[id-1]=newId;
|
|
}
|
|
|
|
|
|
void k9vamps::setInputSize(uint64_t size) {
|
|
ps_size=size;
|
|
}
|
|
|
|
void k9vamps::setVapFactor(float factor) {
|
|
vap_fact=factor;
|
|
}
|
|
|
|
void k9vamps::setSaveImage(k9SaveImage *m_save) {
|
|
m_saveImage=m_save;
|
|
}
|
|
|
|
void k9vamps::reset() {
|
|
m_preserve=true;
|
|
bytes_read =0;
|
|
bytes_written=0;
|
|
padding_bytes=0;
|
|
total_packs=0;
|
|
video_packs=0;
|
|
skipped_video_packs=0;
|
|
aux_packs=0;
|
|
skipped_aux_packs=0;
|
|
sequence_headers=0;
|
|
nav_packs=0;
|
|
|
|
rptr = rbuf;
|
|
rhwp = rbuf;
|
|
wptr = wbuf;
|
|
vbuf_size = VBUF_SIZE;
|
|
vap_fact= 1.0f;
|
|
|
|
// inbuffw=inbuff;
|
|
for (uint i=0; i<8;i++) {
|
|
audio_track_map[i]=0;
|
|
}
|
|
for (uint i=0; i<32;i++) {
|
|
spu_track_map[i]=0;
|
|
}
|
|
|
|
calc_ps_vap = 1;
|
|
vap_fact=1.0;
|
|
ps_size=0;
|
|
noData=false;
|
|
|
|
avgdiff=1;
|
|
m_totfact=m_nbfact=m_avgfact=0;
|
|
|
|
vin_bytes=0;
|
|
vout_bytes=0;
|
|
|
|
}
|
|
|
|
k9vamps::k9vamps(k9DVDBackup *dvdbackup) {
|
|
m_saveImage=NULL;
|
|
m_dvdbackup=dvdbackup;
|
|
reset();
|
|
m_requant=NULL;
|
|
if (dvdbackup !=NULL)
|
|
m_bgUpdate = new k9bgUpdate(dvdbackup);
|
|
else
|
|
m_bgUpdate=NULL;
|
|
rbuf_size= RBUF_SIZE;
|
|
rbuf = (uchar*) malloc(rbuf_size);;
|
|
m_output=NULL;
|
|
}
|
|
|
|
|
|
void k9vamps::setPreserve(bool _value) {
|
|
m_preserve = _value;
|
|
}
|
|
void k9vamps::setOutput(TQFile *_output) {
|
|
m_output=_output;
|
|
}
|
|
|
|
k9vamps::~k9vamps() {
|
|
if (m_bgUpdate !=NULL)
|
|
delete m_bgUpdate;
|
|
free (rbuf);
|
|
}
|
|
|
|
|
|
void k9vamps::run () {
|
|
m_error=false;
|
|
m_errMsg="";
|
|
m_requant=new k9requant();
|
|
eof=0;
|
|
|
|
// allocate video buffers
|
|
vibuf =(uchar*) malloc (vbuf_size);
|
|
vobuf = (uchar*) malloc (vbuf_size);
|
|
|
|
if (vibuf == NULL || vobuf == NULL)
|
|
fatal (TQString("Allocation of video buffers failed: %1").arg(strerror (errno)));
|
|
|
|
|
|
// actually do vaporization
|
|
vaporize ();
|
|
|
|
|
|
flush();
|
|
|
|
if (m_requant !=NULL) {
|
|
m_requant->rqt_stop=true;
|
|
while(m_requant->running()) {
|
|
m_requant->condr.wakeAll();
|
|
m_requant->condw.wakeAll();
|
|
m_requant->wait(10);
|
|
}
|
|
// m_requant->mutr.unlock();
|
|
// m_requant->mutw.unlock();
|
|
}
|
|
delete m_requant;
|
|
m_requant=NULL;
|
|
free (vibuf);
|
|
free(vobuf);
|
|
if (m_bgUpdate!=NULL)
|
|
m_bgUpdate->wait();
|
|
//mutex.unlock();
|
|
}
|
|
|
|
|
|
|
|
// lock `size' bytes in read buffer
|
|
// i.e. ensure the next `size' input bytes are available in buffer
|
|
// returns nonzero on EOF
|
|
int k9vamps::lock (int size) {
|
|
int avail, n;
|
|
|
|
avail = rhwp - rptr;
|
|
|
|
if (avail >= size)
|
|
return 0;
|
|
|
|
if (avail) {
|
|
tc_memcpy (rbuf, rptr, avail);
|
|
rptr = rbuf;
|
|
rhwp = rptr + avail;
|
|
}
|
|
|
|
if (rbuf_size -avail <=0) {
|
|
uchar *buffer =(uchar*) malloc (rbuf_size+20480);
|
|
tc_memcpy (buffer,rbuf,rbuf_size);
|
|
rptr = buffer +(rptr-rbuf);
|
|
rhwp=buffer+(rhwp-rbuf);
|
|
rbuf_size+=20480;
|
|
free(rbuf);
|
|
rbuf=buffer;
|
|
}
|
|
|
|
n = readData(rhwp,rbuf_size - avail);
|
|
|
|
if (n % SECT_SIZE)
|
|
fatal ("Premature EOF");
|
|
|
|
rhwp += n;
|
|
bytes_read += n;
|
|
|
|
return !n;
|
|
}
|
|
|
|
|
|
// copy `size' bytes from rbuf to wbuf
|
|
void k9vamps::copy (int size) {
|
|
if (!size)
|
|
return;
|
|
|
|
if ((wptr - wbuf) + size > WBUF_SIZE)
|
|
fatal ("Write buffer overflow");
|
|
|
|
tc_memcpy (wptr, rptr, size);
|
|
rptr += size;
|
|
wptr += size;
|
|
}
|
|
|
|
|
|
// skip `size' bytes in rbuf
|
|
void k9vamps::skip (int size) {
|
|
rptr += size;
|
|
}
|
|
|
|
|
|
// flush wbuf
|
|
void k9vamps::flush (void) {
|
|
int size;
|
|
mutex.lock();
|
|
size = wptr - wbuf;
|
|
|
|
if (!size) {
|
|
mutex.unlock();
|
|
return;
|
|
}
|
|
//m_dvdbackup->getOutput(wbuf,size);
|
|
// wait for a preceding update to finish
|
|
if (m_bgUpdate!=NULL) {
|
|
m_bgUpdate->wait();
|
|
m_bgUpdate->update( wbuf,size);
|
|
}
|
|
if (m_output != NULL)
|
|
m_output->writeBlock((const char*) wbuf,size);
|
|
if (m_saveImage !=NULL)
|
|
m_saveImage->addData(wbuf,size);
|
|
wptr = wbuf;
|
|
bytes_written += size;
|
|
mutex.unlock();
|
|
}
|
|
|
|
|
|
// returns no. bytes read up to where `ptr' points
|
|
uint64_t k9vamps::rtell (uchar *ptr) {
|
|
return bytes_read - (rhwp - ptr);
|
|
}
|
|
|
|
|
|
// returns no. bytes written up to where `ptr' points
|
|
// (including those in buffer which are not actually written yet)
|
|
uint64_t k9vamps::wtell (uchar *ptr) {
|
|
return bytes_written + (ptr - wbuf);
|
|
}
|
|
|
|
|
|
// some pack header consistency checking
|
|
bool k9vamps::check_pack (uchar *ptr) {
|
|
uint32_t pack_start_code;
|
|
int pack_stuffing_length;
|
|
|
|
pack_start_code = (uint32_t) (ptr [0]) << 24;
|
|
pack_start_code |= (uint32_t) (ptr [1]) << 16;
|
|
pack_start_code |= (uint32_t) (ptr [2]) << 8;
|
|
pack_start_code |= (uint32_t) (ptr [3]);
|
|
|
|
if (pack_start_code != 0x000001ba) {
|
|
// fatal ("Bad pack start code at %llu: %08lx", rtell (ptr), pack_start_code);
|
|
return false;
|
|
}
|
|
|
|
if ((ptr [4] & 0xc0) != 0x40) {
|
|
// fatal ("Not an MPEG2 program stream pack at %llu", rtell (ptr));
|
|
return false;
|
|
}
|
|
|
|
// we rely on a fixed pack header size of 14
|
|
// so better to ensure this is true
|
|
pack_stuffing_length = ptr [13] & 7;
|
|
|
|
if (pack_stuffing_length) {
|
|
//fatal ("Non-zero pack stuffing length at %llu: %d\n", rtell (ptr), pack_stuffing_length);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// video packet consistency checking
|
|
int k9vamps::check_video_packet (uchar *ptr) {
|
|
int vid_packet_length, pad_packet_length, rc = 0;
|
|
uint32_t vid_packet_start_code, pad_packet_start_code, sequence_header_code;
|
|
|
|
vid_packet_start_code = (uint32_t) (ptr [0]) << 24;
|
|
vid_packet_start_code |= (uint32_t) (ptr [1]) << 16;
|
|
vid_packet_start_code |= (uint32_t) (ptr [2]) << 8;
|
|
vid_packet_start_code |= (uint32_t) (ptr [3]);
|
|
|
|
if (vid_packet_start_code != 0x000001e0)
|
|
fatal(TQString ("Bad video packet start code at %1: %2").arg(rtell(ptr)).arg(vid_packet_start_code,0,16));
|
|
|
|
vid_packet_length = ptr [4] << 8;
|
|
vid_packet_length |= ptr [5];
|
|
vid_packet_length += 6;
|
|
|
|
if ((ptr [6] & 0xc0) != 0x80)
|
|
fatal (TQString("Not an MPEG2 video packet at %1").arg(rtell (ptr)));
|
|
|
|
if (ptr [7]) {
|
|
if ((ptr [7] & 0xc0) != 0xc0)
|
|
tqDebug ("%s", (TQString("First video packet in sequence starting at %1 misses PTS or DTS, flags=%2").arg(rtell (ptr)).arg(ptr [7])).ascii());
|
|
else {
|
|
sequence_header_code = (uint32_t) (ptr [6 + 3 + ptr [8] + 0]) << 24;
|
|
sequence_header_code |= (uint32_t) (ptr [6 + 3 + ptr [8] + 1]) << 16;
|
|
sequence_header_code |= (uint32_t) (ptr [6 + 3 + ptr [8] + 2]) << 8;
|
|
sequence_header_code |= (uint32_t) (ptr [6 + 3 + ptr [8] + 3]);
|
|
|
|
if (sequence_header_code == 0x000001b3) {
|
|
rc = 1;
|
|
} else {
|
|
//fprintf (stderr, "Start of GOP at %llu not on sector boundary\n",
|
|
// rtell (ptr + 6 + 3 + ptr [8]));
|
|
sequence_headers++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
pad_packet_length = 0;
|
|
|
|
if (14 + vid_packet_length < SECT_SIZE - 6) {
|
|
// video packet does not fill whole sector
|
|
// check for padding packet
|
|
ptr += vid_packet_length;
|
|
|
|
pad_packet_start_code = (uint32_t) (ptr [0]) << 24;
|
|
pad_packet_start_code |= (uint32_t) (ptr [1]) << 16;
|
|
pad_packet_start_code |= (uint32_t) (ptr [2]) << 8;
|
|
pad_packet_start_code |= (uint32_t) (ptr [3]);
|
|
|
|
if (pad_packet_start_code != 0x000001be)
|
|
tqDebug ("%s", (TQString("Bad padding packet start code at %1: %2").arg(rtell (ptr + vid_packet_length)).arg(pad_packet_start_code)).ascii());
|
|
else {
|
|
pad_packet_length = ptr [4] << 8;
|
|
pad_packet_length |= ptr [5];
|
|
pad_packet_length += 6;
|
|
}
|
|
}
|
|
|
|
// length of video packet plus padding packet must always match sector size
|
|
if (14 + vid_packet_length + pad_packet_length != SECT_SIZE)
|
|
tqDebug ("%s", (TQString("Bad video packet length at %1: %2").arg(rtell (ptr)).arg(vid_packet_length)).ascii());
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
// here we go
|
|
// this is where we switch to the requantization thread
|
|
// note that this and the requant thread never run concurrently (apart
|
|
// from a very short time) so a dual CPU box does not give an advantage
|
|
// returns size of evaporated GOP
|
|
int k9vamps::requant (uchar *dst, uchar *src, int n, float fact) {
|
|
if (n==0) return 0;
|
|
int rv;
|
|
if (! m_requant->running()) {
|
|
m_requant->initvar();
|
|
}
|
|
m_requant->rqt_stop=false;
|
|
// this ensures for the requant thread to stop at this GOP's end
|
|
tc_memcpy (src + n, "\0\0\1", 3);
|
|
|
|
m_requant->mutr.lock();
|
|
|
|
m_requant->rqt_rptr = src;
|
|
m_requant->rqt_wptr = dst;
|
|
m_requant->rqt_rcnt = n;
|
|
m_requant->rqt_wcnt = 0;
|
|
m_requant->rqt_fact = fact ;
|
|
m_requant->rqt_inbytes = vin_bytes;
|
|
m_requant->rqt_outbytes = vout_bytes;
|
|
m_requant->rqt_visize = (uint64_t) ((float) ps_size * (float) vin_bytes / ((float) total_packs * (float) SECT_SIZE));
|
|
|
|
// create requantization thread
|
|
if (! m_requant->running()) {
|
|
m_requant->start();
|
|
m_requant->rqt_run=true;
|
|
}
|
|
|
|
m_requant->condr.wakeAll();
|
|
m_requant->mutr.unlock();
|
|
|
|
// now the requant thread should be running
|
|
|
|
m_requant->mutw.lock();
|
|
|
|
// wait for requant thread to finish
|
|
while (!m_requant->rqt_wcnt)
|
|
m_requant->condw.wait( &m_requant->mutw);
|
|
|
|
rv = m_requant->rqt_wcnt;
|
|
|
|
m_requant->mutw.unlock();
|
|
/* if ((m_requant->rbuf-m_requant->cbuf -3) >0 ) {
|
|
tc_memcpy(dst+m_requant->rqt_wcnt,m_requant->cbuf,m_requant->rbuf-m_requant->cbuf -3);
|
|
rv +=m_requant->rbuf-m_requant->cbuf -3;
|
|
}
|
|
/*/
|
|
if ((m_requant->rbuf-m_requant->cbuf -2) >0 ) {
|
|
tc_memcpy(dst+m_requant->rqt_wcnt,m_requant->cbuf,m_requant->rbuf-m_requant->cbuf -2);
|
|
rv +=m_requant->rbuf-m_requant->cbuf -2;
|
|
}
|
|
|
|
|
|
|
|
// if (rv>n)
|
|
// tqDebug("requant error");
|
|
|
|
double realrqtfact=(double)(vin_bytes) / (double)(vout_bytes+rv);
|
|
avgdiff = ((m_avgfact) /realrqtfact);
|
|
|
|
//tqDebug ("factor : " +TQString::number(m_avgfact) +" --> " +TQString::number((float)n/(float)rv) +" avgdiff : " + TQString::number(avgdiff) +" rqt_visize :" +TQString::number(m_requant->rqt_visize) +" ps_size :" +TQString::number(ps_size) + " vin_bytes :" + TQString::number(vin_bytes)) ;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
// translate type of private stream 1 packet
|
|
// according to the track translation maps
|
|
// returns new track type (e.g. 0x80 for first AC3 audio
|
|
// track in cmd line) or zero if track is not to be copied
|
|
int k9vamps::new_private_1_type (uchar *ptr) {
|
|
int type, track, abase;
|
|
|
|
type = ptr [6 + 3 + ptr [8]];
|
|
//fprintf (stderr, "type=%02x\n", type);
|
|
|
|
if (type >= 0x20 && type <= 0x3f) {
|
|
// subpicture
|
|
|
|
track = spu_track_map [type - 0x20];
|
|
|
|
return track ? track - 1 + 0x20 : 0;
|
|
}
|
|
|
|
if (type >= 0x80 && type <= 0x87) {
|
|
// AC3 audio
|
|
abase = 0x80;
|
|
} else if (type >= 0x88 && type <= 0x8f) {
|
|
// DTS audio
|
|
abase = 0x88;
|
|
} else if (type >= 0xa0 && type <= 0xa7) {
|
|
// LPCM audio
|
|
abase = 0xa0;
|
|
} else {
|
|
// fatal ("Unknown private stream 1 type at %llu: %02x", rtell (ptr), type);
|
|
abase = 0;
|
|
}
|
|
|
|
track = audio_track_map [type - abase];
|
|
|
|
return track ? track - 1 + abase : 0;
|
|
}
|
|
|
|
|
|
// selectivly copy private stream 1 packs
|
|
// patches track type to reflect new track
|
|
// mapping unless user opted to preserve them
|
|
void k9vamps::copy_private_1 (uchar *ptr) {
|
|
int type;
|
|
|
|
type = new_private_1_type (ptr);
|
|
|
|
if (type) {
|
|
if (!m_preserve)
|
|
ptr [6 + 3 + ptr [8]] = type;
|
|
|
|
copy (SECT_SIZE);
|
|
|
|
return;
|
|
}
|
|
|
|
skip (SECT_SIZE);
|
|
}
|
|
|
|
|
|
// translate ID of MPEG audio packet
|
|
// according to the audio track translation map
|
|
// returns new ID (e.g. 0xc0 for first MPEG audio
|
|
// track in cmd line) or zero if track is not to be copied
|
|
int k9vamps::new_mpeg_audio_id (int id) {
|
|
int track;
|
|
|
|
track = audio_track_map [id - 0xc0];
|
|
|
|
return track ? track - 1 + 0xc0 : 0;
|
|
}
|
|
|
|
|
|
// selectivly copy MPEG audio packs
|
|
// patches ID to reflect new track mapping unless user opted to preserve them
|
|
void k9vamps::copy_mpeg_audio (uchar *ptr) {
|
|
int id;
|
|
|
|
id = new_mpeg_audio_id (ptr [3]);
|
|
|
|
if (id) {
|
|
if (!m_preserve)
|
|
ptr [3] = id;
|
|
|
|
copy (SECT_SIZE);
|
|
|
|
return;
|
|
}
|
|
|
|
skip (SECT_SIZE);
|
|
}
|
|
|
|
|
|
// process beginning of program stream up to
|
|
// - but not including - first sequence header
|
|
// this PS leader is NOT shrunk since the PS may not
|
|
// necessarily begin at a GOP boundary (although it should?)
|
|
// nevertheless the unwanted private stream 1 and MPEG audio
|
|
// packs are skipped since some players could get confused otherwise
|
|
void k9vamps::vap_leader () {
|
|
uchar *ptr;
|
|
int id, data_length;
|
|
|
|
while (!lock (SECT_SIZE)) {
|
|
ptr = rptr;
|
|
if (check_pack (ptr)) {
|
|
ptr += 14;
|
|
id = ptr [3];
|
|
} else {
|
|
ptr +=14;
|
|
id = 0;
|
|
}
|
|
|
|
switch (id) {
|
|
case 0xe0:
|
|
// video
|
|
if (check_video_packet (ptr))
|
|
// sequence header
|
|
return;
|
|
|
|
copy (SECT_SIZE);
|
|
break;
|
|
|
|
case 0xbd:
|
|
// private 1: audio/subpicture
|
|
copy_private_1 (ptr);
|
|
break;
|
|
|
|
case 0xc0:
|
|
case 0xc1:
|
|
case 0xc2:
|
|
case 0xc3:
|
|
case 0xc4:
|
|
case 0xc5:
|
|
case 0xc6:
|
|
case 0xc7:
|
|
// MPEG audio
|
|
copy_mpeg_audio (ptr);
|
|
break;
|
|
|
|
case 0xbb:
|
|
// system header/private 2: PCI/DSI
|
|
copy (SECT_SIZE);
|
|
break;
|
|
|
|
case 0xbe:
|
|
// padding
|
|
data_length = ptr [4] << 8;
|
|
data_length |= ptr [5];
|
|
|
|
if (14 + data_length != SECT_SIZE - 6)
|
|
fatal (TQString("Bad padding packet length at %1: %2").arg(rtell (ptr)).arg(data_length));
|
|
//JMP:à vérifier
|
|
skip (SECT_SIZE);
|
|
|
|
break;
|
|
|
|
default:
|
|
// fatal("Encountered stream ID %02x at %llu, "
|
|
// "probably bad MPEG2 program stream", id, rtell (ptr));
|
|
copy (SECT_SIZE);
|
|
}
|
|
|
|
if (wptr == wbuf + WBUF_SIZE)
|
|
flush ();
|
|
}
|
|
|
|
eof = 1;
|
|
flush ();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// process end of program stream
|
|
// the same counts here as for the PS' beginning
|
|
void k9vamps::vap_trailer (int length) {
|
|
uchar *ptr;
|
|
int i, id, data_length;
|
|
|
|
for (i = 0; i < length; i += SECT_SIZE) {
|
|
ptr = rptr + 14;
|
|
id = ptr [3];
|
|
|
|
if (id == 0xbd) {
|
|
// private 1: audio/subpicture
|
|
copy_private_1 (ptr);
|
|
} else if (id >= 0xc0 && id <= 0xc7) {
|
|
// MPEG audio
|
|
copy_mpeg_audio (ptr);
|
|
} else if (id == 0xbe) {
|
|
// padding
|
|
data_length = ptr [4] << 8;
|
|
data_length |= ptr [5];
|
|
|
|
if (14 + data_length != SECT_SIZE - 6)
|
|
fatal (TQString("Bad padding packet length at %1: %2").arg(rtell (ptr)).arg(data_length));
|
|
skip (SECT_SIZE);
|
|
} else {
|
|
copy (SECT_SIZE);
|
|
}
|
|
|
|
if (wptr == wbuf + WBUF_SIZE)
|
|
flush ();
|
|
}
|
|
|
|
flush ();
|
|
}
|
|
|
|
|
|
// vaporization is split in two phases - this is phase 1
|
|
// PS packs are read into rbuf until a sequence header is found.
|
|
// All video packs are unpacketized and the contained video ES
|
|
// GOP copied to vibuf. In the same course the private stream 1
|
|
// and MPEG audio packs are inspected and the number of packs
|
|
// not to be copied are counted. This is to forecast the video
|
|
// vaporization factor in case the user specified a PS shrink factor.
|
|
// returns GOP length in bytes
|
|
int k9vamps::vap_phase1 (void) {
|
|
uchar *ptr, *viptr = vibuf;
|
|
int seq_length, id, data_length, opt_length, seqhdr;
|
|
|
|
for (seq_length = 0;
|
|
!lock (seq_length + SECT_SIZE); seq_length += SECT_SIZE) {
|
|
ptr = rptr + seq_length;
|
|
if (check_pack (ptr)) {
|
|
ptr += 14;
|
|
id = ptr [3];
|
|
} else {
|
|
ptr += 14;
|
|
id = 0;
|
|
}
|
|
|
|
|
|
// avoid duplicate counts for sequence headers
|
|
if (seq_length)
|
|
total_packs++;
|
|
|
|
switch (id) {
|
|
case 0xe0:
|
|
// video
|
|
seqhdr = check_video_packet (ptr);
|
|
|
|
if (seq_length) {
|
|
video_packs++;
|
|
|
|
if (seqhdr) {
|
|
sequence_headers++;
|
|
vilen = viptr - vibuf;
|
|
|
|
return seq_length;
|
|
}
|
|
}
|
|
|
|
// copy contained video ES fragment to vibuf
|
|
data_length = ptr [4] << 8;
|
|
data_length |= ptr [5];
|
|
opt_length = 3 + ptr [8];
|
|
data_length -= opt_length;
|
|
|
|
if ((viptr - vibuf) + data_length > vbuf_size - 3) {
|
|
// reallocate video buffers
|
|
int i = viptr - vibuf;
|
|
|
|
// grow by another VBUF_SIZE bytes
|
|
vbuf_size += VBUF_SIZE;
|
|
vibuf = (uchar*)realloc (vibuf, vbuf_size);
|
|
vobuf = (uchar*)realloc (vobuf, vbuf_size);
|
|
|
|
if (vibuf == NULL || vobuf == NULL)
|
|
fatal ("Reallocation of video buffers failed");
|
|
|
|
viptr = vibuf + i;
|
|
}
|
|
|
|
//fprintf (stderr, "data_length=%d\n", data_length);
|
|
tc_memcpy (viptr, ptr + 6 + opt_length, data_length);
|
|
viptr += data_length;
|
|
break;
|
|
|
|
case 0xbd:
|
|
// private 1: audio/subpicture
|
|
aux_packs++;
|
|
|
|
if (!new_private_1_type (ptr))
|
|
skipped_aux_packs++;
|
|
|
|
break;
|
|
|
|
case 0xc0:
|
|
case 0xc1:
|
|
case 0xc2:
|
|
case 0xc3:
|
|
case 0xc4:
|
|
case 0xc5:
|
|
case 0xc6:
|
|
case 0xc7:
|
|
// MPEG audio
|
|
aux_packs++;
|
|
|
|
if (!new_mpeg_audio_id (id))
|
|
skipped_aux_packs++;
|
|
|
|
break;
|
|
|
|
case 0xbb:
|
|
// system header/private 2: PCI/DSI
|
|
nav_packs++;
|
|
break;
|
|
|
|
case 0xbe:
|
|
// padding
|
|
skipped_aux_packs++;
|
|
data_length = ptr [4] << 8;
|
|
data_length |= ptr [5];
|
|
|
|
if (14 + data_length != SECT_SIZE - 6)
|
|
fatal (TQString("Bad padding packet length at %1: %2").arg(rtell (ptr)).arg(data_length));
|
|
|
|
break;
|
|
|
|
default:
|
|
// fatal("Encountered stream ID %02x at %llu, "
|
|
// "probably bad MPEG2 program stream", id, rtell (ptr));
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
eof = 1;
|
|
|
|
return seq_length;
|
|
}
|
|
|
|
|
|
// re-packetize the video ES
|
|
// `ptr' points to original PES packet where to put the video data
|
|
// `voptr' points to first unpacketized byte in vobuf
|
|
// `avail' specifies number of bytes remaining in vobuf
|
|
// returns number of ES bytes in generated PES packet
|
|
int k9vamps::gen_video_packet (uchar *ptr, uchar *voptr, int avail) {
|
|
int i, header_data_length, data_length, padding_length;
|
|
|
|
// if original PES holds optional data (e.g. DTS/PTS) we must keep it
|
|
header_data_length = (ptr [7] & 0xc0) == 0xc0 ? ptr [8] : 0;
|
|
data_length = SECT_SIZE - (14 + 6 + 3 + header_data_length);
|
|
|
|
if (avail >= data_length) {
|
|
// write out a full video packet (usually 2025 byte)
|
|
tc_memcpy (ptr + 6 + 3 + header_data_length, voptr, data_length);
|
|
ptr [4] = (SECT_SIZE - (14 + 6)) >> 8;
|
|
ptr [5] = (SECT_SIZE - (14 + 6)) & 0xff;
|
|
ptr [8] = header_data_length;
|
|
|
|
return data_length;
|
|
}
|
|
|
|
if (avail < data_length - 6) {
|
|
// write a short video packet and a padding packet
|
|
tc_memcpy (ptr + 6 + 3 + header_data_length, voptr, avail);
|
|
ptr [4] = (3 + header_data_length + avail) >> 8;
|
|
ptr [5] = 3 + header_data_length + avail;
|
|
ptr [8] = header_data_length;
|
|
|
|
// generate padding packet
|
|
ptr += 6 + 3 + header_data_length + avail;
|
|
padding_length = data_length - (avail + 6);
|
|
padding_bytes += padding_length + 6;
|
|
ptr [0] = 0;
|
|
ptr [1] = 0;
|
|
ptr [2] = 1;
|
|
ptr [3] = 0xbe;
|
|
ptr [4] = padding_length >> 8;
|
|
ptr [5] = padding_length;
|
|
|
|
for (i = 0; i < padding_length; i++)
|
|
ptr [6+i] = 0xff;
|
|
|
|
return avail;
|
|
}
|
|
|
|
// write a padded video packet (1 to 6 padding bytes)
|
|
padding_length = data_length - avail;
|
|
padding_bytes += padding_length;
|
|
memset (ptr + 6 + 3 + header_data_length, 0xff, padding_length);
|
|
header_data_length += padding_length;
|
|
tc_memcpy (ptr + 6 + 3 + header_data_length, voptr, avail);
|
|
ptr [4] = (SECT_SIZE - (14 + 6)) >> 8;
|
|
ptr [5] = (SECT_SIZE - (14 + 6)) & 0xff;
|
|
ptr [8] = header_data_length;
|
|
|
|
return avail;
|
|
}
|
|
|
|
|
|
// this is phase 2 of vaporization
|
|
// the shrunk video ES is re-packetized by using the source PES packets
|
|
// unused PS packs are skipped
|
|
// only wanted private stream 1 and MPEG audio packs are copied
|
|
// all nav packs are copied
|
|
void k9vamps::vap_phase2 (int seq_length) {
|
|
int i, id, avail, data_length;
|
|
uchar *ptr, *voptr = vobuf, *vohwp = vobuf + volen;
|
|
|
|
for (i = 0; i < seq_length; i += SECT_SIZE) {
|
|
ptr = rptr + 14;
|
|
id = ptr [3];
|
|
|
|
switch (id) {
|
|
case 0xe0:
|
|
// video
|
|
avail = vohwp - voptr;
|
|
|
|
if (avail) {
|
|
// still some video output data left
|
|
voptr += gen_video_packet (ptr, voptr, avail);
|
|
copy (SECT_SIZE);
|
|
} else {
|
|
// no video output data left - skip input sector
|
|
skip (SECT_SIZE);
|
|
skipped_video_packs++;
|
|
}
|
|
|
|
break;
|
|
|
|
case 0xbd:
|
|
// private 1: audio/subpicture
|
|
copy_private_1 (ptr);
|
|
break;
|
|
|
|
case 0xc0:
|
|
case 0xc1:
|
|
case 0xc2:
|
|
case 0xc3:
|
|
case 0xc4:
|
|
case 0xc5:
|
|
case 0xc6:
|
|
case 0xc7:
|
|
// MPEG audio
|
|
copy_mpeg_audio (ptr);
|
|
break;
|
|
|
|
case 0xbb:
|
|
// system header/private 2: PCI/DSI
|
|
copy (SECT_SIZE);
|
|
break;
|
|
|
|
case 0xbe:
|
|
// padding
|
|
data_length = ptr [4] << 8;
|
|
data_length |= ptr [5];
|
|
|
|
if (14 + data_length != SECT_SIZE - 6)
|
|
fatal (TQString("Bad padding packet length at %1: %2").arg(rtell (ptr)).arg(data_length));
|
|
//JMP: à vérifier
|
|
skip (SECT_SIZE);
|
|
break;
|
|
|
|
default:
|
|
copy (SECT_SIZE);
|
|
// fatal("Encountered stream ID %02x at %llu, "
|
|
// "probably bad MPEG2 program stream", id, rtell (ptr));
|
|
}
|
|
|
|
if (wptr == wbuf + WBUF_SIZE)
|
|
// end of write buffer reached --> flush it to disk
|
|
flush ();
|
|
}
|
|
}
|
|
|
|
TQString & k9vamps::geterrMsg() {
|
|
return m_errMsg;
|
|
}
|
|
|
|
bool k9vamps::geterror() {
|
|
return m_error;
|
|
}
|
|
|
|
// entry point from main()
|
|
// the requant thread already has been started
|
|
void k9vamps::vaporize (void) {
|
|
int seq_length;
|
|
float fact = vap_fact;
|
|
|
|
// process PS up to but not including first sequence header
|
|
vap_leader ();
|
|
|
|
// just in case - maybe should spit out a warning/error here
|
|
if (eof)
|
|
return;
|
|
|
|
total_packs++;
|
|
nav_packs++;
|
|
total_packs++;
|
|
video_packs++;
|
|
|
|
// main loop
|
|
while (1) {
|
|
// do phase 1 of vaporization
|
|
seq_length = vap_phase1 ();
|
|
|
|
if (eof) {
|
|
// EOF on source PS
|
|
// process packs after and including last sequence header
|
|
vap_trailer (seq_length);
|
|
|
|
// only exit point from main loop
|
|
return;
|
|
}
|
|
|
|
//fprintf (stderr, "seq_length=%d\n", seq_length);
|
|
|
|
if (calc_ps_vap && vap_fact > 1.0f) {
|
|
// forecast video ES vaporization factor
|
|
// the basic formulars look like:
|
|
// vap_fact = total_packs/(restpacks+vop)
|
|
// restpacks = total_packs-(video_packs+skipped_aux_packs)
|
|
// fact = (video_packs*net-(gops*net/2+10))/(vop*net-(gops*net/2+10))
|
|
// net = SECT_SIZE-(14+9)
|
|
// 14: pack header size
|
|
// 9: PES header size
|
|
// 10: PTS+DTS size in PES header of sequence header
|
|
// You are welcome to double check everything here!
|
|
float vop, net;
|
|
net = (float) (SECT_SIZE - (14+9));
|
|
vop = video_packs + skipped_aux_packs -
|
|
(float) total_packs * (1.0f-1.0f/vap_fact);
|
|
fact = ((float) video_packs * net -
|
|
((float) sequence_headers * net/2.0f + 10.0f)) /
|
|
(vop * net - ((float) sequence_headers * net/2.0f + 10.0f));
|
|
|
|
//JMP
|
|
m_totfact+=fact ;
|
|
m_nbfact++;
|
|
m_avgfact=m_totfact/m_nbfact;
|
|
|
|
// requant seems to get stuck on factors < 1
|
|
if (fact < 1.0f)
|
|
fact = 1.0f;
|
|
|
|
if (verbose >= 2)
|
|
fprintf (stderr, "Info: Target video ES vaporization factor: %.3f\n",
|
|
fact);
|
|
}
|
|
|
|
vin_bytes += vilen;
|
|
|
|
if (fact > 1.0f) {
|
|
// do requantization
|
|
volen = requant (vobuf, vibuf, vilen, fact);
|
|
} else {
|
|
// don't do requantization
|
|
tc_memcpy (vobuf, vibuf, vilen);
|
|
volen = vilen;
|
|
}
|
|
|
|
vout_bytes += volen;
|
|
|
|
// do phase 2 of vaporization
|
|
vap_phase2 (seq_length);
|
|
|
|
//fprintf (stderr,
|
|
// "tot=%d, vid=%d, ps1=%d, nav=%d, sv=%d, sp1=%d, fact=%.3f\n",
|
|
// total_packs, video_packs, aux_packs, nav_packs,
|
|
// skipped_video_packs, skipped_aux_packs, fact);
|
|
}
|
|
}
|
|
|
|
uint64_t k9vamps::getOutputBytes() {
|
|
return bytes_written;
|
|
}
|
|
|
|
void k9vamps::abort() {
|
|
//fatal("vamps stopped");
|
|
setNoData();
|
|
if (m_requant !=NULL)
|
|
m_requant->wait();
|
|
if (m_bgUpdate!=NULL)
|
|
m_bgUpdate->wait();
|
|
}
|
|
|
|
// this is a *very* sophisticated kind of error handling :-)
|
|
void
|
|
k9vamps::fatal (TQString msg) {
|
|
m_errMsg=msg;
|
|
m_error=true;
|
|
if (m_requant !=NULL)
|
|
m_requant->terminate();
|
|
if (m_bgUpdate !=NULL)
|
|
m_bgUpdate->terminate();
|
|
terminate();
|
|
}
|
|
|
|
/**************************** BACKGROUND UPDATE **********************/
|
|
|
|
k9bgUpdate::k9bgUpdate(k9DVDBackup * _backup) {
|
|
m_backup = _backup;
|
|
|
|
}
|
|
|
|
void k9bgUpdate::update(uchar *_buffer,uint32_t _size) {
|
|
mutex.lock();
|
|
m_buffer=(uchar*)malloc(_size);
|
|
tc_memcpy(m_buffer,_buffer,_size);
|
|
m_size=_size;
|
|
start();
|
|
mutex.unlock();
|
|
}
|
|
|
|
void k9bgUpdate::run() {
|
|
m_backup->getOutput(m_buffer,m_size);
|
|
free(m_buffer);
|
|
}
|