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.
k9copy/src/k9play.cpp

568 lines
16 KiB

//
// C++ Implementation: k9play
//
// Description:
//
//
// Author: Jean-Michel PETIT <k9copy@free.fr>, (C) 2006
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "k9play.h"
#include "k9dvdread.h"
#include "k9cell.h"
#include "k9vamps.h"
#include "ac.h"
#include "dvdnav.h"
#include "k9saveimage.h"
#include <stdio.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "dvdread.h"
#include <tdetempfile.h>
#include <kstandarddirs.h>
void k9play::saveStatus(k9play_st _status) {
TQFile fstatus(m_inject);
fstatus.open(IO_WriteOnly);
fstatus.writeBlock((const char*)&_status,sizeof(k9play_st));
fstatus.close();
kdebug (TQString("saving status : %1 %2 %3 %4 %5 %6 %7\n").arg(_status.title).arg(_status.chapter).arg(_status.cell).arg(_status.sector).arg(_status.bytesWritten).arg(_status.bytesRead).arg(_status.bytesSkipped));
}
void k9play::readStatus(k9play_st &_status) {
TQFile fstatus(m_inject);
if (fstatus.open(IO_ReadOnly)) {
fstatus.readBlock((char*)&_status,sizeof(k9play_st));
fstatus.close();
} else memset(&_status,0,sizeof(k9play_st));
kdebug (TQString("reading status : title:%1 chapter:%2 cell:%3 sector:%4 written:%5 readen:%6 skipped:%7 chapters:%8 \n").arg(_status.title).arg(_status.chapter).arg(_status.cell).arg(_status.sector).arg(_status.bytesWritten).arg(_status.bytesRead).arg(_status.bytesSkipped).arg(_status.bytesChapters));
}
k9play::k9play() {
m_stderr.open(IO_WriteOnly,stderr);
m_startSector=0xFFFFFFFF;
m_endSector=0xFFFFFFFF;
m_vampsFactor=1;
m_inputSize=1;
m_chapterSize=0;
m_chapter=0;
m_cell=0;
m_totalSize=0;
m_forcedFactor=false;
m_firstPass=false;
m_useCache=false;
}
void k9play::kdebug(TQString const & _msg) {
#ifdef debug
m_stderr.writeBlock(_msg.latin1(),_msg.length());
#endif
}
void k9play::writeOutput(TQString const & _msg) {
m_stderr.writeBlock(_msg.latin1(),_msg.length());
}
k9play::~k9play() {
m_stderr.close();
}
void k9play::setstartSector(TQString _value) {
if (_value !="")
m_startSector=_value.toUInt();
}
void k9play::setinject(TQString _value) {
m_inject=_value;
}
void k9play::setendSector(TQString _value) {
if (_value!="")
m_endSector=_value.toUInt();
}
void k9play::setaudioFilter( TQString _value) {
if (_value!="")
m_audioFilter=TQStringList::split(",",_value);
}
void k9play::setsubpictureFilter( TQString _value) {
if (_value!="")
m_subpictureFilter=TQStringList::split(",",_value);
}
void k9play::setchapterList( TQString _value) {
if (_value!="")
m_chapterList=TQStringList::split(",",_value);
}
void k9play::setvampsFactor(TQString _value) {
if (_value!="")
m_vampsFactor=_value.toDouble();
}
void k9play::setinputSize( TQString _value) {
if (_value!="")
m_inputSize=_value.toULongLong();
}
void k9play::settotalSize( TQString _value) {
if (_value!="")
m_totalSize=_value.toULongLong();
}
void k9play::setdvdSize( TQString _value) {
if (_value!="")
m_dvdSize=_value.toULongLong();
}
void k9play::setchapterSize( TQString _value) {
if (_value!="")
m_chapterSize=_value.toULongLong();
}
void k9play::setchapter( TQString _value) {
if (_value!="")
m_chapter=_value.toUInt();
}
void k9play::setcell(TQString _value) {
if (_value !="")
m_cell=_value.toUInt();
}
void k9play::setinitStatus(bool _value) {
m_initstatus=_value;
}
void k9play::setcontinue(bool _value) {
m_continue=_value;
}
void k9play::setfirstPass(bool _value) {
m_firstPass=_value;
}
void k9play::setforcedFactor( bool _value) {
m_forcedFactor=_value;
}
void k9play::setuseCache(bool _value) {
m_useCache=_value;
}
void k9play::execute() {
//playCell();
play();
return;
}
void k9play::insert_nav_pack (int8_t *buf)
{
int8_t *ptr = (int8_t*)buf;
static uint8_t nav_pack1 [] =
{
/* pack header: SCR=0, mux rate=10080000bps, stuffing length=0 */
0, 0, 1, 0xba, 0x44, 0x00, 0x04, 0x00, 0x04, 0x01, 0x01, 0x89, 0xc3, 0xf8,
/* system header */
0, 0, 1, 0xbb, 0x00, 0x12,
/* contents of system header filled in at run time (18 bytes) */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* PES header for first private stream 2 packet */
0, 0, 1, 0xbf, 0x03, 0xd4
};
static uint8_t nav_pack2 [] =
{
/* PES header for second private stream 2 packet */
0, 0, 1, 0xbf, 0x03, 0xfa
};
memcpy (ptr, nav_pack1, sizeof (nav_pack1));
ptr += sizeof (nav_pack1);
memset (ptr, 0, DVD_VIDEO_LB_LEN/2 - sizeof (nav_pack1));
ptr = buf + DVD_VIDEO_LB_LEN/2;
memcpy (ptr, nav_pack2, sizeof (nav_pack2));
ptr += sizeof (nav_pack2);
memset (ptr, 0, DVD_VIDEO_LB_LEN/2 - sizeof (nav_pack2));
}
void k9play::insert_dummy_pack (int8_t *buf)
{
int8_t *ptr = buf;
static uint8_t dummy_pack [] =
{
/* pack header: SCR=0, mux rate=10080000bps, stuffing length=0 */
0, 0, 1, 0xba, 0x44, 0x00, 0x04, 0x00, 0x04, 0x01, 0x01, 0x89, 0xc3, 0xf8,
/* PES header for dummy video packet */
0, 0, 1, 0xe0, 0x07, 0xec, 0x81, 0x00, 0x00
};
memcpy (ptr, dummy_pack, sizeof (dummy_pack));
ptr += sizeof (dummy_pack);
memset (ptr, 0xff, DVD_VIDEO_LB_LEN - sizeof (dummy_pack));
}
/* which is the default language for menus/audio/subpictures? */
#define DVD_LANGUAGE "en"
#define DVD_READ_CACHE 1
void k9play::play() {
dvdnav_t *dvdnav;
uint8_t mem[DVD_VIDEO_LB_LEN];
int finished = 0;
bool skipped=false;
int32_t tt = 0,ptt=0;
uint32_t pos, lgr;
uint currCell=0;
m_pos=0xFFFFFFFF;
k9play_st status;
if (m_initstatus)
memset(&status,0,sizeof(k9play_st));
else {
readStatus( status);
if (m_continue)
m_startSector=status.sector;
}
KTempFile *bufferFile = 0;
if (m_useCache) {
bufferFile=new KTempFile(locateLocal("tmp", "k9copy/k9p"), "");
m_output=bufferFile->file();
} else {
m_output=new TQFile();
m_output->open(IO_WriteOnly,stdout);
}
k9vamps vamps(NULL);
vamps.reset();
vamps.setPreserve( false);
vamps.setOutput(m_output);
k9SaveImage saveImage;
saveImage.play();
vamps.setSaveImage(&saveImage);
// if reading of previous cell reached end of chapter, don't seek for cell
if (m_chapter !=0 && m_cell !=0) {
if (m_cell==1) {
if (status.bytesRead <=status.bytesChapters)
status.bytesSkipped = status.bytesChapters - status.bytesRead;
else
status.bytesSkipped=0;
status.bytesChapters += m_chapterSize;
}
if (status.title == m_title &&
status.chapter > m_chapter) {
skipped=true;
}
}
//vamps.setVapFactor( m_vampsFactor);
if (m_totalSize>0 && !m_forcedFactor) {
double factor;
factor = (double) (m_totalSize - (status.bytesRead +status.bytesSkipped)) / (double) (m_dvdSize-status.bytesWritten) ;
if (factor <1) factor =1;
kdebug(TQString("shrink factor %1 totalSize:%2 (status.bytesRead +status.bytesSkipped):%3 m_dvdSize:%4 status.bytesWritten:%5").arg(factor).arg(m_totalSize).arg(status.bytesRead +status.bytesSkipped).arg(m_dvdSize).arg(status.bytesWritten) );
vamps.setVapFactor(factor);
} else {
vamps.setVapFactor(m_vampsFactor);
kdebug(TQString("vamps factor %1\n").arg(m_vampsFactor));
}
vamps.setInputSize(m_inputSize);
for ( TQStringList::Iterator it = m_audioFilter.begin(); it != m_audioFilter.end(); ++it ) {
vamps.addAudio((*it).toInt());
}
for ( TQStringList::Iterator it = m_subpictureFilter.begin(); it != m_subpictureFilter.end(); ++it ) {
vamps.addSubpicture((*it).toInt());
}
/* open dvdnav handle */
if (dvdnav_open(&dvdnav, m_device,NULL) != DVDNAV_STATUS_OK) {
writeOutput("ERR:Error on dvdnav_open\n");
return ;
}
/* set read ahead cache usage */
if (dvdnav_set_readahead_flag(dvdnav, DVD_READ_CACHE) != DVDNAV_STATUS_OK) {
writeOutput( TQString("ERR:Error on dvdnav_set_readahead_flag: %1\n").arg(dvdnav_err_to_string(dvdnav)));
return;
}
/* set the language */
if (dvdnav_menu_language_select(dvdnav, DVD_LANGUAGE) != DVDNAV_STATUS_OK ||
dvdnav_audio_language_select(dvdnav, DVD_LANGUAGE) != DVDNAV_STATUS_OK ||
dvdnav_spu_language_select(dvdnav, DVD_LANGUAGE) != DVDNAV_STATUS_OK) {
writeOutput( TQString("ERR:Error on setting languages: %1\n").arg(dvdnav_err_to_string(dvdnav)));
return ;
}
/* set the PGC positioning flag to have position information relatively to the
* whole feature instead of just relatively to the current chapter */
if (dvdnav_set_PGC_positioning_flag(dvdnav, 1) != DVDNAV_STATUS_OK) {
writeOutput(TQString("ERR:Error on dvdnav_set_PGC_positioning_flag: %1\n").arg(dvdnav_err_to_string(dvdnav)));
return ;
}
int32_t parts;
dvdnav_get_number_of_parts(dvdnav , m_title, &parts);
if (m_chapter ==0)
dvdnav_title_play(dvdnav , m_title);
else {
dvdnav_part_play(dvdnav, m_title, m_chapter);
ptt=m_chapter;
}
/* the read loop which regularly calls dvdnav_get_next_block
* and handles the returned events */
bool bcopy=false;
bool bcell=true;
while (!finished && !skipped) {
int result, event, len;
uint8_t *buf = mem;
/* the main reading function */
#if DVD_READ_CACHE
result = dvdnav_get_next_cache_block(dvdnav, &buf, &event, &len);
#else
result = dvdnav_get_next_block(dvdnav, buf, &event, &len);
#endif
if (result == DVDNAV_STATUS_ERR) {
writeOutput(TQString("ERR:Error getting next block: %1\n").arg(dvdnav_err_to_string(dvdnav)));
return;
}
switch (event) {
case DVDNAV_NAV_PACKET:
{
dvdnav_current_title_info(dvdnav, &tt, &ptt);
dvdnav_get_position(dvdnav, &pos, &lgr);
m_length=lgr;
status.title=tt;
status.chapter=ptt;
status.cell=currCell;
status.sector=pos;
if ((m_endSector !=0xFFFFFFFF) && (((status.bytesRead+status.bytesSkipped)/2048) >m_endSector)) {
finished=1;
kdebug(TQString("pos >m_endSector %1 %2").arg((status.bytesRead+status.bytesSkipped)/2048).arg(m_endSector));
}
if ((m_chapter !=0 && ptt !=m_chapter) || (tt != m_title))
finished=1;
if (m_cell!=0 && currCell>m_cell)
finished=1;
if (!finished && m_chapterList.count() >0) {
if (m_chapterList.findIndex( TQString::number(ptt)) == -1) {
dvdnav_part_play(dvdnav,tt, ptt+1);
kdebug( TQString("skipping chapter %1").arg(ptt));
continue;
//dvdnav_part_play(dvdnav_t *self, int32_t title, int32_t part);
}
}
if (m_continue) {
dvdnav_sector_search(dvdnav,m_startSector , SEEK_SET);
kdebug (TQString("repositionning on %1").arg(m_startSector));
m_continue=false;
finished=0;
bcell=true;
} else {
if ((m_cell==0 || (m_cell!=0 && currCell==m_cell)) && finished==0) {
if (!vamps.running())
vamps.start(TQThread::NormalPriority);
bcopy=true;
vamps.addData( buf,len);
status.bytesRead +=len;
if (!m_useCache)
writeOutput(TQString("\rINFOPOS: %1 %2").arg((status.bytesRead+status.bytesSkipped) / DVD_VIDEO_LB_LEN).arg(lgr));
if (m_pos==0xFFFFFFFF)
m_pos=(status.bytesRead+status.bytesSkipped) / DVD_VIDEO_LB_LEN;
}
}
}
break;
//removed break --> save
case DVDNAV_BLOCK_OK:
/* We have received a regular block of the currently playing MPEG stream.*/
if (m_cell==0 || (m_cell!=0 && currCell==m_cell)) {
if (!vamps.running())
vamps.start(TQThread::NormalPriority);
vamps.addData( buf,len);
status.bytesRead +=len;
bcopy=true;
}
break;
case DVDNAV_NOP:
/* Nothing to do here. */
break;
case DVDNAV_STILL_FRAME:
/* We have reached a still frame. A real player application would wait
* the amount of time specified by the still's length while still handling
* user input to make menus and other interactive stills work.
* A length of 0xff means an indefinite still which has to be skipped
* indirectly by some user interaction. */
{
dvdnav_still_skip(dvdnav);
}
break;
case DVDNAV_WAIT:
/* We have reached a point in DVD playback, where timing is critical.
* Player application with internal fifos can introduce state
* inconsistencies, because libdvdnav is always the fifo's length
* ahead in the stream compared to what the application sees.
* Such applications should wait until their fifos are empty
* when they receive this type of event. */
dvdnav_wait_skip(dvdnav);
break;
case DVDNAV_SPU_CLUT_CHANGE:
/* Player applications should pass the new colour lookup table to their
* SPU decoder */
break;
case DVDNAV_SPU_STREAM_CHANGE:
/* Player applications should inform their SPU decoder to switch channels */
break;
case DVDNAV_AUDIO_STREAM_CHANGE:
/* Player applications should inform their audio decoder to switch channels */
break;
case DVDNAV_HIGHLIGHT:
/* Player applications should inform their overlay engine to highlight the
* given button */
break;
case DVDNAV_VTS_CHANGE:
/* Some status information like video aspect and video scale permissions do
* not change inside a VTS. Therefore this event can be used to query such
* information only when necessary and update the decoding/displaying
* accordingly. */
break;
case DVDNAV_CELL_CHANGE:
if (bcell) {
currCell++;
dvdnav_get_position(dvdnav, &pos, &lgr);
status.title=tt;
status.chapter=ptt;
status.cell=currCell;
status.sector=pos;
if (m_useCache) {
flush(saveImage);
delete bufferFile;
bufferFile=new KTempFile(locateLocal("tmp", "k9copy/k9p"), "");
m_output=bufferFile->file();
vamps.setOutput(m_output);
}
}
break;
case DVDNAV_HOP_CHANNEL:
/* This event is issued whenever a non-seamless operation has been executed.
* Applications with fifos should drop the fifos content to speed up responsiveness. */
break;
case DVDNAV_STOP:
/* Playback should end here. */
{
finished = 1;
}
break;
default:
finished = 1;
break;
}
#if DVD_READ_CACHE
dvdnav_free_cache_block(dvdnav, buf);
#endif
}
vamps.setNoData();
vamps.wait();
/* destroy dvdnav handle */
dvdnav_close(dvdnav);
if (! bcopy) {
int8_t buf[DVD_VIDEO_LB_LEN];
insert_nav_pack(buf);
m_output->writeBlock((const char*)buf,DVD_VIDEO_LB_LEN);
insert_dummy_pack(buf);
m_output->writeBlock((const char*)buf,DVD_VIDEO_LB_LEN);
}
if (m_useCache)
flush(saveImage);
else {
m_output->close();
delete m_output;
}
saveImage.stop();
status.bytesWritten +=vamps.getOutputBytes();
if (!m_firstPass)
saveStatus( status);
delete bufferFile;
}
void k9play::flush(k9SaveImage &_saveImage ) {
char buffer[20*DVD_VIDEO_LB_LEN];
m_output->reset();
TQFile out;
out.open(IO_WriteOnly,stdout);
while(!m_output->atEnd()) {
writeOutput(TQString("\rINFOPOS: %1 %2").arg(m_pos).arg(m_length));
m_pos+=20;
int l=m_output->readBlock(buffer,20*DVD_VIDEO_LB_LEN);
if (l>0) {
out.writeBlock(buffer,l);
}
}
m_output->close();
m_output->remove();
m_pos=0xFFFFFFFF;
}
bool k9play::readNavPack (k9DVDFile *fh, dsi_t *dsi,int sector,uchar *_buffer)
{
int n;
/* try max_read_retries+1 times */
n = fh->readBlocks( sector, 1,_buffer);
if (n == 1)
{
/* read Ok */
if (k9Cell::isNavPack (_buffer))
/* parse contained DSI pack */
navRead_DSI (dsi, _buffer + DSI_START_BYTE);
if (sector == dsi -> dsi_gi.nv_pck_lbn) {
return true;
}
}
return false;
}