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.
568 lines
16 KiB
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;
|
|
}
|
|
|
|
|
|
|
|
|
|
|