diff options
Diffstat (limited to 'libk3b/projects/videocd')
24 files changed, 4525 insertions, 0 deletions
diff --git a/libk3b/projects/videocd/Makefile.am b/libk3b/projects/videocd/Makefile.am new file mode 100644 index 0000000..1e18d02 --- /dev/null +++ b/libk3b/projects/videocd/Makefile.am @@ -0,0 +1,20 @@ +AM_CPPFLAGS= -I$(srcdir)/../../core \ + -I$(srcdir)/../../../src \ + -I$(srcdir)/../../../libk3bdevice \ + -I$(srcdir)/../../tools \ + -I$(srcdir)/.. \ + $(all_includes) + +METASOURCES = AUTO + +noinst_LTLIBRARIES = libvcd.la + +libvcd_la_SOURCES = k3bvcddoc.cpp k3bvcdtrack.cpp k3bvcdjob.cpp k3bvcdoptions.cpp k3bvcdxmlview.cpp + +libvcd_la_LIBADD = mpeginfo/libmpeginfo.la + +SUBDIRS = cdi extra mpeginfo + +include_HEADERS = k3bvcdjob.h \ + k3bvcddoc.h \ + k3bvcdoptions.h diff --git a/libk3b/projects/videocd/cdi/Makefile.am b/libk3b/projects/videocd/cdi/Makefile.am new file mode 100644 index 0000000..e487acc --- /dev/null +++ b/libk3b/projects/videocd/cdi/Makefile.am @@ -0,0 +1,5 @@ + +cdidir = $(kde_datadir)/k3b/cdi +cdi_DATA = cdi_imag.rtf cdi_text.fnt cdi_vcd.app cdi_vcd.cfg vcd_on_cdi_41.pdf icdia.htm + +EXTRA_DIST = $(cdi_DATA) diff --git a/libk3b/projects/videocd/cdi/cdi_imag.rtf b/libk3b/projects/videocd/cdi/cdi_imag.rtf Binary files differnew file mode 100644 index 0000000..809145f --- /dev/null +++ b/libk3b/projects/videocd/cdi/cdi_imag.rtf diff --git a/libk3b/projects/videocd/cdi/cdi_text.fnt b/libk3b/projects/videocd/cdi/cdi_text.fnt Binary files differnew file mode 100644 index 0000000..0dd0e15 --- /dev/null +++ b/libk3b/projects/videocd/cdi/cdi_text.fnt diff --git a/libk3b/projects/videocd/cdi/cdi_vcd.app b/libk3b/projects/videocd/cdi/cdi_vcd.app Binary files differnew file mode 100644 index 0000000..ceb31fc --- /dev/null +++ b/libk3b/projects/videocd/cdi/cdi_vcd.app diff --git a/libk3b/projects/videocd/cdi/cdi_vcd.cfg b/libk3b/projects/videocd/cdi/cdi_vcd.cfg new file mode 100644 index 0000000..4aed0eb --- /dev/null +++ b/libk3b/projects/videocd/cdi/cdi_vcd.cfg @@ -0,0 +1,12 @@ +CONTROLS=ALL +CURCOL=YELLOW +PSDCURCOL=RED +PSDCURSHAPE=ARROW +CENTRTRACK=2 +AUTOPLAY=AUTO_ON +DUALCHAN=DUAL_ON +TIMECODE_X=64 +TIMECODE_Y=100 +LOTID_X=64 +LOTID_Y=64 +ALBUM=STANDARD
\ No newline at end of file diff --git a/libk3b/projects/videocd/cdi/icdia.htm b/libk3b/projects/videocd/cdi/icdia.htm new file mode 100644 index 0000000..cd6c47b --- /dev/null +++ b/libk3b/projects/videocd/cdi/icdia.htm @@ -0,0 +1,12 @@ +<HTML> +<HEAD> +<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> +<META HTTP-EQUIV="Refresh" CONTENT="0; URL=http://www.icdia.org/"> +<TITLE>The New International CD-i Association</TITLE> +</HEAD> +<BODY> + +<A HREF="http://www.icdia.org">The New International CD-i Association - http://www.icdia.org</A> + +</BODY> +</HTML> diff --git a/libk3b/projects/videocd/cdi/vcd_on_cdi_41.pdf b/libk3b/projects/videocd/cdi/vcd_on_cdi_41.pdf Binary files differnew file mode 100644 index 0000000..cdf4fed --- /dev/null +++ b/libk3b/projects/videocd/cdi/vcd_on_cdi_41.pdf diff --git a/libk3b/projects/videocd/extra/Makefile.am b/libk3b/projects/videocd/extra/Makefile.am new file mode 100644 index 0000000..717fa99 --- /dev/null +++ b/libk3b/projects/videocd/extra/Makefile.am @@ -0,0 +1,5 @@ + +extradir = $(kde_datadir)/k3b/extra +extra_DATA = k3bphotovcd.mpg k3bphotosvcd.mpg + +EXTRA_DIST = $(extra_DATA) diff --git a/libk3b/projects/videocd/extra/k3bphotosvcd.mpg b/libk3b/projects/videocd/extra/k3bphotosvcd.mpg Binary files differnew file mode 100644 index 0000000..50156d7 --- /dev/null +++ b/libk3b/projects/videocd/extra/k3bphotosvcd.mpg diff --git a/libk3b/projects/videocd/extra/k3bphotovcd.mpg b/libk3b/projects/videocd/extra/k3bphotovcd.mpg Binary files differnew file mode 100644 index 0000000..2ddb69e --- /dev/null +++ b/libk3b/projects/videocd/extra/k3bphotovcd.mpg diff --git a/libk3b/projects/videocd/k3bvcddoc.cpp b/libk3b/projects/videocd/k3bvcddoc.cpp new file mode 100644 index 0000000..462aea3 --- /dev/null +++ b/libk3b/projects/videocd/k3bvcddoc.cpp @@ -0,0 +1,894 @@ +/* +* +* $Id: k3bvcddoc.cpp 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003-2005 Christian Kvasny <[email protected]> +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> +* +* This program 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 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + +// QT-includes +#include <qstring.h> +#include <qstringlist.h> +#include <qfile.h> +#include <qdatastream.h> +#include <qdom.h> +#include <qdatetime.h> +#include <qtimer.h> +#include <qtextstream.h> + +// KDE-includes +#include <kprocess.h> +#include <kurl.h> +#include <kapplication.h> +#include <kmessagebox.h> +#include <kconfig.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <kio/global.h> +#include <kdebug.h> +#include <kstdguiitem.h> + +// K3b-includes +#include "k3bvcddoc.h" +#include "k3bvcdtrack.h" +#include "k3bvcdjob.h" +#include <k3bglobals.h> +#include <k3bmsf.h> + + +bool desperate_mode = false; +bool preserve_header = false; +bool print_progress = true; +bool aspect_correction = false; +byte forced_sequence_header = 0; + +K3bVcdDoc::K3bVcdDoc( QObject* parent ) + : K3bDoc( parent ) +{ + m_tracks = 0L; + m_vcdOptions = new K3bVcdOptions(); + + m_docType = VCD; + m_vcdType = NONE; + + m_urlAddingTimer = new QTimer( this ); + connect( m_urlAddingTimer, SIGNAL( timeout() ), this, SLOT( slotWorkUrlQueue() ) ); + + // FIXME: remove the newTracks() signal and replace it with the changed signal + connect( this, SIGNAL( newTracks() ), this, SIGNAL( changed() ) ); + connect( this, SIGNAL( trackRemoved( K3bVcdTrack* ) ), this, SIGNAL( changed() ) ); +} + +K3bVcdDoc::~K3bVcdDoc() +{ + if ( m_tracks ) { + m_tracks->setAutoDelete( true ); + delete m_tracks; + } + + delete m_vcdOptions; +} + +bool K3bVcdDoc::newDocument() +{ + if ( m_tracks ) + while ( m_tracks->first() ) + removeTrack( m_tracks->first() ); + else + m_tracks = new QPtrList<K3bVcdTrack>; + m_tracks->setAutoDelete( false ); + + return K3bDoc::newDocument(); +} + + +QString K3bVcdDoc::name() const +{ + return m_vcdOptions->volumeId(); +} + + +KIO::filesize_t K3bVcdDoc::calcTotalSize() const +{ + unsigned long long sum = 0; + if ( m_tracks ) { + for ( K3bVcdTrack * track = m_tracks->first(); track; track = m_tracks->next() ) { + sum += track->size(); + } + } + return sum; +} + +KIO::filesize_t K3bVcdDoc::size() const +{ + // mode2 -> mode1 int(( n+2047 ) / 2048) * 2352 + // mode1 -> mode2 int(( n+2351 ) / 2352) * 2048 + long tracksize = long( ( calcTotalSize() + 2351 ) / 2352 ) * 2048; + return tracksize + ISOsize(); +} + +KIO::filesize_t K3bVcdDoc::ISOsize() const +{ + // 136000b for vcd iso reseved + long long iso_size = 136000; + if ( vcdOptions() ->CdiSupport() ) { + iso_size += vcdOptions() ->CDIsize(); + } + + return iso_size; +} + +K3b::Msf K3bVcdDoc::length() const +{ + return K3b::Msf( size() / 2048 ); +} + + +bool K3bVcdDoc::isImage( const KURL& url ) +{ + QImage p; + return p.load( QFile::encodeName( url.path() ) ); +} + +void K3bVcdDoc::addUrls( const KURL::List& urls ) +{ + // make sure we add them at the end even if urls are in the queue + addTracks( urls, 99 ); +} + +void K3bVcdDoc::addTracks( const KURL::List& urls, uint position ) +{ + KURL::List::ConstIterator end( urls.end() ); + for ( KURL::List::ConstIterator it = urls.begin(); it != end; ++it ) { + urlsToAdd.enqueue( new PrivateUrlToAdd( K3b::convertToLocalUrl(*it), position++ ) ); + } + + m_urlAddingTimer->start( 0 ); +} + +void K3bVcdDoc::slotWorkUrlQueue() +{ + if ( !urlsToAdd.isEmpty() ) { + PrivateUrlToAdd * item = urlsToAdd.dequeue(); + lastAddedPosition = item->position; + + // append at the end by default + if ( lastAddedPosition > m_tracks->count() ) + lastAddedPosition = m_tracks->count(); + + if ( !item->url.isLocalFile() ) { + kdDebug() << item->url.path() << " no local file" << endl; + return ; + } + + if ( !QFile::exists( item->url.path() ) ) { + kdDebug() << "(K3bVcdDoc) file not found: " << item->url.path() << endl; + m_notFoundFiles.append( item->url.path() ); + return ; + } + + if ( K3bVcdTrack * newTrack = createTrack( item->url ) ) + addTrack( newTrack, lastAddedPosition ); + + delete item; + + emit newTracks(); + } else { + m_urlAddingTimer->stop(); + + emit newTracks(); + + // reorder pbc tracks + setPbcTracks(); + + informAboutNotFoundFiles(); + } +} + +K3bVcdTrack* K3bVcdDoc::createTrack( const KURL& url ) +{ + char filename[ 255 ]; + QString error_string = ""; + strcpy( filename, QFile::encodeName( url.path() ) ); + K3bMpegInfo* Mpeg = new K3bMpegInfo( filename ); + + if ( Mpeg ) { + int mpegVersion = Mpeg->version(); + if ( mpegVersion > 0 ) { + + if ( vcdType() == NONE && mpegVersion < 2 ) { + m_urlAddingTimer->stop(); + setVcdType( vcdTypes( mpegVersion ) ); + vcdOptions() ->setMpegVersion( mpegVersion ); + KMessageBox::information( kapp->mainWidget(), + i18n( "K3b will create a %1 image from the given MPEG " + "files, but these files must already be in %2 " + "format. K3b does not yet resample MPEG files." ) + .arg( i18n( "VCD" ) ) + .arg( i18n( "VCD" ) ), + i18n( "Information" ) ); + m_urlAddingTimer->start( 0 ); + } else if ( vcdType() == NONE ) { + m_urlAddingTimer->stop(); + vcdOptions() ->setMpegVersion( mpegVersion ); + bool force = false; + force = ( KMessageBox::questionYesNo( kapp->mainWidget(), + i18n( "K3b will create a %1 image from the given MPEG " + "files, but these files must already be in %2 " + "format. K3b does not yet resample MPEG files." ) + .arg( i18n( "SVCD" ) ) + .arg( i18n( "SVCD" ) ) + + "\n\n" + + i18n( "Note: Forcing MPEG2 as VCD is not supported by " + "some standalone DVD players." ), + i18n( "Information" ), + KStdGuiItem::ok().text(), + i18n( "Forcing VCD" ) ) == KMessageBox::No ); + if ( force ) { + setVcdType( vcdTypes( 1 ) ); + vcdOptions() ->setAutoDetect( false ); + } else + setVcdType( vcdTypes( mpegVersion ) ); + + m_urlAddingTimer->start( 0 ); + } + + + if ( numOfTracks() > 0 && vcdOptions() ->mpegVersion() != mpegVersion ) { + KMessageBox::error( kapp->mainWidget(), "(" + url.path() + ")\n" + + i18n( "You cannot mix MPEG1 and MPEG2 video files.\nPlease start a new Project for this filetype.\nResample not implemented in K3b yet." ), + i18n( "Wrong File Type for This Project" ) ); + + delete Mpeg; + return 0; + } + + K3bVcdTrack* newTrack = new K3bVcdTrack( m_tracks, url.path() ); + *( newTrack->mpeg_info ) = *( Mpeg->mpeg_info ); + + if ( newTrack->isSegment() && !vcdOptions()->PbcEnabled() ) { + KMessageBox::information( kapp->mainWidget(), + i18n( "PBC (Playback control) enabled.\n" + "Videoplayers can not reach Segments (Mpeg Still Pictures) without Playback control ." ) , + i18n( "Information" ) ); + + vcdOptions()->setPbcEnabled( true ); + } + + // set defaults; + newTrack->setPlayTime( vcdOptions() ->PbcPlayTime() ); + newTrack->setWaitTime( vcdOptions() ->PbcWaitTime() ); + newTrack->setPbcNumKeys( vcdOptions() ->PbcNumkeysEnabled() ); + delete Mpeg; + + // debugging output + newTrack->PrintInfo(); + + return newTrack; + } + } else if ( isImage( url ) ) { // image track + // woking on ... + // for future use + // photoalbum starts here + // return here the new photoalbum track + } + + if ( Mpeg ) { + error_string = Mpeg->error_string(); + delete Mpeg; + } + + // error (unsupported files) + KMessageBox::error( kapp->mainWidget(), "(" + url.path() + ")\n" + + i18n( "Only MPEG1 and MPEG2 video files are supported.\n" ) + error_string , + i18n( "Wrong File Format" ) ); + + + return 0; +} + +void K3bVcdDoc::addTrack( const KURL& url, uint position ) +{ + urlsToAdd.enqueue( new PrivateUrlToAdd( url, position ) ); + + m_urlAddingTimer->start( 0 ); +} + + +void K3bVcdDoc::addTrack( K3bVcdTrack* track, uint position ) +{ + if ( m_tracks->count() >= 98 ) { + kdDebug() << "(K3bVcdDoc) VCD Green Book only allows 98 tracks." << endl; + // TODO: show some messagebox + delete track; + return ; + } + + lastAddedPosition = position; + + if ( !m_tracks->insert( position, track ) ) { + lastAddedPosition = m_tracks->count(); + m_tracks->insert( m_tracks->count(), track ); + } + + if ( track->isSegment() ) + vcdOptions() ->increaseSegments( ); + else + vcdOptions() ->increaseSequence( ); + + emit newTracks(); + + setModified( true ); +} + + +void K3bVcdDoc::removeTrack( K3bVcdTrack* track ) +{ + if ( !track ) { + return ; + } + + // set the current item to track + if ( m_tracks->findRef( track ) >= 0 ) { + // take the current item + track = m_tracks->take(); + + // remove all pbc references to us? + if ( track->hasRevRef() ) + track->delRefToUs(); + + // remove all pbc references from us? + track->delRefFromUs(); + + // emit signal before deleting the track to avoid crashes + // when the view tries to call some of the tracks' methods + emit trackRemoved( track ); + + if ( track->isSegment() ) + vcdOptions() ->decreaseSegments( ); + else + vcdOptions() ->decreaseSequence( ); + + delete track; + + if ( numOfTracks() == 0 ) { + setVcdType( NONE ); + vcdOptions() ->setAutoDetect( true ); + } + + // reorder pbc tracks + setPbcTracks(); + } +} + +void K3bVcdDoc::moveTrack( const K3bVcdTrack* track, const K3bVcdTrack* after ) +{ + if ( track == after ) + return ; + + // set the current item to track + m_tracks->findRef( track ); + // take the current item + track = m_tracks->take(); + + // if after == 0 findRef returnes -1 + int pos = m_tracks->findRef( after ); + m_tracks->insert( pos + 1, track ); + + // reorder pbc tracks + setPbcTracks(); + + emit changed(); +} + + +QString K3bVcdDoc::typeString() const +{ + return "vcd"; +} + + +K3bBurnJob* K3bVcdDoc::newBurnJob( K3bJobHandler* hdl, QObject* parent ) +{ + return new K3bVcdJob( this, hdl, parent ); +} + +void K3bVcdDoc::informAboutNotFoundFiles() +{ + if ( !m_notFoundFiles.isEmpty() ) { + KMessageBox::informationList( view(), i18n( "Could not find the following files:" ), + m_notFoundFiles, i18n( "Not Found" ) ); + + m_notFoundFiles.clear(); + } +} + +void K3bVcdDoc::setVcdType( int type ) +{ + m_vcdType = type; + switch ( type ) { + case 0: + //vcd 1.1 + vcdOptions() ->setVcdClass( "vcd" ); + vcdOptions() ->setVcdVersion( "1.1" ); + break; + case 1: + //vcd 2.0 + vcdOptions() ->setVcdClass( "vcd" ); + vcdOptions() ->setVcdVersion( "2.0" ); + break; + case 2: + //svcd 1.0 + vcdOptions() ->setVcdClass( "svcd" ); + vcdOptions() ->setVcdVersion( "1.0" ); + break; + case 3: + //hqvcd 1.0 + vcdOptions() ->setVcdClass( "hqvcd" ); + vcdOptions() ->setVcdVersion( "1.0" ); + break; + + } +} + +void K3bVcdDoc::setPbcTracks() +{ + // reorder pbc tracks + /* + if ( !vcdOptions()->PbcEnabled() ) + return; + */ + + if ( m_tracks ) { + int count = m_tracks->count(); + kdDebug() << QString( "K3bVcdDoc::setPbcTracks() - we have %1 tracks in list." ).arg( count ) << endl; + + QPtrListIterator<K3bVcdTrack> iterTrack( *m_tracks ); + K3bVcdTrack* track; + while ( ( track = iterTrack.current() ) != 0 ) { + ++iterTrack; + for ( int i = 0; i < K3bVcdTrack::_maxPbcTracks; i++ ) { + // do not change userdefined tracks + if ( !track->isPbcUserDefined( i ) ) { + if ( track->getPbcTrack( i ) ) + track->getPbcTrack( i ) ->delFromRevRefList( track ); + + K3bVcdTrack* t = 0L; + int index = track->index(); + + // we are the last track + if ( index == count - 1 ) { + switch ( i ) { + case K3bVcdTrack::PREVIOUS: + // we are not alone :) + if ( count > 1 ) { + t = at( index - 1 ); + t->addToRevRefList( track ); + track->setPbcTrack( i, t ); + } else { + track->setPbcTrack( i ); + track->setPbcNonTrack( i, K3bVcdTrack::VIDEOEND ); + } + break; + case K3bVcdTrack::AFTERTIMEOUT: + case K3bVcdTrack::NEXT: + track->setPbcTrack( i ); + track->setPbcNonTrack( i, K3bVcdTrack::VIDEOEND ); + break; + case K3bVcdTrack::RETURN: + track->setPbcTrack( i ); + track->setPbcNonTrack( i, K3bVcdTrack::VIDEOEND ); + break; + case K3bVcdTrack::DEFAULT: + track->setPbcTrack( i ); + track->setPbcNonTrack( i, K3bVcdTrack::DISABLED ); + break; + } + } + // we are the first track + else if ( index == 0 ) { + switch ( i ) { + case K3bVcdTrack::PREVIOUS: + track->setPbcTrack( i ); + track->setPbcNonTrack( i, K3bVcdTrack::VIDEOEND ); + break; + case K3bVcdTrack::AFTERTIMEOUT: + case K3bVcdTrack::NEXT: + t = at( index + 1 ); + t->addToRevRefList( track ); + track->setPbcTrack( i, t ); + break; + case K3bVcdTrack::RETURN: + track->setPbcTrack( i ); + track->setPbcNonTrack( i, K3bVcdTrack::VIDEOEND ); + break; + case K3bVcdTrack::DEFAULT: + track->setPbcTrack( i ); + track->setPbcNonTrack( i, K3bVcdTrack::DISABLED ); + break; + } + } + // we are one of the other tracks and have PREVIOUS and NEXT Track + else { + switch ( i ) { + case K3bVcdTrack::PREVIOUS: + t = at( index - 1 ); + t->addToRevRefList( track ); + track->setPbcTrack( i, t ); + break; + case K3bVcdTrack::AFTERTIMEOUT: + case K3bVcdTrack::NEXT: + t = at( index + 1 ); + t->addToRevRefList( track ); + track->setPbcTrack( i, t ); + break; + case K3bVcdTrack::RETURN: + track->setPbcTrack( i ); + track->setPbcNonTrack( i, K3bVcdTrack::VIDEOEND ); + break; + case K3bVcdTrack::DEFAULT: + track->setPbcTrack( i ); + track->setPbcNonTrack( i, K3bVcdTrack::DISABLED ); + break; + } + } + } + } + } + } +} + + +bool K3bVcdDoc::loadDocumentData( QDomElement* root ) +{ + newDocument(); + + QDomNodeList nodes = root->childNodes(); + + if ( nodes.length() < 3 ) + return false; + + if ( nodes.item( 0 ).nodeName() != "general" ) + return false; + if ( !readGeneralDocumentData( nodes.item( 0 ).toElement() ) ) + return false; + + if ( nodes.item( 1 ).nodeName() != "vcd" ) + return false; + + if ( nodes.item( 2 ).nodeName() != "contents" ) + return false; + + + // vcd Label + QDomNodeList vcdNodes = nodes.item( 1 ).childNodes(); + + for ( uint i = 0; i < vcdNodes.count(); i++ ) { + QDomNode item = vcdNodes.item( i ); + QString name = item.nodeName(); + + kdDebug() << QString( "(K3bVcdDoc::loadDocumentData) nodeName = '%1'" ).arg( name ) << endl; + + if ( name == "volumeId" ) + vcdOptions() ->setVolumeId( item.toElement().text() ); + else if ( name == "albumId" ) + vcdOptions() ->setAlbumId( item.toElement().text() ); + else if ( name == "volumeSetId" ) + vcdOptions() ->setVolumeSetId( item.toElement().text() ); + else if ( name == "preparer" ) + vcdOptions() ->setPreparer( item.toElement().text() ); + else if ( name == "publisher" ) + vcdOptions() ->setPublisher( item.toElement().text() ); + else if ( name == "vcdType" ) + setVcdType( vcdTypes( item.toElement().text().toInt() ) ); + else if ( name == "mpegVersion" ) + vcdOptions() ->setMpegVersion( item.toElement().text().toInt() ); + else if ( name == "PreGapLeadout" ) + vcdOptions() ->setPreGapLeadout( item.toElement().text().toInt() ); + else if ( name == "PreGapTrack" ) + vcdOptions() ->setPreGapTrack( item.toElement().text().toInt() ); + else if ( name == "FrontMarginTrack" ) + vcdOptions() ->setFrontMarginTrack( item.toElement().text().toInt() ); + else if ( name == "RearMarginTrack" ) + vcdOptions() ->setRearMarginTrack( item.toElement().text().toInt() ); + else if ( name == "FrontMarginTrackSVCD" ) + vcdOptions() ->setFrontMarginTrackSVCD( item.toElement().text().toInt() ); + else if ( name == "RearMarginTrackSVCD" ) + vcdOptions() ->setRearMarginTrackSVCD( item.toElement().text().toInt() ); + else if ( name == "volumeCount" ) + vcdOptions() ->setVolumeCount( item.toElement().text().toInt() ); + else if ( name == "volumeNumber" ) + vcdOptions() ->setVolumeNumber( item.toElement().text().toInt() ); + else if ( name == "AutoDetect" ) + vcdOptions() ->setAutoDetect( item.toElement().text().toInt() ); + else if ( name == "CdiSupport" ) + vcdOptions() ->setCdiSupport( item.toElement().text().toInt() ); + else if ( name == "NonCompliantMode" ) + vcdOptions() ->setNonCompliantMode( item.toElement().text().toInt() ); + else if ( name == "Sector2336" ) + vcdOptions() ->setSector2336( item.toElement().text().toInt() ); + else if ( name == "UpdateScanOffsets" ) + vcdOptions() ->setUpdateScanOffsets( item.toElement().text().toInt() ); + else if ( name == "RelaxedAps" ) + vcdOptions() ->setRelaxedAps( item.toElement().text().toInt() ); + else if ( name == "UseGaps" ) + vcdOptions() ->setUseGaps( item.toElement().text().toInt() ); + else if ( name == "PbcEnabled" ) + vcdOptions() ->setPbcEnabled( item.toElement().text().toInt() ); + else if ( name == "SegmentFolder" ) + vcdOptions() ->setSegmentFolder( item.toElement().text().toInt() ); + else if ( name == "Restriction" ) + vcdOptions() ->setRestriction( item.toElement().text().toInt() ); + } + + // vcd Tracks + QDomNodeList trackNodes = nodes.item( 2 ).childNodes(); + + for ( uint i = 0; i < trackNodes.length(); i++ ) { + + // check if url is available + QDomElement trackElem = trackNodes.item( i ).toElement(); + QString url = trackElem.attributeNode( "url" ).value(); + if ( !QFile::exists( url ) ) + m_notFoundFiles.append( url ); + else { + KURL k; + k.setPath( url ); + if ( K3bVcdTrack * track = createTrack( k ) ) { + track ->setPlayTime( trackElem.attribute( "playtime", "1" ).toInt() ); + track ->setWaitTime( trackElem.attribute( "waittime", "2" ).toInt() ); + track ->setReactivity( trackElem.attribute( "reactivity", "0" ).toInt() ); + track -> setPbcNumKeys( ( trackElem.attribute( "numkeys", "yes" ).contains( "yes" ) ) ? true : false ); + track -> setPbcNumKeysUserdefined( ( trackElem.attribute( "userdefinednumkeys", "no" ).contains( "yes" ) ) ? true : false ); + + addTrack( track, m_tracks->count() ); + } + } + } + + emit newTracks(); + + // do not add saved pbcTrack links when one ore more files missing. + // TODO: add info message to informAboutNotFoundFiles(); + if ( m_notFoundFiles.isEmpty() ) { + int type; + int val; + bool pbctrack; + for ( uint trackId = 0; trackId < trackNodes.length(); trackId++ ) { + QDomElement trackElem = trackNodes.item( trackId ).toElement(); + QDomNodeList trackNodes = trackElem.childNodes(); + for ( uint i = 0; i < trackNodes.length(); i++ ) { + QDomElement trackElem = trackNodes.item( i ).toElement(); + QString name = trackElem.tagName(); + if ( name.contains( "pbc" ) ) { + if ( trackElem.hasAttribute ( "type" ) ) { + type = trackElem.attribute ( "type" ).toInt(); + if ( trackElem.hasAttribute ( "pbctrack" ) ) { + pbctrack = ( trackElem.attribute ( "pbctrack" ) == "yes" ); + if ( trackElem.hasAttribute ( "val" ) ) { + val = trackElem.attribute ( "val" ).toInt(); + K3bVcdTrack* track = m_tracks->at( trackId ); + K3bVcdTrack* pbcTrack = m_tracks->at( val ); + if ( pbctrack ) { + pbcTrack->addToRevRefList( track ); + track->setPbcTrack( type, pbcTrack ); + track->setUserDefined( type, true ); + } else { + track->setPbcTrack( type ); + track->setPbcNonTrack( type, val ); + track->setUserDefined( type, true ); + } + } + } + } + } else if ( name.contains( "numkeys" ) ) { + if ( trackElem.hasAttribute ( "key" ) ) { + int key = trackElem.attribute ( "key" ).toInt(); + if ( trackElem.hasAttribute ( "val" ) ) { + int val = trackElem.attribute ( "val" ).toInt() - 1; + K3bVcdTrack* track = m_tracks->at( trackId ); + if ( val >= 0 ) { + K3bVcdTrack * numkeyTrack = m_tracks->at( val ); + track->setDefinedNumKey( key, numkeyTrack ); + } else { + track->setDefinedNumKey( key, 0L ); + } + } + } + } + + } + + } + setPbcTracks(); + setModified( false ); + } + + informAboutNotFoundFiles(); + return true; +} + + + +bool K3bVcdDoc::saveDocumentData( QDomElement * docElem ) +{ + QDomDocument doc = docElem->ownerDocument(); + saveGeneralDocumentData( docElem ); + + // save Vcd Label + QDomElement vcdMain = doc.createElement( "vcd" ); + + QDomElement vcdElem = doc.createElement( "volumeId" ); + vcdElem.appendChild( doc.createTextNode( vcdOptions() ->volumeId() ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "albumId" ); + vcdElem.appendChild( doc.createTextNode( vcdOptions() ->albumId() ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "volumeSetId" ); + vcdElem.appendChild( doc.createTextNode( vcdOptions() ->volumeSetId() ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "preparer" ); + vcdElem.appendChild( doc.createTextNode( vcdOptions() ->preparer() ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "publisher" ); + vcdElem.appendChild( doc.createTextNode( vcdOptions() ->publisher() ) ); + vcdMain.appendChild( vcdElem ); + + // applicationId() + // systemId() + + vcdElem = doc.createElement( "vcdType" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdType() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "mpegVersion" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->mpegVersion() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "PreGapLeadout" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->PreGapLeadout() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "PreGapTrack" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->PreGapTrack() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "FrontMarginTrack" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->FrontMarginTrack() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "RearMarginTrack" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->RearMarginTrack() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "FrontMarginTrackSVCD" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->FrontMarginTrackSVCD() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "RearMarginTrackSVCD" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->RearMarginTrackSVCD() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "volumeCount" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->volumeCount() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "volumeNumber" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->volumeNumber() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "AutoDetect" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->AutoDetect() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "CdiSupport" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->CdiSupport() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "NonCompliantMode" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->NonCompliantMode() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "Sector2336" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->Sector2336() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "UpdateScanOffsets" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->UpdateScanOffsets() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "RelaxedAps" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->RelaxedAps() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "UseGaps" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->UseGaps() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "PbcEnabled" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->PbcEnabled() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "SegmentFolder" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->SegmentFolder() ) ) ); + vcdMain.appendChild( vcdElem ); + + vcdElem = doc.createElement( "Restriction" ); + vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->Restriction() ) ) ); + vcdMain.appendChild( vcdElem ); + + docElem->appendChild( vcdMain ); + + // save the tracks + // ------------------------------------------------------------- + QDomElement contentsElem = doc.createElement( "contents" ); + + QPtrListIterator<K3bVcdTrack> iterTrack( *m_tracks ); + K3bVcdTrack* track; + + while ( ( track = iterTrack.current() ) != 0 ) { + ++iterTrack; + + QDomElement trackElem = doc.createElement( "track" ); + trackElem.setAttribute( "url", KIO::decodeFileName( track->absPath() ) ); + trackElem.setAttribute( "playtime", track->getPlayTime() ); + trackElem.setAttribute( "waittime", track->getWaitTime() ); + trackElem.setAttribute( "reactivity", track->Reactivity() ); + trackElem.setAttribute( "numkeys", ( track->PbcNumKeys() ) ? "yes" : "no" ); + trackElem.setAttribute( "userdefinednumkeys", ( track->PbcNumKeysUserdefined() ) ? "yes" : "no" ); + + for ( int i = 0; + i < K3bVcdTrack::_maxPbcTracks; + i++ ) { + if ( track->isPbcUserDefined( i ) ) { + // save pbcTracks + QDomElement pbcElem = doc.createElement( "pbc" ); + pbcElem.setAttribute( "type", i ); + if ( track->getPbcTrack( i ) ) { + pbcElem.setAttribute( "pbctrack", "yes" ); + pbcElem.setAttribute( "val", track->getPbcTrack( i ) ->index() ); + } else { + pbcElem.setAttribute( "pbctrack", "no" ); + pbcElem.setAttribute( "val", track->getNonPbcTrack( i ) ); + } + trackElem.appendChild( pbcElem ); + } + } + QMap<int, K3bVcdTrack*> numKeyMap = track->DefinedNumKey(); + QMap<int, K3bVcdTrack*>::const_iterator trackIt; + + for ( trackIt = numKeyMap.begin(); + trackIt != numKeyMap.end(); + ++trackIt ) { + QDomElement numElem = doc.createElement( "numkeys" ); + if ( trackIt.data() ) { + numElem.setAttribute( "key", trackIt.key() ); + numElem.setAttribute( "val", trackIt.data() ->index() + 1 ); + } else { + numElem.setAttribute( "key", trackIt.key() ); + numElem.setAttribute( "val", 0 ); + } + trackElem.appendChild( numElem ); + } + + contentsElem.appendChild( trackElem ); + } + // ------------------------------------------------------------- + + docElem->appendChild( contentsElem ); + + return true; +} + +#include "k3bvcddoc.moc" diff --git a/libk3b/projects/videocd/k3bvcddoc.h b/libk3b/projects/videocd/k3bvcddoc.h new file mode 100644 index 0000000..8b10837 --- /dev/null +++ b/libk3b/projects/videocd/k3bvcddoc.h @@ -0,0 +1,192 @@ +/* +* +* $Id: k3bvcddoc.h 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003-2004 Christian Kvasny <[email protected]> +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> +* +* This program 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 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + +#ifndef K3BVCDDOC_H +#define K3BVCDDOC_H + +// Qt Includes +#include <qptrqueue.h> +#include <qfile.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qdatetime.h> +#include <qtextstream.h> +#include <qimage.h> + +// Kde Includes +#include <kurl.h> + +// K3b Includes +#include "k3bvcdoptions.h" +#include "mpeginfo/k3bmpeginfo.h" +#include <k3bdoc.h> +#include "k3b_export.h" +class K3bApp; +class K3bVcdTrack; +class K3bVcdJob; +//class K3bView; +class QWidget; +class QTimer; +class QDomDocument; +class QDomElement; +class KConfig; + + + +class LIBK3B_EXPORT K3bVcdDoc : public K3bDoc +{ + Q_OBJECT + + public: + K3bVcdDoc( QObject* ); + ~K3bVcdDoc(); + + int type() const { return VCD; } + + QString name() const; + + enum vcdTypes { VCD11, VCD20, SVCD10, HQVCD, NONE}; + + bool newDocument(); + int numOfTracks() const + { + return m_tracks->count(); + } + + const QString& vcdImage() const + { + return m_vcdImage; + } + void setVcdImage( const QString& s ) + { + m_vcdImage = s; + } + + K3bVcdTrack* first() + { + return m_tracks->first(); + } + K3bVcdTrack* current() const + { + return m_tracks->current(); + } + K3bVcdTrack* next() + { + return m_tracks->next(); + } + K3bVcdTrack* prev() + { + return m_tracks->prev(); + } + K3bVcdTrack* at( uint i ) + { + return m_tracks->at( i ); + } + K3bVcdTrack* take( uint i ) + { + return m_tracks->take( i ); + } + + const QPtrList<K3bVcdTrack>* tracks() const + { + return m_tracks; + } + + /** get the current size of the project */ + KIO::filesize_t size() const; + K3b::Msf length() const; + + K3bBurnJob* newBurnJob( K3bJobHandler* hdl, QObject* parent ); + K3bVcdOptions* vcdOptions() const + { + return m_vcdOptions; + } + + int vcdType() const + { + return m_vcdType; + } + void setVcdType( int type ); + void setPbcTracks(); + + public slots: + /** + * will test the file and add it to the project. + * connect to at least result() to know when + * the process is finished and check error() + * to know about the result. + **/ + void addUrls( const KURL::List& ); + void addTrack( const KURL&, uint ); + void addTracks( const KURL::List&, uint ); + /** adds a track without any testing */ + void addTrack( K3bVcdTrack* track, uint position = 0 ); + + // --- TODO: this should read: removeTrack( K3bVcdTrack* ) + void removeTrack( K3bVcdTrack* ); + void moveTrack( const K3bVcdTrack* track, const K3bVcdTrack* after ); + + protected slots: + /** processes queue "urlsToAdd" **/ + void slotWorkUrlQueue(); + + signals: + void newTracks(); + + void trackRemoved( K3bVcdTrack* ); + + protected: + /** reimplemented from K3bDoc */ + bool loadDocumentData( QDomElement* root ); + /** reimplemented from K3bDoc */ + bool saveDocumentData( QDomElement* ); + + QString typeString() const; + + private: + K3bVcdTrack* createTrack( const KURL& url ); + void informAboutNotFoundFiles(); + + QStringList m_notFoundFiles; + QString m_vcdImage; + + class PrivateUrlToAdd + { + public: + PrivateUrlToAdd( const KURL& u, int _pos ) + : url( u ), position( _pos ) + {} + KURL url; + int position; + }; + + /** Holds all the urls that have to be added to the list of tracks. **/ + QPtrQueue<PrivateUrlToAdd> urlsToAdd; + QTimer* m_urlAddingTimer; + + QPtrList<K3bVcdTrack>* m_tracks; + KIO::filesize_t calcTotalSize() const; + KIO::filesize_t ISOsize() const; + + bool isImage( const KURL& url ); + + K3bVcdTrack* m_lastAddedTrack; + K3bVcdOptions* m_vcdOptions; + + int m_vcdType; + uint lastAddedPosition; +}; + +#endif diff --git a/libk3b/projects/videocd/k3bvcdjob.cpp b/libk3b/projects/videocd/k3bvcdjob.cpp new file mode 100644 index 0000000..a1b347a --- /dev/null +++ b/libk3b/projects/videocd/k3bvcdjob.cpp @@ -0,0 +1,567 @@ +/* +* +* $Id: k3bvcdjob.cpp 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003-2004 Christian Kvasny <[email protected]> +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> +* +* This program 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 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + +#include <klocale.h> +#include <kconfig.h> +#include <kstandarddirs.h> +#include <kurl.h> +#include <ktempfile.h> +#include <kio/global.h> + +#include <qstring.h> +#include <qdatetime.h> +#include <qfile.h> +#include <qtimer.h> +#include <kdebug.h> +#include <qregexp.h> +#include <qdom.h> + +#include "k3bvcdjob.h" + +// K3b Includes +#include "k3bvcddoc.h" +#include "k3bvcdtrack.h" +#include "k3bvcdxmlview.h" +#include <k3bcore.h> +#include <k3bdoc.h> +#include <k3bprocess.h> +#include <k3bdevice.h> +#include <k3bexternalbinmanager.h> +#include <k3bglobals.h> +#include <k3bcdrecordwriter.h> +#include <k3bcdrdaowriter.h> + +K3bVcdJob::K3bVcdJob( K3bVcdDoc* doc, K3bJobHandler* jh, QObject* parent, const char* name ) + : K3bBurnJob( jh, parent, name ) +{ + m_doc = doc; + m_doc->setCopies( m_doc->dummy() || m_doc->onlyCreateImages() ? 1 : m_doc->copies() ); + m_process = 0; + m_currentWrittenTrackNumber = 0; + m_bytesFinishedTracks = 0; + m_writerJob = 0; + // m_createimageonlypercent = 33.3; + m_createimageonlypercent = 100 / ( m_doc->copies() + 2 ); + m_currentcopy = 1; + m_imageFinished = false; +} + + +K3bVcdJob::~K3bVcdJob() +{ + delete m_process; + + if ( m_writerJob ) + delete m_writerJob; +} + + +K3bDoc* K3bVcdJob::doc() const +{ + return m_doc; +} + + +K3bDevice::Device* K3bVcdJob::writer() const +{ + if( doc()->onlyCreateImages() ) + return 0; + else + return doc() ->burner(); +} + +void K3bVcdJob::cancel() +{ + cancelAll(); + + emit canceled(); + jobFinished( false ); +} + + +void K3bVcdJob::cancelAll() +{ + m_canceled = true; + + if ( m_writerJob ) + m_writerJob->cancel(); + + if ( m_process->isRunning() ) { + m_process->disconnect( this ); + m_process->kill(); + } + + // remove bin-file if it is unfinished or the user selected to remove image + if ( QFile::exists( m_doc->vcdImage() ) ) { + if ( !m_doc->onTheFly() && m_doc->removeImages() || !m_imageFinished ) { + emit infoMessage( i18n( "Removing Binary file %1" ).arg( m_doc->vcdImage() ), K3bJob::SUCCESS ); + QFile::remove + ( m_doc->vcdImage() ); + m_doc->setVcdImage( "" ); + } + } + + // remove cue-file if it is unfinished or the user selected to remove image + if ( QFile::exists( m_cueFile ) ) { + if ( !m_doc->onTheFly() && m_doc->removeImages() || !m_imageFinished ) { + emit infoMessage( i18n( "Removing Cue file %1" ).arg( m_cueFile ), K3bJob::SUCCESS ); + QFile::remove + ( m_cueFile ); + m_cueFile = ""; + } + } +} + + +void K3bVcdJob::start() +{ + kdDebug() << "(K3bVcdJob) starting job" << endl; + + jobStarted(); + emit burning( false ); + m_canceled = false; + + int pos = QString( m_doc->vcdImage() ).find( ".bin", QString( m_doc->vcdImage() ).length() - 4 ); + if ( pos > 0 ) { + m_cueFile = m_doc->vcdImage().left( pos ) + ".cue"; + } else { + m_cueFile = m_doc->vcdImage() + ".cue"; + m_doc->setVcdImage( m_doc->vcdImage() + ".bin" ); + } + + if ( vcdDoc() ->onlyCreateImages() ) + m_createimageonlypercent = 50.0; + + // vcdxGen(); + xmlGen(); +} + +void K3bVcdJob::xmlGen() +{ + + KTempFile tempF; + m_xmlFile = tempF.name(); + tempF.unlink(); + + K3bVcdXmlView xmlView( m_doc ); + + if ( !xmlView.write( m_xmlFile ) ) { + kdDebug() << "(K3bVcdJob) could not write xmlfile." << endl; + emit infoMessage( i18n( "Could not write correct XML-file." ), K3bJob::ERROR ); + cancelAll(); + jobFinished( false ); + } + + // emit infoMessage( i18n( "XML-file successfully created" ), K3bJob::SUCCESS ); + emit debuggingOutput( "K3bVcdXml:", xmlView.xmlString() ); + + vcdxBuild(); + +} + +void K3bVcdJob::vcdxBuild() +{ + emit newTask( i18n( "Creating image files" ) ); + + m_stage = stageUnknown; + firstTrack = true; + delete m_process; + m_process = new K3bProcess(); + + emit infoMessage( i18n( "Creating Cue/Bin files ..." ), K3bJob::INFO ); + const K3bExternalBin* bin = k3bcore ->externalBinManager() ->binObject( "vcdxbuild" ); + if ( !bin ) { + kdDebug() << "(K3bVcdJob) could not find vcdxbuild executable" << endl; + emit infoMessage( i18n( "Could not find %1 executable." ).arg( "vcdxbuild" ), K3bJob::ERROR ); + emit infoMessage( i18n( "To create VideoCDs you must install VcdImager Version %1." ).arg( ">= 0.7.12" ), K3bJob::INFO ); + emit infoMessage( i18n( "You can find this on your distribution disks or download it from http://www.vcdimager.org" ), K3bJob::INFO ); + cancelAll(); + jobFinished( false ); + return ; + } + + if ( bin->version < K3bVersion( "0.7.12" ) ) { + kdDebug() << "(K3bVcdJob) vcdxbuild executable too old!" << endl; + emit infoMessage( i18n( "%1 executable too old: need version %2 or greater." ).arg( "Vcdxbuild" ).arg( "0.7.12" ), K3bJob::ERROR ); + emit infoMessage( i18n( "You can find this on your distribution disks or download it from http://www.vcdimager.org" ), K3bJob::INFO ); + cancelAll(); + jobFinished( false ); + return ; + } + + if ( !bin->copyright.isEmpty() ) + emit infoMessage( i18n( "Using %1 %2 - Copyright (C) %3" ).arg( bin->name() ).arg( bin->version ).arg( bin->copyright ), INFO ); + + *m_process << bin; + + // additional user parameters from config + const QStringList& params = k3bcore->externalBinManager() ->program( "vcdxbuild" ) ->userParameters(); + for ( QStringList::const_iterator it = params.begin(); it != params.end(); ++it ) + *m_process << *it; + + + if ( vcdDoc() ->vcdOptions() ->Sector2336() ) { + kdDebug() << "(K3bVcdJob) Write 2336 Sectors = on" << endl; + *m_process << "--sector-2336"; + } + + *m_process << "--progress" << "--gui"; + + *m_process << QString( "--cue-file=%1" ).arg( m_cueFile ); + + *m_process << QString( "--bin-file=%1" ).arg( m_doc->vcdImage() ); + + *m_process << QString( "%1" ).arg( QFile::encodeName( m_xmlFile ) ); + + connect( m_process, SIGNAL( receivedStderr( KProcess*, char*, int ) ), + this, SLOT( slotParseVcdxBuildOutput( KProcess*, char*, int ) ) ); + connect( m_process, SIGNAL( receivedStdout( KProcess*, char*, int ) ), + this, SLOT( slotParseVcdxBuildOutput( KProcess*, char*, int ) ) ); + connect( m_process, SIGNAL( processExited( KProcess* ) ), + this, SLOT( slotVcdxBuildFinished() ) ); + + // vcdxbuild commandline parameters + kdDebug() << "***** vcdxbuild parameters:" << endl; + ; + const QValueList<QCString>& args = m_process->args(); + QString s; + for ( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) { + s += *it + " "; + } + kdDebug() << s << flush << endl; + emit debuggingOutput( "vcdxbuild command:", s ); + + if ( !m_process->start( KProcess::NotifyOnExit, KProcess::AllOutput ) ) { + kdDebug() << "(K3bVcdJob) could not start vcdxbuild" << endl; + emit infoMessage( i18n( "Could not start %1." ).arg( "vcdxbuild" ), K3bJob::ERROR ); + cancelAll(); + jobFinished( false ); + } +} + +void K3bVcdJob::slotParseVcdxBuildOutput( KProcess*, char* output, int len ) +{ + QString buffer = QString::fromLocal8Bit( output, len ); + + // split to lines + QStringList lines = QStringList::split( "\n", buffer ); + + QDomDocument xml_doc; + QDomElement xml_root; + + // do every line + for ( QStringList::Iterator str = lines.begin(); str != lines.end(); ++str ) { + *str = ( *str ).stripWhiteSpace(); + + emit debuggingOutput( "vcdxbuild", *str ); + + xml_doc.setContent( QString( "<?xml version='1.0'?><vcdxbuild>" ) + *str + "</vcdxbuild>" ); + + xml_root = xml_doc.documentElement(); + + // There should be only one... but ... + for ( QDomNode node = xml_root.firstChild(); !node.isNull(); node = node.nextSibling() ) { + QDomElement el = node.toElement(); + if ( el.isNull() ) + continue; + + const QString tagName = el.tagName().lower(); + + if ( tagName == "progress" ) { + const QString oper = el.attribute( "operation" ).lower(); + const unsigned long long pos = el.attribute( "position" ).toLong(); + const long long size = el.attribute( "size" ).toLong(); + + if ( oper == "scan" ) { + // Scan Video Files + if ( m_stage == stageUnknown || pos < m_bytesFinished ) { + const uint index = el.attribute( "id" ).replace( QRegExp( "sequence-" ), "" ).toUInt(); + + m_currentWrittenTrack = m_doc->at( m_currentWrittenTrackNumber ); + emit newSubTask( i18n( "Scanning video file %1 of %2 (%3)" ).arg( index + 1 ).arg( doc() ->numOfTracks() ).arg( m_currentWrittenTrack->fileName() ) ); + m_bytesFinished = 0; + + if ( !firstTrack ) { + m_bytesFinishedTracks += m_doc->at( m_currentWrittenTrackNumber ) ->size(); + m_currentWrittenTrackNumber++; + } else + firstTrack = false; + } + emit subPercent( ( int ) ( 100.0 * ( double ) pos / ( double ) size ) ); + emit processedSubSize( pos / 1024 / 1024, size / 1024 / 1024 ); + + // this is the first of three processes. + double relOverallWritten = ( ( double ) m_bytesFinishedTracks + ( double ) pos ) / ( double ) doc() ->size(); + emit percent( ( int ) ( m_createimageonlypercent * relOverallWritten ) ); + + m_bytesFinished = pos; + m_stage = stageScan; + + } else if ( oper == "write" ) { + emit subPercent( ( int ) ( 100.0 * ( double ) pos / ( double ) size ) ); + emit processedSubSize( ( pos * 2048 ) / 1024 / 1024, ( size * 2048 ) / 1024 / 1024 ); + emit percent( ( int ) ( m_createimageonlypercent + ( m_createimageonlypercent * ( double ) pos / ( double ) size ) ) ); + + m_stage = stageWrite; + } else { + return ; + } + } else if ( tagName == "log" ) { + QDomText tel = el.firstChild().toText(); + const QString level = el.attribute( "level" ).lower(); + if ( tel.isText() ) { + const QString text = tel.data(); + if ( m_stage == stageWrite && level == "information" ) + kdDebug() << QString( "(K3bVcdJob) VcdxBuild information, %1" ).arg( text ) << endl; + if ( ( text ).startsWith( "writing track" ) ) + emit newSubTask( i18n( "Creating Image for track %1" ).arg( ( text ).mid( 14 ) ) ); + else { + if ( level != "error" ) { + kdDebug() << QString( "(K3bVcdJob) vcdxbuild warning, %1" ).arg( text ) << endl; + parseInformation( text ); + } else { + kdDebug() << QString( "(K3bVcdJob) vcdxbuild error, %1" ).arg( text ) << endl; + emit infoMessage( text, K3bJob::ERROR ); + } + } + } + } + } + } +} + + +void K3bVcdJob::slotVcdxBuildFinished() +{ + if ( m_process->normalExit() ) { + // TODO: check the process' exitStatus() + switch ( m_process->exitStatus() ) { + case 0: + emit infoMessage( i18n( "Cue/Bin files successfully created." ), K3bJob::SUCCESS ); + m_imageFinished = true; + break; + default: + emit infoMessage( i18n( "%1 returned an unknown error (code %2)." ).arg( "vcdxbuild" ).arg( m_process->exitStatus() ), + K3bJob::ERROR ); + emit infoMessage( i18n( "Please send me an email with the last output." ), K3bJob::ERROR ); + cancelAll(); + jobFinished( false ); + return ; + } + } else { + emit infoMessage( i18n( "%1 did not exit cleanly." ).arg( "Vcdxbuild" ), K3bJob::ERROR ); + cancelAll(); + jobFinished( false ); + return ; + } + + //remove xml-file + if ( QFile::exists( m_xmlFile ) ) + QFile::remove + ( m_xmlFile ); + + kdDebug() << QString( "(K3bVcdJob) create only image: %1" ).arg( vcdDoc() ->onlyCreateImages() ) << endl; + if ( !vcdDoc() ->onlyCreateImages() ) + startWriterjob(); + else + jobFinished( true ); +} + +void K3bVcdJob::startWriterjob() +{ + kdDebug() << QString( "(K3bVcdJob) writing copy %1 of %2" ).arg( m_currentcopy ).arg( m_doc->copies() ) << endl; + if ( prepareWriterJob() ) { + if ( waitForMedia( m_doc->burner() ) < 0 ) { + cancel(); + return ; + } + // just to be sure we did not get canceled during the async discWaiting + if ( m_canceled ) + return ; + + if ( m_doc->copies() > 1 ) + emit newTask( i18n( "Writing Copy %1 of %2" ).arg( m_currentcopy ).arg( m_doc->copies() ) ); + + emit burning( true ); + m_writerJob->start(); + } +} + +bool K3bVcdJob::prepareWriterJob() +{ + if ( m_writerJob ) + delete m_writerJob; + + const K3bExternalBin* cdrecordBin = k3bcore->externalBinManager() ->binObject( "cdrecord" ); + if ( writingApp() == K3b::DEFAULT && cdrecordBin->hasFeature( "cuefile" ) && m_doc->burner() ->dao() ) + setWritingApp( K3b::CDRECORD ); + + if ( writingApp() == K3b::CDRDAO || writingApp() == K3b::DEFAULT ) { + K3bCdrdaoWriter * writer = new K3bCdrdaoWriter( m_doc->burner(), this, this ); + // create cdrdao job + writer->setCommand( K3bCdrdaoWriter::WRITE ); + writer->setSimulate( m_doc->dummy() ); + writer->setBurnSpeed( m_doc->speed() ); + + writer->setTocFile( m_cueFile ); + + m_writerJob = writer; + + } else if ( writingApp() == K3b::CDRECORD ) { + K3bCdrecordWriter * writer = new K3bCdrecordWriter( m_doc->burner(), this, this ); + // create cdrecord job + + writer->setSimulate( m_doc->dummy() ); + writer->setBurnSpeed( m_doc->speed() ); + writer->setDao( true ); + writer->setCueFile( m_cueFile ); + + m_writerJob = writer; + + } + + connect( m_writerJob, SIGNAL( infoMessage( const QString&, int ) ), this, SIGNAL( infoMessage( const QString&, int ) ) ); + connect( m_writerJob, SIGNAL( percent( int ) ), this, SLOT( slotWriterJobPercent( int ) ) ); + connect( m_writerJob, SIGNAL( processedSize( int, int ) ), this, SLOT( slotProcessedSize( int, int ) ) ); + connect( m_writerJob, SIGNAL( subPercent( int ) ), this, SIGNAL( subPercent( int ) ) ); + connect( m_writerJob, SIGNAL( processedSubSize( int, int ) ), this, SIGNAL( processedSubSize( int, int ) ) ); + connect( m_writerJob, SIGNAL( nextTrack( int, int ) ), this, SLOT( slotWriterNextTrack( int, int ) ) ); + connect( m_writerJob, SIGNAL( buffer( int ) ), this, SIGNAL( bufferStatus( int ) ) ); + connect( m_writerJob, SIGNAL( deviceBuffer( int ) ), this, SIGNAL( deviceBuffer( int ) ) ); + connect( m_writerJob, SIGNAL( writeSpeed( int, int ) ), this, SIGNAL( writeSpeed( int, int ) ) ); + connect( m_writerJob, SIGNAL( finished( bool ) ), this, SLOT( slotWriterJobFinished( bool ) ) ); + connect( m_writerJob, SIGNAL( newTask( const QString& ) ), this, SIGNAL( newTask( const QString& ) ) ); + connect( m_writerJob, SIGNAL( newSubTask( const QString& ) ), this, SIGNAL( newSubTask( const QString& ) ) ); + connect( m_writerJob, SIGNAL( debuggingOutput( const QString&, const QString& ) ), this, SIGNAL( debuggingOutput( const QString&, const QString& ) ) ); + + return true; +} + +void K3bVcdJob::slotWriterJobPercent( int p ) +{ + emit percent( ( int ) ( ( m_createimageonlypercent * ( m_currentcopy + 1 ) ) + p / ( m_doc->copies() + 2 ) ) ); +} + +void K3bVcdJob::slotProcessedSize( int cs, int ts ) +{ + emit processedSize( cs + ( ts * ( m_currentcopy - 1 ) ) , ts * m_doc->copies() ); +} + +void K3bVcdJob::slotWriterNextTrack( int t, int tt ) +{ + emit newSubTask( i18n( "Writing Track %1 of %2" ).arg( t ).arg( tt ) ); +} + +void K3bVcdJob::slotWriterJobFinished( bool success ) +{ + if ( m_canceled ) + return ; + + if ( m_currentcopy >= m_doc->copies() ) { + // remove bin-file if it is unfinished or the user selected to remove image + if ( QFile::exists( m_doc->vcdImage() ) ) { + if ( !m_doc->onTheFly() && m_doc->removeImages() || !m_imageFinished ) { + emit infoMessage( i18n( "Removing Binary file %1" ).arg( m_doc->vcdImage() ), K3bJob::SUCCESS ); + QFile::remove + ( m_doc->vcdImage() ); + m_doc->setVcdImage( "" ); + } + } + + // remove cue-file if it is unfinished or the user selected to remove image + if ( QFile::exists( m_cueFile ) ) { + if ( !m_doc->onTheFly() && m_doc->removeImages() || !m_imageFinished ) { + emit infoMessage( i18n( "Removing Cue file %1" ).arg( m_cueFile ), K3bJob::SUCCESS ); + QFile::remove + ( m_cueFile ); + m_cueFile = ""; + } + } + } + + if ( success ) { + // allright + // the writerJob should have emited the "simulation/writing successful" signal + if ( m_currentcopy >= m_doc->copies() ) { + jobFinished( true ); + } else { + m_currentcopy++; + startWriterjob(); + } + } else { + cancelAll(); + jobFinished( false ); + } +} + +void K3bVcdJob::parseInformation( const QString &text ) +{ + // parse warning + if ( text.contains( "mpeg user scan data: one or more BCD fields out of range for" ) ) { + int index = text.find( " for" ); + + emit infoMessage( i18n( "One or more BCD fields out of range for %1" ).arg( text.mid( index + 4 ).stripWhiteSpace() ), K3bJob::WARNING ); + + } else if ( text.contains( "mpeg user scan data: from now on, scan information data errors will not be reported anymore" ) ) { + emit infoMessage( i18n( "From now on, scan information data errors will not be reported anymore" ), K3bJob::INFO ); + emit infoMessage( i18n( "Consider enabling the 'update scan offsets' option, if it is not enabled already." ), K3bJob::INFO ); + + } else if ( text.contains( "APS' pts seems out of order (actual pts" ) ) { + int index = text.find( "(actual pts" ); + int index2 = text.find( ", last seen pts" ); + int index3 = text.find( ") -- ignoring this aps" ); + + emit infoMessage( i18n( "APS' pts seems out of order (actual pts %1, last seen pts %2)" ).arg( text.mid( index + 12, index2 - index - 12 ).stripWhiteSpace() ).arg( text.mid( index2 + 14, index3 - index2 - 14 ).stripWhiteSpace() ), K3bJob::WARNING ); + emit infoMessage( i18n( "Ignoring this aps" ), K3bJob::INFO ); + + } else if ( text.contains( "bad packet at packet" ) ) { + int index = text.find( "at packet #" ); + int index2 = text.find( "(stream byte offset" ); + int index3 = text.find( ") -- remaining " ); + int index4 = text.find( "bytes of stream will be ignored" ); + + emit infoMessage( i18n( "Bad packet at packet #%1 (stream byte offset %2)" ).arg( text.mid( index + 11, index2 - index - 11 ).stripWhiteSpace() ).arg( text.mid( index2 + 19, index3 - index2 - 19 ).stripWhiteSpace() ), K3bJob::WARNING ); + emit infoMessage( i18n( "Remaining %1 bytes of stream will be ignored." ).arg( text.mid( index3 + 15, index4 - index3 - 15 ).stripWhiteSpace() ), K3bJob::WARNING ); + } +} + +QString K3bVcdJob::jobDescription() const +{ + switch ( m_doc->vcdType() ) { + case K3bVcdDoc::VCD11: + return i18n( "Writing Video CD (Version 1.1)" ); + case K3bVcdDoc::VCD20: + return i18n( "Writing Video CD (Version 2.0)" ); + case K3bVcdDoc::SVCD10: + return i18n( "Writing Super Video CD" ); + case K3bVcdDoc::HQVCD: + return i18n( "Writing High-Quality Video CD" ); + default: + return i18n( "Writing Video CD" ); + } +} + + +QString K3bVcdJob::jobDetails() const +{ + return ( i18n( "1 MPEG (%1)", + "%n MPEGs (%1)", + m_doc->tracks() ->count() ).arg( KIO::convertSize( m_doc->size() ) ) + + ( m_doc->copies() > 1 + ? i18n( " - %n copy", " - %n copies", m_doc->copies() ) + : QString::null ) ); +} + +#include "k3bvcdjob.moc" diff --git a/libk3b/projects/videocd/k3bvcdjob.h b/libk3b/projects/videocd/k3bvcdjob.h new file mode 100644 index 0000000..917c8b1 --- /dev/null +++ b/libk3b/projects/videocd/k3bvcdjob.h @@ -0,0 +1,115 @@ +/* +* +* $Id: k3bvcdjob.h 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003-2004 Christian Kvasny <[email protected]> +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> +* +* This program 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 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + +#ifndef K3BVCDJOB_H +#define K3BVCDJOB_H + +#include <k3bjob.h> + +class K3bVcdDoc; +class K3bVcdTrack; +class QString; +class K3bProcess; +class KProcess; +class QDataStream; +class K3bAbstractWriter; +class K3bDevice::Device; + + +class K3bVcdJob : public K3bBurnJob +{ + Q_OBJECT + + public: + K3bVcdJob( K3bVcdDoc*, K3bJobHandler*, QObject* parent = 0, const char* name = 0 ); + ~K3bVcdJob(); + + K3bDoc* doc() const; + K3bVcdDoc* vcdDoc() const + { + return m_doc; + } + K3bDevice::Device* writer() const; + + QString jobDescription() const; + QString jobDetails() const; + + public slots: + void start(); + void cancel(); + + private slots: + void cancelAll(); + + protected slots: + void slotVcdxBuildFinished(); + void slotParseVcdxBuildOutput( KProcess*, char* output, int len ); + + void slotWriterJobPercent( int p ); + void slotProcessedSize( int cs, int ts ); + void slotWriterNextTrack( int t, int tt ); + void slotWriterJobFinished( bool success ); + + + private: + bool prepareWriterJob(); + + void xmlGen(); + void vcdxBuild(); + void parseInformation( const QString& ); + void startWriterjob(); + + int m_copies; + int m_finishedCopies; + + unsigned long m_blocksToCopy; + unsigned long m_bytesFinishedTracks; + unsigned long m_bytesFinished; + + enum { stageUnknown, stageScan, stageWrite, _stage_max }; + + K3bVcdDoc* m_doc; + K3bDevice::Device* m_writer; + K3bDevice::Device* m_reader; + K3bVcdTrack* m_currentWrittenTrack; + + int m_speed; + int m_stage; + int m_currentcopy; + int m_currentWrittenTrackNumber; + + double m_createimageonlypercent; + + bool firstTrack; + bool m_burnProof; + bool m_keepImage; + bool m_onlyCreateImage; + bool m_onTheFly; + bool m_dummy; + bool m_fastToc; + bool m_readRaw; + bool m_imageFinished; + bool m_canceled; + + QString m_tempPath; + QString m_cueFile; + QString m_xmlFile; + QString m_collectedOutput; + + K3bAbstractWriter* m_writerJob; + K3bProcess* m_process; +}; + +#endif diff --git a/libk3b/projects/videocd/k3bvcdoptions.cpp b/libk3b/projects/videocd/k3bvcdoptions.cpp new file mode 100644 index 0000000..6009a4a --- /dev/null +++ b/libk3b/projects/videocd/k3bvcdoptions.cpp @@ -0,0 +1,146 @@ +/* +* +* $Id: k3bvcdoptions.cpp 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003-2004 Christian Kvasny <[email protected]> +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> +* +* This program 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 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + +// Kde Includes +#include <kapplication.h> +#include <kconfig.h> +#include <k3bcore.h> +#include <klocale.h> +#include <kstandarddirs.h> + +// Qt Includes +#include <qstring.h> +#include <qfile.h> + +// K3b Includes +#include "k3bvcdoptions.h" +#include <k3bversion.h> + +K3bVcdOptions::K3bVcdOptions() + : m_restriction( 0 ), + m_segment( 0 ), + m_sequence( 0 ), + m_pbcenabled( PbcEnabled() ), + m_pbcnumkeysenabled( PbcNumkeysEnabled() ), + m_volumeID( "VIDEOCD" ), + m_albumID( "" ), + m_volumeSetId( "" ), + m_publisher( QString( "K3b - Version %1" ).arg( k3bcore->version() ) ), + m_applicationId( "CDI/CDI_VCD.APP;1" ), + m_systemId( "CD-RTOS CD-BRIDGE" ), + m_vcdclass( "vcd" ), + m_vcdversion( "2.0" ), + m_pregapleadout( 150 ), + m_pregaptrack( 150 ), + m_frontmargintrack( 30 ), + m_rearmargintrack( 45 ), + m_frontmargintrackSVCD( 0 ), + m_rearmargintrackSVCD( 0 ), + m_mpegversion( 1 ), + m_volumeCount( 1 ), + m_volumeNumber( 1 ), + m_autodetect( true ), + m_cdisupport( false ), + m_brokensvcdmode( false ), + m_VCD30interpretation( false ), + m_sector2336( false ), + m_updatescanoffsets( false ), + m_relaxedaps( false ), + m_segmentfolder( true ), + m_usegaps( false ) +{} + +bool K3bVcdOptions::checkCdiFiles() +{ + m_cdisize = 0; + if ( !QFile::exists( locate( "data", "k3b/cdi/cdi_imag.rtf" ) ) ) + return false; + if ( !QFile::exists( locate( "data", "k3b/cdi/cdi_text.fnt" ) ) ) + return false; + if ( !QFile::exists( locate( "data", "k3b/cdi/cdi_vcd.app" ) ) ) + return false; + if ( !QFile::exists( locate( "data", "k3b/cdi/cdi_vcd.cfg" ) ) ) + return false; + + m_cdisize += QFile( locate( "data", "k3b/cdi/cdi_imag.rtf" ) ).size(); + m_cdisize += QFile( locate( "data", "k3b/cdi/cdi_text.fnt" ) ).size(); + m_cdisize += QFile( locate( "data", "k3b/cdi/cdi_vcd.app" ) ).size(); + m_cdisize += QFile( locate( "data", "k3b/cdi/cdi_vcd.cfg" ) ).size(); + + return true; +} + +void K3bVcdOptions::save( KConfigBase* c ) +{ + c->writeEntry( "volume_id", m_volumeID ); + c->writeEntry( "album_id", m_albumID ); + c->writeEntry( "volume_set_id", m_volumeSetId ); + c->writeEntry( "preparer", m_preparer ); + c->writeEntry( "publisher", m_publisher ); + c->writeEntry( "volume_count", m_volumeCount ); + c->writeEntry( "volume_number", m_volumeNumber ); + c->writeEntry( "autodetect", m_autodetect ); + c->writeEntry( "cdi_support", m_cdisupport ); + c->writeEntry( "broken_svcd_mode", m_brokensvcdmode ); + c->writeEntry( "VCD30interpretation", m_VCD30interpretation ); + c->writeEntry( "2336_sectors", m_sector2336 ); + c->writeEntry( "UpdateScanOffsets", m_updatescanoffsets ); + c->writeEntry( "RelaxedAps", m_relaxedaps ); + c->writeEntry( "PbcEnabled", m_pbcenabled ); + c->writeEntry( "SegmentFolder", m_segmentfolder ); + c->writeEntry( "Restriction", m_restriction ); + c->writeEntry( "PreGapLeadout", m_pregapleadout ); + c->writeEntry( "PreGapTrack", m_pregaptrack ); + c->writeEntry( "FrontMarginTrack", m_frontmargintrack ); + c->writeEntry( "RearMarginTrack", m_rearmargintrack ); + c->writeEntry( "UseGaps", m_usegaps ); +} + + +K3bVcdOptions K3bVcdOptions::load( KConfigBase* c ) +{ + K3bVcdOptions options; + + options.setVolumeId( c->readEntry( "volume_id", options.volumeId() ) ); + options.setAlbumId( c->readEntry( "album_id", options.albumId() ) ); + options.setVolumeSetId( c->readEntry( "volume_set_id", options.volumeSetId() ) ); + options.setPreparer( c->readEntry( "preparer", options.preparer() ) ); + options.setPublisher( c->readEntry( "publisher", options.publisher() ) ); + options.setVolumeCount( c->readNumEntry( "volume_count", options.volumeCount() ) ); + options.setVolumeNumber( c->readNumEntry( "volume_number", options.volumeNumber() ) ); + options.setAutoDetect( c->readBoolEntry( "autodetect", options.AutoDetect() ) ); + options.setCdiSupport( c->readBoolEntry( "cdi_support", options.CdiSupport() ) ); + options.setNonCompliantMode( c->readBoolEntry( "broken_svcd_mode", options.NonCompliantMode() ) ); + options.setVCD30interpretation( c->readBoolEntry( "VCD30interpretation", options.VCD30interpretation() ) ); + options.setSector2336( c->readBoolEntry( "2336_sectors", options.Sector2336() ) ); + options.setUpdateScanOffsets( c->readBoolEntry( "UpdateScanOffsets", options.UpdateScanOffsets() ) ); + options.setRelaxedAps( c->readBoolEntry( "RelaxedAps", options.RelaxedAps() ) ); + options.setPbcEnabled( c->readBoolEntry( "PbcEnabled", options.PbcEnabled() ) ); + options.setSegmentFolder( c->readBoolEntry( "SegmentFolder", options.SegmentFolder() ) ); + options.setRestriction( c->readNumEntry( "Restriction", options.Restriction() ) ); + options.setPreGapLeadout( c->readNumEntry( "PreGapLeadout", options.PreGapLeadout() ) ); + options.setPreGapTrack( c->readNumEntry( "PreGapTrack", options.PreGapTrack() ) ); + options.setFrontMarginTrack( c->readNumEntry( "FrontMarginTrack", options.FrontMarginTrack() ) ); + options.setRearMarginTrack( c->readNumEntry( "RearMarginTrack", options.RearMarginTrack() ) ); + options.setUseGaps( c->readBoolEntry( "UseGaps", options.UseGaps() ) ); + + return options; +} + +K3bVcdOptions K3bVcdOptions::defaults() +{ + // let the constructor create defaults + return K3bVcdOptions(); +} diff --git a/libk3b/projects/videocd/k3bvcdoptions.h b/libk3b/projects/videocd/k3bvcdoptions.h new file mode 100644 index 0000000..aa5fed2 --- /dev/null +++ b/libk3b/projects/videocd/k3bvcdoptions.h @@ -0,0 +1,377 @@ +/* +* +* $Id: k3bvcdoptions.h 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003-2004 Christian Kvasny <[email protected]> +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> +* +* This program 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 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + +#ifndef K3B_VCD_OPTIONS_H +#define K3B_VCD_OPTIONS_H + +#include <qstring.h> +#include "k3b_export.h" + +class KConfigBase; + +class LIBK3B_EXPORT K3bVcdOptions +{ + public: + K3bVcdOptions(); + const QString& volumeId() const + { + return m_volumeID; + } + const QString& albumId() const + { + return m_albumID; + } + const QString& volumeSetId() const + { + return m_volumeSetId; + } + const QString& preparer() const + { + return m_preparer; + } + const QString& publisher() const + { + return m_publisher; + } + + const QString& applicationId() const + { + return m_applicationId; + } + const QString& systemId() const + { + return m_systemId; + } + + const QString& vcdClass() const + { + return m_vcdclass; + } + const QString& vcdVersion() const + { + return m_vcdversion; + } + + const int PreGapLeadout() + { + return m_pregapleadout; + } + const int PreGapTrack() + { + return m_pregaptrack; + } + const int FrontMarginTrack() + { + return m_frontmargintrack; + } + const int RearMarginTrack() + { + return m_rearmargintrack; + } + const int FrontMarginTrackSVCD() + { + return m_frontmargintrackSVCD; + } + const int RearMarginTrackSVCD() + { + return m_rearmargintrackSVCD; + } + + const int mpegVersion() const + { + return m_mpegversion; + } + + const int volumeCount() const + { + return m_volumeCount; + } + const int volumeNumber() const + { + return m_volumeNumber; + } + + const bool AutoDetect() const + { + return m_autodetect; + } + const bool CdiSupport() const + { + return m_cdisupport; + } + const bool NonCompliantMode() const + { + return m_brokensvcdmode; + } + const bool VCD30interpretation() const + { + return m_VCD30interpretation; + } + const bool Sector2336() const + { + return m_sector2336; + } + const bool UpdateScanOffsets() const + { + return m_updatescanoffsets; + } + const bool RelaxedAps() const + { + return m_relaxedaps; + } + const bool UseGaps() const + { + return m_usegaps; + } + const unsigned long long CDIsize() const + { + return m_cdisize; + } + + void setAlbumId( const QString& s ) + { + m_albumID = s; + } + void setVolumeId( const QString& s ) + { + m_volumeID = s; + } + void setVolumeSetId( const QString& s ) + { + m_volumeSetId = s; + } + void setPreparer( const QString& s ) + { + m_preparer = s; + } + void setPublisher( const QString& s ) + { + m_publisher = s; + } + + void setVcdClass( const QString& s ) + { + m_vcdclass = s; + } + void setVcdVersion( const QString& s ) + { + m_vcdversion = s; + } + + void setPreGapLeadout( const int i ) + { + m_pregapleadout = i; + } + void setPreGapTrack( const int i ) + { + m_pregaptrack = i; + } + void setFrontMarginTrack( const int i ) + { + m_frontmargintrack = i; + } + void setRearMarginTrack( const int i ) + { + m_rearmargintrack = i; + } + void setFrontMarginTrackSVCD( const int i ) + { + m_frontmargintrackSVCD = i; + } + void setRearMarginTrackSVCD( const int i ) + { + m_rearmargintrackSVCD = i; + } + + void setMpegVersion( const int v ) + { + m_mpegversion = v; + } + void setVolumeCount( const int c ) + { + m_volumeCount = c; + } + void setVolumeNumber( const int n ) + { + m_volumeNumber = n; + } + + void setAutoDetect( const bool& b ) + { + m_autodetect = b; + } + void setCdiSupport( const bool& b ) + { + m_cdisupport = b; + } + void setNonCompliantMode( const bool& b ) + { + m_brokensvcdmode = b; + } + void setVCD30interpretation( const bool& b ) + { + m_VCD30interpretation = b; + } + void setSector2336( const bool& b ) + { + m_sector2336 = b; + } + void setUpdateScanOffsets( const bool& b ) + { + m_updatescanoffsets = b; + } + void setRelaxedAps( const bool& b ) + { + m_relaxedaps = b; + } + void setUseGaps( const bool& b ) + { + m_usegaps = b; + } + + bool checkCdiFiles(); + void save( KConfigBase* c ); + + static K3bVcdOptions load( KConfigBase* c ); + static K3bVcdOptions defaults(); + + void setPbcEnabled( const bool& b ) + { + m_pbcenabled = b; + } + bool PbcEnabled() const + { + return m_pbcenabled; + }; + void setPbcNumkeysEnabled( const bool& b ) + { + m_pbcnumkeysenabled = b; + } + bool PbcNumkeysEnabled() const + { + return m_pbcnumkeysenabled; + }; + + void setPbcPlayTime( const int i ) + { + m_def_pbcplaytime = i; + } + int PbcPlayTime( ) + { + return m_def_pbcplaytime; + } + + void setPbcWaitTime( const int i ) + { + m_def_pbcwaittime = i; + } + int PbcWaitTime( ) + { + return m_def_pbcwaittime; + } + + void setSegmentFolder( const bool& b ) + { + m_segmentfolder = b; + } + bool SegmentFolder() const + { + return m_segmentfolder; + }; + + void setRestriction( const int i ) + { + m_restriction = i; + } + int Restriction() const + { + return m_restriction; + }; + void increaseSegments( ) + { + m_segment += 1; + } + void decreaseSegments( ) + { + m_segment -= 1; + } + bool haveSegments() const + { + return m_segment > 0; + }; + void increaseSequence( ) + { + m_sequence += 1; + } + void decreaseSequence( ) + { + m_sequence -= 1; + } + + bool haveSequence() const + { + return m_sequence > 0; + }; + + private: + int m_restriction; + int m_segment; + int m_sequence; + + // pbc + bool m_pbcenabled; + bool m_pbcnumkeysenabled; + + // volume descriptor + QString m_volumeID; + QString m_albumID; + QString m_volumeSetId; + + QString m_preparer; + QString m_publisher; + + QString m_applicationId; + QString m_systemId; + + QString m_vcdclass; + QString m_vcdversion; + + int m_pregapleadout; + int m_pregaptrack; + int m_frontmargintrack; + int m_rearmargintrack; + int m_frontmargintrackSVCD; + int m_rearmargintrackSVCD; + + int m_mpegversion; + int m_volumeCount; + int m_volumeNumber; + + bool m_autodetect; + bool m_cdisupport; + bool m_brokensvcdmode; + bool m_VCD30interpretation; + bool m_sector2336; + bool m_updatescanoffsets; + bool m_relaxedaps; + bool m_segmentfolder; + bool m_usegaps; + + int m_def_pbcplaytime; + int m_def_pbcwaittime; + unsigned long long m_cdisize; +}; + +#endif diff --git a/libk3b/projects/videocd/k3bvcdtrack.cpp b/libk3b/projects/videocd/k3bvcdtrack.cpp new file mode 100644 index 0000000..7f0043f --- /dev/null +++ b/libk3b/projects/videocd/k3bvcdtrack.cpp @@ -0,0 +1,456 @@ +/* +* +* $Id: k3bvcdtrack.cpp 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003-2004 Christian Kvasny <[email protected]> +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> +* +* This program 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 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + +#include <kapplication.h> +#include <kconfig.h> + +#include <qstring.h> +#include <qfileinfo.h> + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <kdebug.h> +#include <klocale.h> + +// K3b Includes +#include "k3bvcdtrack.h" +#include <k3bglobals.h> + +K3bVcdTrack::K3bVcdTrack( QPtrList<K3bVcdTrack>* parent, const QString& filename ) + : m_pbcnumkeys( true ), + m_pbcnumkeysuserdefined( false ), + m_file( filename ) +{ + m_parent = parent; + m_title = QFileInfo( m_file ).baseName( true ); + + m_revreflist = new QPtrList<K3bVcdTrack>; + + for ( int i = 0; i < K3bVcdTrack::_maxPbcTracks; i++ ) { + m_pbctrackmap.insert( i, 0L ); + m_pbcnontrackmap.insert( i, K3bVcdTrack::DISABLED ); + m_pbcusrdefmap.insert( i, false ); + } + + m_reactivity = false; + + m_definedkeysmap.clear(); + + mpeg_info = new Mpeginfo(); +} + + +K3bVcdTrack::~K3bVcdTrack() +{} + + +KIO::filesize_t K3bVcdTrack::size() const +{ + return m_file.size(); +} + +int K3bVcdTrack::index() const +{ + int i = m_parent->find( this ); + if ( i < 0 ) + kdDebug() << "(K3bVcdTrack) I'm not part of my parent!" << endl; + return i; +} + +void K3bVcdTrack::addToRevRefList( K3bVcdTrack* revreftrack ) +{ + kdDebug() << "K3bVcdTrack::addToRevRefList: track = " << revreftrack << endl; + + m_revreflist->append( revreftrack ); + + kdDebug() << "K3bVcdTrack::hasRevRef count = " << m_revreflist->count() << " empty = " << m_revreflist->isEmpty() << endl; +} + +void K3bVcdTrack::delFromRevRefList( K3bVcdTrack* revreftrack ) +{ + if ( !m_revreflist->isEmpty() ) { + m_revreflist->remove + ( revreftrack ); + } +} + +bool K3bVcdTrack::hasRevRef() +{ + return !m_revreflist->isEmpty() ; +} + +void K3bVcdTrack::delRefToUs() +{ + for ( K3bVcdTrack * track = m_revreflist->first(); track; track = m_revreflist->next() ) { + for ( int i = 0; i < K3bVcdTrack::_maxPbcTracks; i++ ) { + kdDebug() << "K3bVcdTrack::delRefToUs count = " << m_revreflist->count() << " empty = " << m_revreflist->isEmpty() << " track = " << track << " this = " << this << endl; + if ( this == track->getPbcTrack( i ) ) { + track->setPbcTrack( i ); + track->setUserDefined( i, false ); + track->delFromRevRefList( this ); + } + } + } +} + +void K3bVcdTrack::delRefFromUs() +{ + for ( int i = 0; i < K3bVcdTrack::_maxPbcTracks; i++ ) { + if ( this->getPbcTrack( i ) ) { + this->getPbcTrack( i ) ->delFromRevRefList( this ); + } + } +} + +void K3bVcdTrack::setPbcTrack( int which, K3bVcdTrack* pbctrack ) +{ + kdDebug() << "K3bVcdTrack::setPbcTrack " << which << ", " << pbctrack << endl; + m_pbctrackmap.replace( which, pbctrack ); +} + +void K3bVcdTrack::setPbcNonTrack( int which, int type ) +{ + kdDebug() << "K3bVcdTrack::setNonPbcTrack " << which << ", " << type << endl; + m_pbcnontrackmap.replace( which, type ); +} + +void K3bVcdTrack::setUserDefined( int which, bool ud ) +{ + m_pbcusrdefmap.replace( which, ud ); +} + +K3bVcdTrack* K3bVcdTrack::getPbcTrack( const int& which ) +{ + if ( m_pbctrackmap.find( which ) == m_pbctrackmap.end() ) + return 0; + else + return m_pbctrackmap[ which ]; +} + +int K3bVcdTrack::getNonPbcTrack( const int& which ) +{ + if ( m_pbcnontrackmap.find( which ) == m_pbcnontrackmap.end() ) + return 0; + else + return m_pbcnontrackmap[ which ]; +} + +bool K3bVcdTrack::isPbcUserDefined( int which ) +{ + return m_pbcusrdefmap[ which ]; +} + +const QString K3bVcdTrack::resolution() +{ + if ( mpeg_info->has_video ) { + for ( int i = 0; i < 2; i++ ) { + if ( mpeg_info->video[ i ].seen ) { + return QString( "%1 x %2" ).arg( mpeg_info->video[ i ].hsize ).arg( mpeg_info->video[ i ].vsize ); + } + } + } + + return i18n( "n/a" ); +} + +const QString K3bVcdTrack::highresolution() +{ + if ( mpeg_info->has_video ) { + if ( mpeg_info->video[ 2 ].seen ) { + return QString( "%1 x %2" ).arg( mpeg_info->video[ 2 ].hsize ).arg( mpeg_info->video[ 2 ].vsize ); + } + } + return i18n( "n/a" ); +} + +const QString K3bVcdTrack::video_frate() +{ + if ( mpeg_info->has_video ) { + for ( int i = 0; i < 2; i++ ) { + if ( mpeg_info->video[ i ].seen ) { + return QString::number( mpeg_info->video[ i ].frate ); + } + } + } + + return i18n( "n/a" ); +} + +const QString K3bVcdTrack::video_bitrate() +{ + if ( mpeg_info->has_video ) { + for ( int i = 0; i < 2; i++ ) { + if ( mpeg_info->video[ i ].seen ) { + return i18n( "%1 bit/s" ).arg( mpeg_info->video[ i ].bitrate ) ; + } + } + } + + return i18n( "n/a" ); +} + + + +const QString K3bVcdTrack::video_format() +{ + if ( mpeg_info->has_video ) { + for ( int i = 0; i < 2; i++ ) { + if ( mpeg_info->video[ i ].seen ) { + switch ( mpeg_info->video[ i ].video_format ) { + case 0 : + return i18n( "Component" ); + break; + case 1 : + return "PAL"; + break; + case 2 : + return "NTSC"; + break; + case 3 : + return "SECAM"; + break; + case 4 : + return "MAC"; + break; + case 5 : + default: + return i18n( "Unspecified" ); + kdDebug() << "K3bVcdTrack::video_format() :" << mpeg_info->video[ i ].video_format << endl; + break; + } + } + } + } + return i18n( "n/a" ); +} + +const QString K3bVcdTrack::video_chroma() +{ + if ( mpeg_info->has_video ) { + // MPEG1 only supports 4:2:0 Format + if ( version() == K3bMpegInfo::MPEG_VERS_MPEG1 ) + return QString( "4:2:0" ); + + for ( int i = 0; i < 2; i++ ) { + if ( mpeg_info->video[ i ].seen ) { + switch ( mpeg_info->video[ i ].chroma_format ) { + case 1 : + return QString( "4:2:0" ); + break; + case 2 : + return QString( "4:2:2" ); + break; + case 3 : + return QString( "4:4:4" ); + break; + + } + } + } + } + + return i18n( "n/a" ); +} + +const QString K3bVcdTrack::audio_layer() +{ + if ( mpeg_info->has_audio ) { + for ( int i = 0; i < 2; i++ ) { + if ( mpeg_info->audio[ i ].seen ) { + return QString::number( mpeg_info->audio[ i ].layer ); + } + } + } + + return i18n( "n/a" ); +} + +const QString K3bVcdTrack::audio_bitrate() +{ + if ( mpeg_info->has_audio ) { + for ( int i = 0; i < 2; i++ ) { + if ( mpeg_info->audio[ i ].seen ) { + return i18n( "%1 bit/s" ).arg( mpeg_info->audio[ i ].bitrate ) ; + } + } + } + + return i18n( "n/a" ); +} + +const QString K3bVcdTrack::audio_sampfreq() +{ + if ( mpeg_info->has_audio ) { + for ( int i = 0; i < 2; i++ ) { + if ( mpeg_info->audio[ i ].seen ) { + return i18n( "%1 Hz" ).arg( mpeg_info->audio[ i ].sampfreq ) ; + } + } + } + + return i18n( "n/a" ); +} + +const QString K3bVcdTrack::audio_mode( ) +{ + if ( mpeg_info->has_audio ) { + for ( int i = 2; i >= 0; i-- ) + if ( mpeg_info->audio[ i ].seen ) + return QString( audio_type2str( mpeg_info->audio[ i ].version, mpeg_info->audio[ i ].mode, i ) ); + + } + + return i18n( "n/a" ); +} + +const QString K3bVcdTrack::audio_copyright( ) +{ + if ( mpeg_info->has_audio ) { + for ( int i = 2; i >= 0; i-- ) + if ( mpeg_info->audio[ i ].seen ) + if ( mpeg_info->audio[ i ].copyright ) + return QString( "(c) " ) + ( mpeg_info->audio[ i ].original ? i18n( "original" ) : i18n( "duplicate" ) ); + else + return ( mpeg_info->audio[ i ].original ? i18n( "original" ) : i18n( "duplicate" ) ); + } + + return i18n( "n/a" ); +} + +const QString K3bVcdTrack::mpegTypeS( bool audio ) +{ + if ( mpeg_info->has_video && !audio ) { + for ( int i = 0; i < 3; i++ ) + if ( mpeg_info->video[ i ].seen ) { + if ( i == 0 ) { + return QString( "MPEG%1 " ).arg( mpeg_info->version ) + i18n( "Motion Picture" ); + } else { + return QString( "MPEG%1 " ).arg( mpeg_info->version ) + i18n( "Still Picture" ); + } + } + } + if ( mpeg_info->has_audio && audio ) { + for ( int i = 0; i < 3; i++ ) + if ( mpeg_info->audio[ i ].seen ) { + return QString( "MPEG%1 " ).arg( mpeg_info->audio[ i ].version ) + i18n( "Layer %1" ).arg( mpeg_info->audio[ i ].layer ); + } + } + + return i18n( "n/a" ); +} + +const int K3bVcdTrack::mpegType( ) +{ + if ( mpeg_info->has_video ) { + for ( int i = 0; i < 3; i++ ) + if ( mpeg_info->video[ i ].seen ) { + if ( i == 0 ) { + return 0; // MPEG_MOTION; + } else { + return 1; // MPEG_STILL; + } + } + } + if ( mpeg_info->has_audio ) { + for ( int i = 0; i < 3; i++ ) + if ( mpeg_info->audio[ i ].seen ) + return 2; // MPEG_AUDIO; + } + + return -1; // MPEG_UNKNOWN; +} + +const QString K3bVcdTrack::audio_type2str( unsigned int version, unsigned int audio_mode, unsigned int audio_type ) +{ + kdDebug() << "K3bVcdTrack::audio_type2str() version:" << version << " audio_mode:" << audio_mode << " audio_type:" << audio_type << endl; + + QString audio_types[ 3 ][ 5 ] = { + { + i18n( "unknown" ), + i18n( "invalid" ), + QString::null, + QString::null, + QString::null + }, + { + i18n( "stereo" ), + i18n( "joint stereo" ), + i18n( "dual channel" ), + i18n( "single channel" ) + }, + { + QString::null, + i18n( "dual channel" ), + i18n( "surround sound" ), + QString::null, + QString::null + } + }; + switch ( version ) { + case K3bMpegInfo::MPEG_VERS_MPEG1: + return audio_types[ 1 ][ audio_mode ]; + break; + + case K3bMpegInfo::MPEG_VERS_MPEG2: + if ( audio_type > 0 ) { + return audio_types[ 2 ][ audio_type ]; + } + return audio_types[ 1 ][ audio_mode ]; + break; + } + + return i18n( "n/a" ); +} + +// convert a time in second to HH:mm:ss notation +QString K3bVcdTrack::SecsToHMS( double duration ) +{ + byte hours = ( byte ) ( duration / 3600 ); + byte mins = ( byte ) ( ( duration / 60 ) - ( hours * 60 ) ); + float secs = duration - 60 * mins - 3600 * hours; + if ( hours != 0 ) { + return QString( "%1:" ).arg( hours ).rightJustify( 3, ' ' ) + QString( "%1:" ).arg( mins ).rightJustify( 3, '0' ) + QString::number( secs, 'f', 2 ); + } + if ( mins != 0 ) { + return QString( "%1:" ).arg( mins ).rightJustify( 3, '0' ) + QString::number( secs, 'f', 2 ); + } + return QString::number( secs, 'f', 2 ); +} + +void K3bVcdTrack::PrintInfo() +{ + + kdDebug() << "K3bVcdTrack::PrintInfo() ....................." << endl; + kdDebug() << " version : MPEG" << version() << endl; + kdDebug() << " duration : " << duration() << endl; + kdDebug() << " muxrate : " << muxrate() << endl; + kdDebug() << " video ......................................" << endl; + kdDebug() << " type : " << mpegTypeS() << endl; + kdDebug() << " resolution : " << resolution() << endl; + kdDebug() << " high resolution: " << highresolution() << endl; + kdDebug() << " frate : " << video_frate() << endl; + kdDebug() << " bitrate : " << video_bitrate() << endl; + kdDebug() << " format : " << video_format( ) << endl; + kdDebug() << " chroma : " << video_chroma( ) << endl; + kdDebug() << " audio ......................................" << endl; + kdDebug() << " type : " << mpegTypeS( true ) << endl; + kdDebug() << " mode : " << audio_mode() << endl; + kdDebug() << " layer : " << audio_layer() << endl; + kdDebug() << " bitrate : " << audio_bitrate() << endl; + kdDebug() << " sampfreq : " << audio_sampfreq() << endl; + +} diff --git a/libk3b/projects/videocd/k3bvcdtrack.h b/libk3b/projects/videocd/k3bvcdtrack.h new file mode 100644 index 0000000..0d9a3cf --- /dev/null +++ b/libk3b/projects/videocd/k3bvcdtrack.h @@ -0,0 +1,198 @@ +/* +* +* $Id: k3bvcdtrack.h 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003-2004 Christian Kvasny <[email protected]> +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> +* +* This program 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 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + +#ifndef K3BVCDTRACK_H +#define K3BVCDTRACK_H + +// Qt Includes +#include <qstring.h> +#include <qfileinfo.h> +#include <qfile.h> +#include <qptrlist.h> + +// Kde Includes +#include <kio/global.h> + +// K3b Includes +#include "mpeginfo/k3bmpeginfo.h" +#include "k3b_export.h" +class LIBK3B_EXPORT K3bVcdTrack +{ + public: + K3bVcdTrack( QPtrList<K3bVcdTrack>* parent, const QString& filename ); + ~K3bVcdTrack(); + + QString fileName() const + { + return QFileInfo( m_file ).fileName(); + } + QString absPath() const + { + return QFileInfo( m_file ).absFilePath(); + } + KIO::filesize_t size() const; + int index() const; + + const QString& title() const + { + return m_title; + } + void setTitle( const QString& t ) + { + m_title = t; + } + bool isSegment() + { + return mpegType() == 1; + }; + + + + // PBC + enum PbcTracks { PREVIOUS, NEXT, RETURN, DEFAULT, AFTERTIMEOUT, _maxPbcTracks }; + enum PbcTypes { DISABLED, VIDEOEND }; + + void addToRevRefList( K3bVcdTrack* revreftrack ); + void delFromRevRefList( K3bVcdTrack* revreftrack ); + bool hasRevRef(); + void delRefToUs(); + void delRefFromUs(); + + void setPbcTrack( int, K3bVcdTrack* pbctrack = 0L ); + void setPbcNonTrack( int, int ); + void setUserDefined( int, bool ); + void setPlayTime( int t ) + { + m_pbcplaytime = t; + } + void setWaitTime( int t ) + { + m_pbcwaittime = t; + } + void setReactivity( bool b ) + { + m_reactivity = b; + } + void setPbcNumKeys( const bool& b ) + { + m_pbcnumkeys = b; + } + bool PbcNumKeys() const + { + return m_pbcnumkeys; + }; + void setPbcNumKeysUserdefined( const bool& b ) + { + m_pbcnumkeysuserdefined = b; + }; + bool PbcNumKeysUserdefined() const + { + return m_pbcnumkeysuserdefined; + }; + + K3bVcdTrack* getPbcTrack( const int& ); + int getNonPbcTrack( const int& ); + bool isPbcUserDefined( int ); + int getPlayTime() + { + return m_pbcplaytime; + } + int getWaitTime() + { + return m_pbcwaittime; + } + bool Reactivity() + { + return m_reactivity; + } + + // Numeric keys + void setDefinedNumKey( int key, K3bVcdTrack* track ) + { + m_definedkeysmap.insert( key, track ); + } + void delDefinedNumKey( int key ) + { + m_definedkeysmap.remove( key ); + } + void delDefinedNumKey() + { + m_definedkeysmap.clear(); + } + QMap<int, K3bVcdTrack*> DefinedNumKey() + { + return m_definedkeysmap; + } + + // Mpeg Infos + const QString resolution(); + const QString highresolution(); + const QString video_frate(); + const QString video_bitrate(); + const QString audio_layer(); + const QString audio_bitrate(); + const QString audio_sampfreq(); + + const QString duration() + { + return SecsToHMS( mpeg_info->playing_time ); + }; + const int version() + { + return mpeg_info->version; + }; + const unsigned long muxrate() + { + return mpeg_info->muxrate; + }; + const QString video_format( ); + const QString video_chroma( ); + const QString audio_mode( ); + const QString audio_copyright( ); + const QString mpegTypeS( bool audio = false ); + const int mpegType(); + + void PrintInfo(); + + Mpeginfo* mpeg_info; + + protected: + + const QString audio_type2str( unsigned int , unsigned int, unsigned int ); + QString SecsToHMS( double ); + + QPtrList<K3bVcdTrack>* m_parent; + + // PBC + QPtrList<K3bVcdTrack>* m_revreflist; // List of Tracks which points to us + QMap<int, K3bVcdTrack*> m_pbctrackmap; // Pbc Tracks (Previous, Next, ...) + QMap<int, int> m_pbcnontrackmap; // Pbc NON Track types (Previous, Next, ...) + QMap<int, bool> m_pbcusrdefmap; // Pbc is userdefined or defaults (Previous, Next, ...) + QMap<int, K3bVcdTrack*> m_definedkeysmap; + + bool m_pbcnumkeys; + bool m_pbcnumkeysuserdefined; + + int m_pbcplaytime; + int m_pbcwaittime; + /********************************************************************************/ + + bool m_reactivity; + int m_filetype; + QFile m_file; + QString m_title; +}; + +#endif diff --git a/libk3b/projects/videocd/k3bvcdxmlview.cpp b/libk3b/projects/videocd/k3bvcdxmlview.cpp new file mode 100644 index 0000000..0b6f250 --- /dev/null +++ b/libk3b/projects/videocd/k3bvcdxmlview.cpp @@ -0,0 +1,440 @@ +/* +* +* $Id: k3bvcdxmlview.cpp 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003-2004 Christian Kvasny <[email protected]> +* THX to Manfred Odenstein <[email protected]> +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> +* +* This program 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 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + +#include <qfile.h> + +#include <kstandarddirs.h> +#include <kdebug.h> + + +#include "k3bvcdxmlview.h" +#include "k3bvcdtrack.h" +#include <k3bcore.h> +#include <k3bversion.h> + +K3bVcdXmlView::K3bVcdXmlView( K3bVcdDoc* pDoc ) +{ + + m_doc = pDoc; + +} + +K3bVcdXmlView::~K3bVcdXmlView() +{} + +bool K3bVcdXmlView::write( const QString& fname ) +{ + + QDomDocument xmlDoc( "videocd PUBLIC \"-//GNU//DTD VideoCD//EN\" \"http://www.gnu.org/software/vcdimager/videocd.dtd\"" ); + // xmlDoc.appendChild( xmlDoc.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"iso-8859-1\"" ) ); + xmlDoc.appendChild( xmlDoc.createProcessingInstruction( "xml", "version=\"1.0\"" ) ); + + // create root element + QDomElement root = xmlDoc.createElement( "videocd" ); + root.setAttribute( "xmlns", "http://www.gnu.org/software/vcdimager/1.0/" ); + root.setAttribute( "class", m_doc->vcdOptions() ->vcdClass() ); + root.setAttribute( "version", m_doc->vcdOptions() ->vcdVersion() ); + xmlDoc.appendChild( root ); + + // create option elements + + // Broken SVCD mode - NonCompliantMode + if ( m_doc->vcdOptions() ->NonCompliantMode() ) { + QDomElement elemOption; + elemOption = addSubElement( xmlDoc, root, "option" ); + elemOption.setAttribute( "name", "svcd vcd30 mpegav" ); + elemOption.setAttribute( "value", "true" ); + + elemOption = addSubElement( xmlDoc, root, "option" ); + elemOption.setAttribute( "name", "svcd vcd30 entrysvd" ); + elemOption.setAttribute( "value", "true" ); + } + + // VCD3.0 track interpretation + if ( m_doc->vcdOptions() ->VCD30interpretation() ) { + QDomElement elemOption; + elemOption = addSubElement( xmlDoc, root, "option" ); + elemOption.setAttribute( "name", "svcd vcd30 tracksvd" ); + elemOption.setAttribute( "value", "true" ); + } + + // Relaxed aps + if ( m_doc->vcdOptions() ->RelaxedAps() ) { + QDomElement elemOption; + elemOption = addSubElement( xmlDoc, root, "option" ); + elemOption.setAttribute( "name", "relaxed aps" ); + elemOption.setAttribute( "value", "true" ); + } + + // Update scan offsets + if ( m_doc->vcdOptions() ->UpdateScanOffsets() ) { + QDomElement elemOption; + elemOption = addSubElement( xmlDoc, root, "option" ); + elemOption.setAttribute( "name", "update scan offsets" ); + elemOption.setAttribute( "value", "true" ); + + } + + // Gaps & Margins + if ( m_doc->vcdOptions() ->UseGaps() ) { + QDomElement elemOption; + elemOption = addSubElement( xmlDoc, root, "option" ); + elemOption.setAttribute( "name", "leadout pregap" ); + elemOption.setAttribute( "value", m_doc->vcdOptions() ->PreGapLeadout() ); + + elemOption = addSubElement( xmlDoc, root, "option" ); + elemOption.setAttribute( "name", "track pregap" ); + elemOption.setAttribute( "value", m_doc->vcdOptions() ->PreGapTrack() ); + + if ( m_doc->vcdOptions() ->vcdClass() == "vcd" ) { + elemOption = addSubElement( xmlDoc, root, "option" ); + elemOption.setAttribute( "name", "track front margin" ); + elemOption.setAttribute( "value", m_doc->vcdOptions() ->FrontMarginTrack() ); + + elemOption = addSubElement( xmlDoc, root, "option" ); + elemOption.setAttribute( "name", "track rear margin" ); + elemOption.setAttribute( "value", m_doc->vcdOptions() ->RearMarginTrack() ); + } else { + elemOption = addSubElement( xmlDoc, root, "option" ); + elemOption.setAttribute( "name", "track front margin" ); + elemOption.setAttribute( "value", m_doc->vcdOptions() ->FrontMarginTrackSVCD() ); + + elemOption = addSubElement( xmlDoc, root, "option" ); + elemOption.setAttribute( "name", "track rear margin" ); + elemOption.setAttribute( "value", m_doc->vcdOptions() ->RearMarginTrackSVCD() ); + } + + } + + // create info element + QDomElement elemInfo = addSubElement( xmlDoc, root, "info" ); + addSubElement( xmlDoc, elemInfo, "album-id", m_doc->vcdOptions() ->albumId().upper() ); + addSubElement( xmlDoc, elemInfo, "volume-count", m_doc->vcdOptions() ->volumeCount() ); + addSubElement( xmlDoc, elemInfo, "volume-number", m_doc->vcdOptions() ->volumeNumber() ); + addSubElement( xmlDoc, elemInfo, "restriction", m_doc->vcdOptions() ->Restriction() ); + + // create pvd element + QDomElement elemPvd = addSubElement( xmlDoc, root, "pvd" ); + addSubElement( xmlDoc, elemPvd, "volume-id", m_doc->vcdOptions() ->volumeId().upper() ); + addSubElement( xmlDoc, elemPvd, "system-id", m_doc->vcdOptions() ->systemId() ); + addSubElement( xmlDoc, elemPvd, "application-id", m_doc->vcdOptions() ->applicationId() ); + addSubElement( xmlDoc, elemPvd, "preparer-id", QString( "K3b - Version %1" ).arg( k3bcore->version() ).upper() ); + addSubElement( xmlDoc, elemPvd, "publisher-id", m_doc->vcdOptions() ->publisher().upper() ); + + + // create filesystem element + QDomElement elemFileSystem = addSubElement( xmlDoc, root, "filesystem" ); + + // SEGMENT folder, some standalone DVD-Player need this + if ( !m_doc->vcdOptions() ->haveSegments() && m_doc->vcdOptions() ->SegmentFolder() ) + addFolderElement( xmlDoc, elemFileSystem, "SEGMENT" ); + + // create cdi element + if ( m_doc->vcdOptions() ->CdiSupport() ) { + QDomElement elemFolder = addFolderElement( xmlDoc, elemFileSystem, "CDI" ); + + addFileElement( xmlDoc, elemFolder, locate( "data", "k3b/cdi/cdi_imag.rtf" ), "CDI_IMAG.RTF", true ); + addFileElement( xmlDoc, elemFolder, locate( "data", "k3b/cdi/cdi_text.fnt" ), "CDI_TEXT.FNT" ); + addFileElement( xmlDoc, elemFolder, locate( "data", "k3b/cdi/cdi_vcd.app" ), "CDI_VCD.APP" ); + + QString usercdicfg = locateLocal( "appdata", "cdi/cdi_vcd.cfg" ); + if ( QFile::exists( usercdicfg ) ) + addFileElement( xmlDoc, elemFolder, usercdicfg, "CDI_VCD.CFG" ); + else + addFileElement( xmlDoc, elemFolder, locate( "data", "k3b/cdi/cdi_vcd.cfg" ), "CDI_VCD.CFG" ); + } + + // sequence-items element & segment-items element + QDomElement elemsequenceItems; + QDomElement elemsegmentItems; + + // sequence-item element & segment-item element + QDomElement elemsequenceItem; + QDomElement elemsegmentItem; + + // if we have segments, elemsegmentItems must be before any sequence in xml file order + if ( m_doc->vcdOptions()->haveSegments() ) + elemsegmentItems = addSubElement( xmlDoc, root, "segment-items" ); + + // sequence must always available ... + elemsequenceItems = addSubElement( xmlDoc, root, "sequence-items" ); + // if we have no sequence (photo (s)vcd) we must add a dummy sequence they inform the user to turn on pbc on there videoplayer + if ( !m_doc->vcdOptions()->haveSequence() ) { + QString filename; + if ( m_doc->vcdOptions()->mpegVersion() == 1 ) + filename = locate( "data", "k3b/extra/k3bphotovcd.mpg" ); + else + filename = locate( "data", "k3b/extra/k3bphotosvcd.mpg" ); + + elemsequenceItem = addSubElement( xmlDoc, elemsequenceItems, "sequence-item" ); + elemsequenceItem.setAttribute( "src", QString( "%1" ).arg( QFile::encodeName( filename ) ) ); + elemsequenceItem.setAttribute( "id", "sequence-000" ); + // default entry + QDomElement elemdefaultEntry; + elemdefaultEntry = addSubElement( xmlDoc, elemsequenceItem, "default-entry" ); + elemdefaultEntry.setAttribute( "id", "entry-000" ); + } + + + // pbc + QDomElement elemPbc; + + // Add Tracks to XML + QPtrListIterator<K3bVcdTrack> it( *m_doc->tracks() ); + for ( ; it.current(); ++it ) { + if ( !it.current() ->isSegment() ) { + QString seqId = QString::number( it.current() ->index() ).rightJustify( 3, '0' ); + + elemsequenceItem = addSubElement( xmlDoc, elemsequenceItems, "sequence-item" ); + elemsequenceItem.setAttribute( "src", QString( "%1" ).arg( QFile::encodeName( it.current() ->absPath() ) ) ); + elemsequenceItem.setAttribute( "id", QString( "sequence-%1" ).arg( seqId ) ); + + // default entry + QDomElement elemdefaultEntry; + elemdefaultEntry = addSubElement( xmlDoc, elemsequenceItem, "default-entry" ); + elemdefaultEntry.setAttribute( "id", QString( "entry-%1" ).arg( seqId ) ); + + } else { + // sequence-items element needs at least one segment to fit the XML + elemsegmentItem = addSubElement( xmlDoc, elemsegmentItems, "segment-item" ); + elemsegmentItem.setAttribute( "src", QString( "%1" ).arg( QFile::encodeName( it.current() ->absPath() ) ) ); + elemsegmentItem.setAttribute( "id", QString( "segment-%1" ).arg( QString::number( it.current() ->index() ).rightJustify( 3, '0' ) ) ); + + } + } + for ( it.toFirst(); it.current(); ++it ) { + + if ( m_doc->vcdOptions() ->PbcEnabled() ) { + if ( elemPbc.isNull() ) + elemPbc = addSubElement( xmlDoc, root, "pbc" ); + + doPbc( xmlDoc, elemPbc, it.current() ); + } + } + + if ( ! elemPbc.isNull() ) { + QDomElement elemEndlist = addSubElement( xmlDoc, elemPbc, "endlist" ); + elemEndlist.setAttribute( "id", "end" ); + elemEndlist.setAttribute( "rejected", "true" ); + } + + m_xmlstring = xmlDoc.toString(); + kdDebug() << QString( "(K3bVcdXmlView) Write Data to %1:" ).arg( fname ) << endl; + + QFile xmlFile( fname ); + if ( xmlFile.open( IO_WriteOnly ) ) { + QTextStream ts( & xmlFile ); + ts << m_xmlstring; + xmlFile.close(); + return true; + } + + return false; +} + +void K3bVcdXmlView::addComment( QDomDocument& doc, QDomElement& parent, const QString& text ) +{ + QDomComment comment = doc.createComment( text ); + parent.appendChild( comment ); +} + +QDomElement K3bVcdXmlView::addSubElement( QDomDocument& doc, QDomElement& parent, const QString& name, const QString& value ) +{ + QDomElement element = doc.createElement( name ); + parent.appendChild( element ); + if ( !value.isNull() ) { + QDomText t = doc.createTextNode( value ); + element.appendChild( t ); + } + return element; +} + +QDomElement K3bVcdXmlView::addSubElement( QDomDocument& doc, QDomElement& parent, const QString& name, const int& value ) +{ + QDomElement element = doc.createElement( name ); + parent.appendChild( element ); + if ( value >= -1 ) { + QDomText t = doc.createTextNode( QString( "%1" ).arg( value ) ); + element.appendChild( t ); + } + return element; +} + +QDomElement K3bVcdXmlView::addFolderElement( QDomDocument& doc, QDomElement& parent, const QString& name ) +{ + QDomElement elemFolder = addSubElement( doc, parent, "folder" ); + addSubElement( doc, elemFolder, "name", name ); + + return elemFolder; +} + +void K3bVcdXmlView::addFileElement( QDomDocument& doc, QDomElement& parent, const QString& src, const QString& name, bool mixed ) +{ + QDomElement elemFile = addSubElement( doc, parent, "file" ); + elemFile.setAttribute( "src", QString( "%1" ).arg( src ) ); + if ( mixed ) + elemFile.setAttribute( "format", "mixed" ); + + addSubElement( doc, elemFile, "name", name ); +} + +void K3bVcdXmlView::doPbc( QDomDocument& doc, QDomElement& parent, K3bVcdTrack* track ) +{ + QString ref = ( track->isSegment() ) ? "segment" : "sequence"; + + QDomElement elemSelection = addSubElement( doc, parent, "selection" ); + elemSelection.setAttribute( "id", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( track->index() ).rightJustify( 3, '0' ) ) ); + + setNumkeyBSN( doc, elemSelection, track ); + + for ( int i = 0; i < K3bVcdTrack::_maxPbcTracks; i++ ) { + QDomElement elemPbcSelectionPNRDT; + + if ( track->getPbcTrack( i ) ) { + int index = track->getPbcTrack( i ) ->index(); + QString ref = ( track->getPbcTrack( i ) ->isSegment() ) ? "segment" : "sequence"; + + switch ( i ) { + case K3bVcdTrack::PREVIOUS: + elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "prev" ); + elemPbcSelectionPNRDT.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( index ).rightJustify( 3, '0' ) ) ); + break; + case K3bVcdTrack::NEXT: + elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "next" ); + elemPbcSelectionPNRDT.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( index ).rightJustify( 3, '0' ) ) ); + break; + case K3bVcdTrack::RETURN: + elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "return" ); + elemPbcSelectionPNRDT.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( index ).rightJustify( 3, '0' ) ) ); + break; + case K3bVcdTrack::DEFAULT: + elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "default" ); + elemPbcSelectionPNRDT.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( index ).rightJustify( 3, '0' ) ) ); + break; + case K3bVcdTrack::AFTERTIMEOUT: + if ( track->getWaitTime() >= 0 ) { + elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "timeout" ); + elemPbcSelectionPNRDT.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( index ).rightJustify( 3, '0' ) ) ); + } + break; + } + } else { + // jump to <endlist> otherwise do noop while disabled + if ( track->getNonPbcTrack( i ) == K3bVcdTrack::VIDEOEND ) { + switch ( i ) { + case K3bVcdTrack::PREVIOUS: + elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "prev" ); + elemPbcSelectionPNRDT.setAttribute( "ref", "end" ); + break; + case K3bVcdTrack::NEXT: + elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "next" ); + elemPbcSelectionPNRDT.setAttribute( "ref", "end" ); + break; + case K3bVcdTrack::RETURN: + elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "return" ); + elemPbcSelectionPNRDT.setAttribute( "ref", "end" ); + break; + case K3bVcdTrack::DEFAULT: + elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "default" ); + elemPbcSelectionPNRDT.setAttribute( "ref", "end" ); + break; + case K3bVcdTrack::AFTERTIMEOUT: + if ( track->getWaitTime() >= 0 ) { + elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "timeout" ); + elemPbcSelectionPNRDT.setAttribute( "ref", "end" ); + } + break; + } + } + } + } + + addSubElement( doc, elemSelection, "wait", track->getWaitTime() ); + QDomElement loop = addSubElement( doc, elemSelection, "loop", track->getPlayTime() ); + if ( track->Reactivity() ) + loop.setAttribute( "jump-timing", "delayed" ); + else + loop.setAttribute( "jump-timing", "immediate" ); + + addSubElement( doc, elemSelection, "play-item" ).setAttribute( "ref", QString( "%1-%2" ).arg( ref ).arg( QString::number( track->index() ).rightJustify( 3, '0' ) ) ); + + setNumkeySEL( doc, elemSelection, track ); +} + +void K3bVcdXmlView::setNumkeyBSN( QDomDocument& doc, QDomElement& parent, K3bVcdTrack* track ) +{ + if ( track->PbcNumKeys() ) { + if ( track->PbcNumKeysUserdefined() ) { + QMap<int, K3bVcdTrack*> numKeyMap = track->DefinedNumKey(); + QMap<int, K3bVcdTrack*>::const_iterator trackIt; + + m_startkey = 0; + trackIt = numKeyMap.begin(); + if ( trackIt != numKeyMap.end() ) + m_startkey = trackIt.key(); + + if ( m_startkey > 0 ) + addSubElement( doc, parent, "bsn", m_startkey ); + else // user has no numKeys defined for this track + track->setPbcNumKeys( false ); + + } else { + // default start with key #1 + addSubElement( doc, parent, "bsn", 1 ); + } + } +} + +void K3bVcdXmlView::setNumkeySEL( QDomDocument& doc, QDomElement& parent, K3bVcdTrack* track ) +{ + if ( track->PbcNumKeys() ) { + QDomElement elemPbcSelectionNumKeySEL; + QString ref = ( track->isSegment() ) ? "segment" : "sequence"; + int none = m_startkey; + if ( track->PbcNumKeysUserdefined() ) { + QMap<int, K3bVcdTrack*> numKeyMap = track->DefinedNumKey(); + QMap<int, K3bVcdTrack*>::const_iterator trackIt; + + for ( trackIt = numKeyMap.begin(); trackIt != numKeyMap.end(); ++trackIt ) { + + kdDebug() << QString( "trackIt key: %1 none: %2" ).arg( trackIt.key() ).arg( none ) << endl; + while ( none < trackIt.key() ) { + elemPbcSelectionNumKeySEL = addSubElement( doc, parent, "select" ); + elemPbcSelectionNumKeySEL.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( track->index() ).rightJustify( 3, '0' ) ) ); + addComment( doc, parent, QString( "key %1 -> %2 (normal none)" ).arg( none ).arg( QFile::encodeName( track->absPath() ) ) ); + none++; + } + + if ( trackIt.data() ) { + QString ref = ( trackIt.data() ->isSegment() ) ? "segment" : "sequence"; + elemPbcSelectionNumKeySEL = addSubElement( doc, parent, "select" ); + elemPbcSelectionNumKeySEL.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( trackIt.data() ->index() ).rightJustify( 3, '0' ) ) ); + addComment( doc, parent, QString( "key %1 -> %2" ).arg( trackIt.key() ).arg( QFile::encodeName( trackIt.data() ->absPath() ) ) ); + } else { + elemPbcSelectionNumKeySEL = addSubElement( doc, parent, "select" ); + elemPbcSelectionNumKeySEL.setAttribute( "ref", "end" ); + addComment( doc, parent, QString( "key %1 -> end" ).arg( trackIt.key() ) ); + } + none++; + } + } else { + // default reference to itSelf + elemPbcSelectionNumKeySEL = addSubElement( doc, parent, "select" ); + elemPbcSelectionNumKeySEL.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( track->index() ).rightJustify( 3, '0' ) ) ); + } + } +} + diff --git a/libk3b/projects/videocd/k3bvcdxmlview.h b/libk3b/projects/videocd/k3bvcdxmlview.h new file mode 100644 index 0000000..d99549b --- /dev/null +++ b/libk3b/projects/videocd/k3bvcdxmlview.h @@ -0,0 +1,59 @@ +/* +* +* $Id: k3bvcdxmlview.h 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003-2004 Christian Kvasny <[email protected]> +* THX to Manfred Odenstein <[email protected]> +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> +* +* This program 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 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + +#ifndef K3B_VCD_XMLVIEW_H +#define K3B_VCD_XMLVIEW_H + +#include <qstring.h> +#include <qdom.h> +#include <ktempfile.h> + +#include <k3bvcddoc.h> + +class K3bVcdOptions; +class K3bVcdTrack; + + +class K3bVcdXmlView +{ + + public: + K3bVcdXmlView( K3bVcdDoc* ); + ~K3bVcdXmlView(); + + bool write( const QString& ); + QString xmlString() + { + return m_xmlstring; + } + + private: + QString m_xmlstring; + + void addComment( QDomDocument& doc, QDomElement& parent, const QString& text ); + QDomElement addSubElement( QDomDocument&, QDomElement&, const QString& name, const QString& value = QString::null ); + QDomElement addSubElement( QDomDocument&, QDomElement&, const QString& name, const int& value ); + + QDomElement addFolderElement( QDomDocument&, QDomElement&, const QString& name ); + void addFileElement( QDomDocument&, QDomElement&, const QString& src, const QString& name, bool mixed = false ); + void doPbc( QDomDocument&, QDomElement&, K3bVcdTrack* ); + void setNumkeyBSN( QDomDocument& , QDomElement&, K3bVcdTrack* ); + void setNumkeySEL( QDomDocument& , QDomElement&, K3bVcdTrack* ); + K3bVcdDoc* m_doc; + int m_startkey; +}; + +#endif diff --git a/libk3b/projects/videocd/mpeginfo/Makefile.am b/libk3b/projects/videocd/mpeginfo/Makefile.am new file mode 100644 index 0000000..af1b06b --- /dev/null +++ b/libk3b/projects/videocd/mpeginfo/Makefile.am @@ -0,0 +1,5 @@ +INCLUDES = $(all_includes) +noinst_LTLIBRARIES = libmpeginfo.la + +libmpeginfo_la_SOURCES = k3bmpeginfo.cpp + diff --git a/libk3b/projects/videocd/mpeginfo/k3bmpeginfo.cpp b/libk3b/projects/videocd/mpeginfo/k3bmpeginfo.cpp new file mode 100644 index 0000000..583a0aa --- /dev/null +++ b/libk3b/projects/videocd/mpeginfo/k3bmpeginfo.cpp @@ -0,0 +1,844 @@ +/* +* +* $Id: k3bmpeginfo.cpp 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003-2004 Christian Kvasny <[email protected]> +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> +* +* This program 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 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + +// kde includes +#include <klocale.h> + +// k3b includes +#include "k3bmpeginfo.h" + +static const double frame_rates[ 16 ] = + { + 0.0, 24000.0 / 1001, 24.0, 25.0, + 30000.0 / 1001, 30.0, 50.0, 60000.0 / 1001, + 60.0, 0.0, + }; + +K3bMpegInfo::K3bMpegInfo( const char* filename ) + : m_mpegfile( 0 ), + m_filename( filename ), + m_done( false ), + m_buffstart( 0 ), + m_buffend( 0 ), + m_buffer( 0 ), + m_initial_TS( 0.0 ) +{ + + mpeg_info = new Mpeginfo(); + + m_mpegfile = fopen( filename, "rb" ); + + if ( m_mpegfile == 0 ) { + kdDebug() << QString( "Unable to open %1" ).arg( m_filename ) << endl; + return ; + } + + if ( fseeko( m_mpegfile, 0, SEEK_END ) ) { + kdDebug() << QString( "Unable to seek in file %1" ).arg( m_filename ) << endl; + return ; + } + + llong lof = ftello( m_mpegfile ); + + if ( lof == -1 ) { + kdDebug() << QString( "Seeking to end of input file %1 failed." ).arg( m_filename ) << endl; + //give up.. + return ; + } else + m_filesize = lof; + + // nothing to do on an empty file + if ( !m_filesize ) { + kdDebug() << QString( "File %1 is empty." ).arg( m_filename ) << endl; + m_error_string = i18n( "File %1 is empty." ).arg( m_filename ); + return ; + } + + m_buffer = new byte[ BUFFERSIZE ]; + + MpegParsePacket ( ); + +} + +K3bMpegInfo::~K3bMpegInfo() +{ + if ( m_buffer ) { + delete[] m_buffer; + } + if ( m_mpegfile ) { + fclose( m_mpegfile ); + } + + delete mpeg_info; +} +bool K3bMpegInfo::MpegParsePacket () +{ + + /* verify the packet begins with a pack header */ + if ( !EnsureMPEG( 0, MPEG_PACK_HEADER_CODE ) ) { + llong code = GetNBytes( 0, 4 ); + + kdDebug() << QString( "(K3bMpegInfo::mpeg_parse_packet ()) pack header code 0x%1 expected, but 0x%2 found" ).arg( 0x00000100 + MPEG_PACK_HEADER_CODE, 0, 16 ).arg( code, 0, 16 ) << endl; + + if ( code == 0x00000100 + MPEG_SEQUENCE_CODE ) { + kdDebug() << "...this looks like a elementary video stream but a multiplexed program stream was required." << endl; + m_error_string = i18n( "This looks like a elementary video stream but a multiplexed program stream was required." ); + } + + if ( ( 0xfff00000 & code ) == 0xfff00000 ) { + kdDebug() << "...this looks like a elementary audio stream but a multiplexed program stream was required." << endl; + m_error_string = i18n( "This looks like a elementary audio stream but a multiplexed program stream was required." ); + } + + if ( code == 0x52494646 ) { + kdDebug() << "...this looks like a RIFF header but a plain multiplexed program stream was required." << endl; + m_error_string = i18n( "This looks like a RIFF header but a plain multiplexed program stream was required." ); + } + + return false; + } + + + /* take a look at the pack header */ + int offset = 0; + while ( GetByte( offset ) == 0x00 ) + offset ++; + //here we're on the first non null byte let's get back to leave two zeros (packet start code) + offset -= 2; + + if ( offset != 0 ) { + // we actually skipped some zeroes + kdDebug() << QString( "Skipped %1 zeroes at start of file" ).arg( offset ) << endl; + } + + // here while schleife + while ( offset != -1 ) { + offset = MpegParsePacket( offset ); + } + + /* + int pkt = 0; + offset = FindNextMarker( 0, MPEG_PACK_HEADER_CODE ); + + while ( offset != -1 ) { + pkt++; + offset = FindNextMarker( offset+1, MPEG_PACK_HEADER_CODE ); + } + + kdDebug() << "Pkt found: " << pkt << endl; + */ + + //seek the file duration by fetching the last PACK + //and reading its timestamp + llong last_pack = bdFindNextMarker( m_filesize - 13, MPEG_PACK_HEADER_CODE ); + // -12 because a PACK is at least 12 bytes + double duration; + last_pack += 4; + int bits = GetByte( last_pack ) >> 4; + + if ( bits == 0x2 ) /* %0010 ISO11172-1 */ + { + duration = ReadTS( last_pack ); + } else if ( bits >> 2 == 0x1 ) /* %01xx ISO13818-1 */ + { + duration = ReadTSMpeg2( last_pack ); + } else { + kdDebug() << QString( "no timestamp found" ) << endl; + duration = ReadTS( last_pack ); + } + + mpeg_info->playing_time = duration - m_initial_TS; + + + if ( !mpeg_info->has_video ) + for ( int i = 0; i < 2; i++ ) + if ( mpeg_info->video[ i ].seen ) + mpeg_info->has_video = true; + + if ( !mpeg_info->has_audio ) + for ( int i = 0; i < 2; i++ ) + if ( mpeg_info->audio[ i ].seen ) + mpeg_info->has_audio = true; + + return true; +} + +llong K3bMpegInfo::MpegParsePacket ( llong offset ) +{ + byte mark = 0; + uint size = 0; + + /* continue until start code seen */ + offset = FindNextMarker( offset, &mark ); + + if ( offset < 0 ) + return offset; + + switch ( mark ) { + int bits; + + case MPEG_PACK_HEADER_CODE: + // kdDebug() << QString( "MPEG_PACK_HEADER_CODE @ %1" ).arg( offset ) << endl; + + offset += 4; + + if ( mpeg_info->version != MPEG_VERS_INVALID ) + break; + + bits = GetByte( offset ) >> 4; + + if ( bits == 0x2 ) /* %0010 ISO11172-1 */ + { + mpeg_info->version = MPEG_VERS_MPEG1; + + unsigned long muxrate = 0; + + muxrate = ( GetByte( offset + 5 ) & 0x7F ) << 15; + muxrate |= ( GetByte( offset + 6 ) << 7 ); + muxrate |= ( GetByte( offset + 7 ) >> 1 ); + + mpeg_info->muxrate = muxrate * 50 * 8; + + if ( m_initial_TS == 0.0 ) + { + m_initial_TS = ReadTS( offset ); + kdDebug() << QString( "Initial TS = %1" ).arg( m_initial_TS ) << endl; + } + + } else if ( bits >> 2 == 0x1 ) /* %01xx ISO13818-1 */ + { + mpeg_info->version = MPEG_VERS_MPEG2; + + unsigned long muxrate = 0; + muxrate = GetByte( offset + 6 ) << 14; + muxrate |= GetByte( offset + 7 ) << 6; + muxrate |= GetByte( offset + 8 ) >> 2; + + mpeg_info->muxrate = muxrate * 50 * 8; + + if ( m_initial_TS == 0.0 ) + { + m_initial_TS = ReadTSMpeg2( offset ); + kdDebug() << QString( "Initial TS = %1" ).arg( m_initial_TS ) << endl; + } + + } else { + kdDebug() << QString( "packet not recognized as either version 1 or 2 (%1)" ).arg( bits ) << endl; + mpeg_info->version = MPEG_VERS_INVALID; + return -1; + } + break; + + case MPEG_SYSTEM_HEADER_CODE: + case MPEG_PAD_CODE: + case MPEG_PRIVATE_1_CODE: + case MPEG_VIDEO_E0_CODE: + case MPEG_VIDEO_E1_CODE: + case MPEG_VIDEO_E2_CODE: + case MPEG_AUDIO_C0_CODE: + case MPEG_AUDIO_C1_CODE: + case MPEG_AUDIO_C2_CODE: + + offset += 4; + size = GetSize( offset ); + offset += 2; + // kdDebug() << QString( "offset = %1, size = %2" ).arg( offset ).arg( size ) << endl; + + switch ( mark ) { + case MPEG_SYSTEM_HEADER_CODE: + // kdDebug() << QString( "Systemheader: %1" ).arg( m_code, 0, 16 ) << endl; + break; + + case MPEG_VIDEO_E0_CODE: + case MPEG_VIDEO_E1_CODE: + case MPEG_VIDEO_E2_CODE: + ParseVideo( offset, mark ); + // _analyze_video_pes (code & 0xff, buf + pos, size, !parse_pes, ctx); + if ( mpeg_info->has_video && mpeg_info->has_audio ) { + return -1; + } else if ( mark == MPEG_VIDEO_E0_CODE || mpeg_info->version == MPEG_VERS_MPEG2 && mark == MPEG_VIDEO_E1_CODE || mpeg_info->version == MPEG_VERS_MPEG1 && mark == MPEG_VIDEO_E2_CODE ) { + mpeg_info->has_video = true; + offset = FindNextAudio( offset ); + } + break; + case MPEG_AUDIO_C0_CODE: + case MPEG_AUDIO_C1_CODE: + case MPEG_AUDIO_C2_CODE: + offset = SkipPacketHeader( offset - 6 ); + ParseAudio( offset, mark ); + // audio packet doesn't begin with 0xFFF + if ( !mpeg_info->audio[ GetAudioIdx( mark ) ].seen ) { + int a_idx = GetAudioIdx( mark ); + while ( ( offset < m_filesize - 10 ) && !mpeg_info->audio[ a_idx ].seen ) { + if ( ( GetByte( offset ) == 0xFF ) && ( GetByte( offset + 1 ) & 0xF0 ) == 0xF0 ) + ParseAudio( offset, mark ); + offset++; + } + } + + mpeg_info->has_audio = true; + if ( mpeg_info->has_video ) + return -1; + + offset = FindNextVideo( offset ); + break; + + case MPEG_PRIVATE_1_CODE: + kdDebug() << QString( "PrivateCode: %1" ).arg( mark, 0, 16 ) << endl; + break; + } + break; + + case MPEG_PROGRAM_END_CODE: + kdDebug() << QString( "ProgramEndCode: %1" ).arg( mark, 0, 16 ) << endl; + offset += 4; + break; + + case MPEG_PICTURE_CODE: + kdDebug() << QString( "PictureCode: %1" ).arg( mark, 0, 16 ) << endl; + offset += 3; + break; + + default: + offset += 4; + break; + } + + return offset; +} + +byte K3bMpegInfo::GetByte( llong offset ) +{ + unsigned long nread; + if ( ( offset >= m_buffend ) || ( offset < m_buffstart ) ) { + + if ( fseeko( m_mpegfile, offset, SEEK_SET ) ) { + kdDebug() << QString( "could not get seek to offset (%1) in file %2 (size:%3)" ).arg( offset ).arg( m_filename ).arg( m_filesize ) << endl; + return 0x11; + } + nread = fread( m_buffer, 1, BUFFERSIZE, m_mpegfile ); + m_buffstart = offset; + m_buffend = offset + nread; + if ( ( offset >= m_buffend ) || ( offset < m_buffstart ) ) { + // weird + kdDebug() << QString( "could not get offset %1 in file %2 [%3]" ).arg( offset ).arg( m_filename ).arg( m_filesize ) << endl; + return 0x11; + } + } + return m_buffer[ offset - m_buffstart ]; +} + +// same as above but improved for backward search +byte K3bMpegInfo::bdGetByte( llong offset ) +{ + unsigned long nread; + if ( ( offset >= m_buffend ) || ( offset < m_buffstart ) ) { + llong start = offset - BUFFERSIZE + 1 ; + start = start >= 0 ? start : 0; + + fseeko( m_mpegfile, start, SEEK_SET ); + + nread = fread( m_buffer, 1, BUFFERSIZE, m_mpegfile ); + m_buffstart = start; + m_buffend = start + nread; + if ( ( offset >= m_buffend ) || ( offset < m_buffstart ) ) { + // weird + kdDebug() << QString( "could not get offset %1 in file %2 [%3]" ).arg( offset ).arg( m_filename ).arg( m_filesize ) << endl; + + return 0x11; + } + } + return m_buffer[ offset - m_buffstart ]; +} + + +llong K3bMpegInfo::GetNBytes( llong offset, int n ) +{ + llong nbytes = 0; + n--; + for ( int i = 0; i < n; i++ ) + ( ( char* ) & nbytes ) [ n - i ] = GetByte( offset + i ); + + return nbytes; + +} + +// get a two byte size +unsigned short int K3bMpegInfo::GetSize( llong offset ) +{ + return GetByte( offset ) * 256 + GetByte( offset + 1 ); + // return GetNBytes( offset, 2 ); + +} + +bool K3bMpegInfo::EnsureMPEG( llong offset, byte mark ) +{ + if ( ( GetByte( offset ) == 0x00 ) && + ( GetByte( offset + 1 ) == 0x00 ) && + ( GetByte( offset + 2 ) == 0x01 ) && + ( GetByte( offset + 3 ) == mark ) ) + return true; + else + return false; +} + + +// find next 0x 00 00 01 xx sequence, returns offset or -1 on err +llong K3bMpegInfo::FindNextMarker( llong from ) +{ + llong offset; + for ( offset = from; offset < ( m_filesize - 4 ); offset++ ) { + if ( + ( GetByte( offset + 0 ) == 0x00 ) && + ( GetByte( offset + 1 ) == 0x00 ) && + ( GetByte( offset + 2 ) == 0x01 ) ) { + return offset; + } + } + return -1; +} + +// find next 0x 00 00 01 xx sequence, returns offset or -1 on err and +// change mark to xx +llong K3bMpegInfo::FindNextMarker( llong from, byte* mark ) +{ + llong offset = FindNextMarker( from ); + if ( offset >= 0 ) { + *mark = GetByte( offset + 3 ); + return offset; + } else { + return -1; + } +} + +// find next 0X00 00 01 mark +llong K3bMpegInfo::FindNextMarker( llong from, byte mark ) +{ + llong offset = from; + while ( offset >= 0 ) { + offset = FindNextMarker( offset ); + if ( offset < 0 ) { + return -1; + } + if ( EnsureMPEG( offset, mark ) ) { + return offset; + } else + offset++; + } + + //shouldn't be here + return -1; +} + +llong K3bMpegInfo::bdFindNextMarker( llong from, byte mark ) +{ + llong offset; + for ( offset = from; offset >= 0; offset-- ) { + if ( + ( bdGetByte( offset ) == 0x00 ) && + ( bdGetByte( offset + 1 ) == 0x00 ) && + ( bdGetByte( offset + 2 ) == 0x01 ) && + ( bdGetByte( offset + 3 ) == mark ) ) { + return offset; + } + } + return -1; +} + +llong K3bMpegInfo::bdFindNextMarker( llong from, byte* mark ) +{ + llong offset; + for ( offset = from; offset >= 0; offset-- ) { + if ( ( bdGetByte( offset ) == 0x00 ) && + ( bdGetByte( offset + 1 ) == 0x00 ) && + ( bdGetByte( offset + 2 ) == 0x01 ) ) { + *mark = bdGetByte( offset + 3 ); + return offset; + } + } + return -1; + +} + +llong K3bMpegInfo::FindNextVideo( llong from ) +{ + llong offset = from; + while ( offset >= 0 ) { + offset = FindNextMarker( offset ); + if ( offset < 0 ) { + return -1; + } + if ( EnsureMPEG( offset, MPEG_VIDEO_E0_CODE ) || EnsureMPEG( offset, MPEG_VIDEO_E1_CODE ) || EnsureMPEG( offset, MPEG_VIDEO_E2_CODE ) ) { + return offset; + } else + offset++; + } + + //shouldn't be here + return -1; +} + +llong K3bMpegInfo::FindNextAudio( llong from ) +{ + llong offset = from; + while ( offset >= 0 ) { + offset = FindNextMarker( offset ); + if ( offset < 0 ) { + return -1; + } + if ( EnsureMPEG( offset, MPEG_AUDIO_C0_CODE ) || EnsureMPEG( offset, MPEG_AUDIO_C1_CODE ) || EnsureMPEG( offset, MPEG_AUDIO_C2_CODE ) ) { + return offset; + } else + offset++; + } + + return -1; +} + + +int K3bMpegInfo::GetVideoIdx ( byte marker ) +{ + switch ( marker ) { + case MPEG_VIDEO_E0_CODE: + return 0; + break; + + case MPEG_VIDEO_E1_CODE: + return 1; + break; + + case MPEG_VIDEO_E2_CODE: + return 2; + break; + + default: + kdDebug() << "VideoCode not reached" << endl; + break; + } + + return -1; +} + +int K3bMpegInfo::GetAudioIdx ( byte marker ) +{ + switch ( marker ) { + case MPEG_AUDIO_C0_CODE: + return 0; + break; + + case MPEG_AUDIO_C1_CODE: + return 1; + break; + + case MPEG_AUDIO_C2_CODE: + return 2; + break; + + default: + kdDebug() << "VideoCode not reached" << endl; + break; + } + + return -1; +} + +llong K3bMpegInfo::SkipPacketHeader( llong offset ) +{ + byte tmp_byte; + if ( mpeg_info->version == MPEG_VERS_MPEG1 ) { + // skip startcode and packet size + offset += 6; + //remove stuffing bytes + tmp_byte = GetByte( offset ); + while ( tmp_byte & 0x80 ) + tmp_byte = GetByte( ++offset ); + + if ( ( tmp_byte & 0xC0 ) == 0x40 ) // next two bits are 01 + offset += 2; + + tmp_byte = GetByte( offset ); + if ( ( tmp_byte & 0xF0 ) == 0x20 ) + offset += 5; + else if ( ( tmp_byte & 0xF0 ) == 0x30 ) + offset += 10; + else + offset++; + + return offset; + } else if ( mpeg_info->version == MPEG_VERS_MPEG2 ) { + return ( offset + 9 + GetByte( offset + 8 ) ); + } else + return ( offset + 10 ); +} + +void K3bMpegInfo::ParseAudio ( llong offset, byte marker ) +{ + unsigned brate, srate; + bool mpeg2_5 = false; + + const int a_idx = GetAudioIdx( marker ); + + if ( mpeg_info->audio[ a_idx ].seen ) /* we have it already */ + return ; + + if ( ( GetByte( offset ) != 0xFF ) || ( ( GetByte( offset + 1 ) & 0xF0 ) != 0xF0 ) ) { + // doesn't start with 12 bits set + if ( ( GetByte( offset ) != 0xFF ) || ( ( GetByte( offset + 1 ) & 0xE0 ) != 0xE0 ) ) { + // doesn't start with 11 bits set + return ; + } else { + // start with 11 bits set + mpeg2_5 = true; + } + } + + // Find mpeg version 1.0 or 2.0 + if ( GetByte( offset + 1 ) & 0x08 ) { + if ( !mpeg2_5 ) + mpeg_info->audio[ a_idx ].version = 1; + else + return ; // invalid 01 encountered + } else { + if ( !mpeg2_5 ) + mpeg_info->audio[ a_idx ].version = 2; + else + mpeg_info->audio[ a_idx ].version = 3; //for mpeg 2.5 + } + + // Find Layer + mpeg_info->audio[ a_idx ].layer = ( GetByte( offset + 1 ) & 0x06 ) >> 1; + switch ( mpeg_info->audio[ a_idx ].layer ) { + case 0: + mpeg_info->audio[ a_idx ].layer = 0; + break; + case 1: + mpeg_info->audio[ a_idx ].layer = 3; + break; + case 2: + mpeg_info->audio[ a_idx ].layer = 2; + break; + case 3: + mpeg_info->audio[ a_idx ].layer = 1; + break; + } + + // Protection Bit + mpeg_info->audio[ a_idx ].protect = GetByte( offset + 1 ) & 0x01; + if ( mpeg_info->audio[ a_idx ].protect ) + mpeg_info->audio[ a_idx ].protect = 0; + else + mpeg_info->audio[ a_idx ].protect = 1; + + const unsigned bit_rates[ 4 ][ 16 ] = { + { + 0, + }, + {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0}, + {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0}, + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0} + }; + + + /* const unsigned bit_rates [ 3 ][ 3 ][ 16 ] = { + { + {0, }, + {0, }, + {0, }, + }, + { + {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0}, + {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0}, + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0} + }, + { + {0, 32, 48, 56, 64, 80 , 96 , 112, 128, 144, 160, 176, 192, 224, 256, 0}, + {0, 8, 16, 24, 32, 40, 48, 56, 64 , 80 , 96 , 112, 128, 144, 160, 0}, + {0, 8, 16, 24, 32, 40, 48, 56, 64 , 80 , 96 , 112, 128, 144, 160, 0} + } + }; + */ + + const unsigned sampling_rates[ 4 ][ 4 ] = { + { + 0, + }, + {44100, 48000, 32000, 0}, //mpeg 1 + {22050, 24000, 16000, 0}, //mpeg 2 + {11025, 12000, 8000, 0} //mpeg 2.5 + }; + + + // Bitrate index and sampling index to pass through the array + brate = GetByte( offset + 2 ) >> 4; + srate = ( GetByte( offset + 2 ) & 0x0f ) >> 2; + + mpeg_info->audio[ a_idx ].bitrate = 1024 * bit_rates[ mpeg_info->audio[ a_idx ].layer ][ brate ]; + mpeg_info->audio[ a_idx ].byterate = ( float ) ( mpeg_info->audio[ a_idx ].bitrate / 8.0 ); + mpeg_info->audio[ a_idx ].sampfreq = sampling_rates[ mpeg_info->audio[ a_idx ].version ][ srate ]; + + // Audio mode + mpeg_info->audio[ a_idx ].mode = 1 + ( GetByte( offset + 3 ) >> 6 ) ; + + // Copyright bit + if ( GetByte( offset + 3 ) & 0x08 ) + mpeg_info->audio[ a_idx ].copyright = true; + else + mpeg_info->audio[ a_idx ].copyright = false; + + // Original/Copy bit + if ( GetByte( offset + 3 ) & 0x04 ) + mpeg_info->audio[ a_idx ].original = true; + else + mpeg_info->audio[ a_idx ].original = false; + + + mpeg_info->audio[ a_idx ].seen = true; +} + +void K3bMpegInfo::ParseVideo ( llong offset, byte marker ) +{ + unsigned long aratio, frate, brate; + + const int v_idx = GetVideoIdx( marker ); + + const double aspect_ratios[ 16 ] = { + 0.0000, 1.0000, 0.6735, 0.7031, + 0.7615, 0.8055, 0.8437, 0.8935, + 0.9375, 0.9815, 1.0255, 1.0695, + 1.1250, 1.1575, 1.2015, 0.0000 + }; + + if ( mpeg_info->video[ v_idx ].seen ) /* we have it already */ + return ; + + offset = FindNextMarker( offset + 1, MPEG_SEQUENCE_CODE ); + + if ( !offset ) + return ; + + offset += 4; + + mpeg_info->video[ v_idx ].hsize = GetSize( offset ) >> 4; + mpeg_info->video[ v_idx ].vsize = GetSize( offset + 1 ) & 0x0FFF; + + // Get picture rate + offset += 3; // after picture sizes + + aratio = ( GetByte( offset ) & 0x0F ) >> 4; + mpeg_info->video[ v_idx ].aratio = aspect_ratios[ aratio ]; + + // offset += 3; // after picture sizes + frate = GetByte( offset ) & 0x0F; + mpeg_info->video[ v_idx ].frate = frame_rates[ frate ]; + + offset += 1; // after picture rate + + // 18 following bytes are the bitrate /400 + + //read first 16 bytes + brate = GetSize( offset ); + // scale + brate <<= 2; + byte lasttwo = GetByte( offset + 2 ); + lasttwo >>= 6; + brate |= lasttwo; + + mpeg_info->video[ v_idx ].bitrate = 400 * brate; + + byte mark; + while ( true ) { + offset = FindNextMarker( offset, &mark ); + if ( mark == MPEG_GOP_CODE ) + break; + switch ( GetByte( offset + 3 ) ) { + case MPEG_EXT_CODE : + // Extension + offset += 4; + switch ( GetByte( offset ) >> 4 ) { + case 1: + //SequenceExt + if ( GetByte( offset + 1 ) & 0x08 ) + mpeg_info->video[ v_idx ].progressive = true; + mpeg_info->video[ v_idx ].chroma_format = ( GetByte( offset + 1 ) & 0x06 ) >> 1; + break; + case 2: + // SequenceDisplayExt + mpeg_info->video[ v_idx ].video_format = ( GetByte( offset ) & 0x0E ) >> 1; + break; + } + + break; + case MPEG_USER_CODE : + // UserData + break; + } + offset++; + } + + mpeg_info->video[ v_idx ].seen = true; +} + +double K3bMpegInfo::ReadTS( llong offset ) +{ + byte highbit; + unsigned long low4Bytes; + double TS; + + highbit = ( GetByte( offset ) >> 3 ) & 0x01; + + low4Bytes = ( ( GetByte( offset ) >> 1 ) & 0x03 ) << 30; + low4Bytes |= GetByte( offset + 1 ) << 22; + low4Bytes |= ( GetByte( offset + 2 ) >> 1 ) << 15; + low4Bytes |= GetByte( offset + 3 ) << 7; + low4Bytes |= GetByte( offset + 4 ) >> 1; + + + TS = ( double ) ( highbit * FLOAT_0x10000 * FLOAT_0x10000 ); + TS += ( double ) ( low4Bytes ); + TS /= ( double ) ( STD_SYSTEM_CLOCK_FREQ ); + + return TS; +} + +double K3bMpegInfo::ReadTSMpeg2( llong offset ) +{ + byte highbit; + unsigned long low4Bytes; + unsigned long sys_clock_ref; + double TS; + + highbit = ( GetByte( offset ) & 0x20 ) >> 5; + + low4Bytes = ( ( GetByte( offset ) & 0x18 ) >> 3 ) << 30; + low4Bytes |= ( GetByte( offset ) & 0x03 ) << 28; + low4Bytes |= GetByte( offset + 1 ) << 20; + low4Bytes |= ( GetByte( offset + 2 ) & 0xF8 ) << 12; + low4Bytes |= ( GetByte( offset + 2 ) & 0x03 ) << 13; + low4Bytes |= GetByte( offset + 3 ) << 5; + low4Bytes |= ( GetByte( offset + 4 ) ) >> 3; + + sys_clock_ref = ( GetByte( offset + 4 ) & 0x3 ) << 7; + sys_clock_ref |= ( GetByte( offset + 5 ) >> 1 ); + + TS = ( double ) ( highbit * FLOAT_0x10000 * FLOAT_0x10000 ); + TS += ( double ) ( low4Bytes ); + if ( sys_clock_ref == 0 ) + TS /= ( double ) ( STD_SYSTEM_CLOCK_FREQ ); + else { + TS /= ( double ) ( 27000000 / sys_clock_ref ); + } + + return TS; +} diff --git a/libk3b/projects/videocd/mpeginfo/k3bmpeginfo.h b/libk3b/projects/videocd/mpeginfo/k3bmpeginfo.h new file mode 100644 index 0000000..3436214 --- /dev/null +++ b/libk3b/projects/videocd/mpeginfo/k3bmpeginfo.h @@ -0,0 +1,178 @@ +/* +* +* $Id: k3bmpeginfo.h 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003-2004 Christian Kvasny <[email protected]> +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> +* +* This program 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 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + +#ifndef K3BMPEGINFO +#define K3BMPEGINFO + +#include <stdio.h> + +// #define BUFFERSIZE 16384 +#define BUFFERSIZE 65536 + +#define MPEG_START_CODE_PATTERN ((ulong) 0x00000100) +#define MPEG_START_CODE_MASK ((ulong) 0xffffff00) + +#define MPEG_PICTURE_CODE ((ulong) 0x00000100) +/* [...slice codes... 0x1a7] */ + +#define MPEG_USER_CODE ((uchar) 0xb2) +#define MPEG_SEQUENCE_CODE ((uchar) 0xb3) +#define MPEG_EXT_CODE ((uchar) 0xb5) +#define MPEG_SEQ_END_CODE ((uchar) 0xb7) +#define MPEG_GOP_CODE ((uchar) 0xb8) +#define MPEG_PROGRAM_END_CODE ((uchar) 0xb9) +#define MPEG_PACK_HEADER_CODE ((uchar) 0xba) +#define MPEG_SYSTEM_HEADER_CODE ((uchar) 0xbb) +#define MPEG_PRIVATE_1_CODE ((uchar) 0xbd) +#define MPEG_PAD_CODE ((uchar) 0xbe) + +#define MPEG_AUDIO_C0_CODE ((uchar) 0xc0) /* default */ +#define MPEG_AUDIO_C1_CODE ((uchar) 0xc1) /* 2nd audio stream id (dual channel) */ +#define MPEG_AUDIO_C2_CODE ((uchar) 0xc2) /* 3rd audio stream id (surround sound) */ + +#define MPEG_VIDEO_E0_CODE ((uchar) 0xe0) /* motion */ +#define MPEG_VIDEO_E1_CODE ((uchar) 0xe1) /* lowres still */ +#define MPEG_VIDEO_E2_CODE ((uchar) 0xe2) /* hires still */ + +#define FLOAT_0x10000 (double)((unsigned long)1 << 16) +#define STD_SYSTEM_CLOCK_FREQ (unsigned long)90000 + +typedef unsigned char byte; +typedef long long llong; + +#include <kdebug.h> + +class video_info +{ + public: + bool seen; + unsigned long hsize; + unsigned long vsize; + double aratio; + double frate; + unsigned long bitrate; + unsigned long vbvsize; + bool progressive; + unsigned char video_format; + unsigned char chroma_format; + bool constrained_flag; +}; + +class audio_info +{ + public: + bool seen; + unsigned int version; + unsigned int layer; + unsigned int protect; + unsigned long bitrate; + float byterate; + unsigned long sampfreq; + int mode; + bool copyright; + bool original; +}; + +class Mpeginfo +{ + + public: + Mpeginfo() + : version( 0 ), + muxrate( 0 ), + playing_time( 0 ), + has_video ( false ), + has_audio ( false ) + { + for ( int i = 0; i < 3; i++ ) { + video[ i ].seen = false; + audio[ i ].seen = false; + } + }; + + ~Mpeginfo() + {} + ; + + unsigned int version; + unsigned long muxrate; + double playing_time; + bool has_video; + bool has_audio; + video_info video[ 3 ]; + audio_info audio[ 3 ]; +}; + +class K3bMpegInfo +{ + public: + K3bMpegInfo( const char* filename ); + ~K3bMpegInfo(); + enum mpeg_version { MPEG_VERS_INVALID = 0, MPEG_VERS_MPEG1 = 1, MPEG_VERS_MPEG2 = 2 }; + enum mode { MPEG_STEREO = 1, MPEG_JOINT_STEREO, MPEG_DUAL_CHANNEL, MPEG_SINGLE_CHANNEL }; + + const int version() + { + return mpeg_info->version; + }; + const QString error_string() + { + return m_error_string; + }; + Mpeginfo* mpeg_info; + + + private: + // General ToolBox + byte GetByte( llong offset ); + byte bdGetByte( llong offset ); + llong GetNBytes( llong, int ); + unsigned short int GetSize( llong offset ); + llong FindNextMarker( llong ); + llong FindNextMarker( llong, byte* ); + llong FindNextMarker( llong, byte ); + llong bdFindNextMarker( llong, byte ); + llong bdFindNextMarker( llong, byte* ); + llong FindNextVideo( llong ); + llong FindNextAudio( llong ); + + int GetVideoIdx ( byte ); + int GetAudioIdx ( byte ); + bool EnsureMPEG( llong, byte ); + void ParseVideo ( llong, byte ); + void ParseAudio ( llong, byte ); + bool MpegParsePacket (); + llong MpegParsePacket ( llong ); + llong SkipPacketHeader( llong ); + + double ReadTS( llong offset ); + double ReadTSMpeg2( llong offset ); + + FILE* m_mpegfile; + + const char* m_filename; + llong m_filesize; + + bool m_done; + + llong m_buffstart; + llong m_buffend; + byte* m_buffer; + double m_initial_TS; + QString m_error_string; + +}; + +#endif //K3bMpegInfo |