[matroska] r1281 - in trunk/DvdMenuXtractor: . dmx
[email protected] Sun, 11 Mar 2007 00:15:13 +0300 (MSK)
| Newsgroups | gmane.comp.multimedia.matroska.cvs |
|---|---|
| Message-ID | <[email protected]> |
Author: smssms
Date: 2007-03-11 00:14:50 +0300 (Sun, 11 Mar 2007)
New Revision: 1281
Added:
trunk/DvdMenuXtractor/dmx/
trunk/DvdMenuXtractor/dmx/chaptermanager.cpp
trunk/DvdMenuXtractor/dmx/chaptermanager.h
trunk/DvdMenuXtractor/dmx/dmx.cpp
trunk/DvdMenuXtractor/dmx/dmx.h
trunk/DvdMenuXtractor/dmx/dmx.proj
trunk/DvdMenuXtractor/dmx/dmx_project.h
trunk/DvdMenuXtractor/dmx/dmxselectionitem.cpp
trunk/DvdMenuXtractor/dmx/dmxselectionitem.h
trunk/DvdMenuXtractor/dmx/utilities.cpp
trunk/DvdMenuXtractor/dmx/utilities.h
Log:
Separate projects for CLI and GUI modes
Added: trunk/DvdMenuXtractor/dmx/chaptermanager.cpp
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/DvdMenuXtractor/dmx/chaptermanager.cpp 2007-03-08 09:04:37 UTC =
(rev 1280)
+++ trunk/DvdMenuXtractor/dmx/chaptermanager.cpp 2007-03-10 21:14:50 UTC =
(rev 1281)
@@ -0,0 +1,689 @@
+#include "chaptermanager.h"
+#include "utilities.h"
+
+#include <QtXml>
+
+QString ChapterManager::INFO_SUFFIX ("_info.xml");
+QString ChapterManager::CHAPTER_SUFFIX ("_menu.xml");
+
+bool ChapterManager::generateScript(const IFOFile& ifoFile, const QStrin=
g& filename, uint16_t title, const QString& editionUID) const
+{
+ static unsigned char _PrivateVTS[] =3D {0x30, 0x80, 0x00, 0x00};
+ static unsigned char _PrivateTT[] =3D {0x28, 0x00, 0x00, 0x00};
+ uint64_t start_timeH =3D uint64_t(-1);
+
+ generateInfoScript(filename, title, editionUID);
+
+ // create document
+ QDomDocument document;
+
+ // specify processing info
+ document.appendChild(document.createProcessingInstruction("xml", "versi=
on=3D\"1.0\" encoding=3D\"UTF-8\" standalone=3D\"no\""));
+ document.appendChild(document.implementation().createDocumentType("Chap=
ters", QString::null, "matroskachapters.dtd"));
+
+ // create comments
+ document.appendChild(document.createComment("Created with " + Utilities=
::APPLICATION_NAME + " " + Utilities::APPLICATION_VERSION));
+
+ // create the root element
+ QDomNode root =3D document.createElement("Chapters");
+ document.appendChild(root);
+
+ // VTS content
+ QDomElement editionEntryElement =3D document.createElement("EditionEntr=
y");
+ root.appendChild(editionEntryElement);
+=09
+ editionEntryElement.appendChild(CreateDOMElement(document, "EditionUID"=
, editionUID));
+ editionEntryElement.appendChild(CreateDOMElement(document, "EditionFlag=
Ordered", "1"));
+ editionEntryElement.appendChild(document.createComment("Video Title Set=
"));
+
+ QDomElement chapterAtomElement =3D document.createElement("ChapterAtom"=
);
+ editionEntryElement.appendChild(chapterAtomElement);
+
+ QString _myUIDa =3D Utilities::CreateUID();
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterUID",=
_myUIDa));
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterFlagH=
idden", "1"));
+=09
+ _PrivateVTS[2] =3D title >> 8;
+ _PrivateVTS[3] =3D title & 0xFF;
+ AddCodecPrivateData(document, chapterAtomElement, _PrivateVTS, 4);
+
+ QDomNode chapterAtomParentElement =3D chapterAtomElement;
+
+ if (ifoFile.VtsTitles(title) && ifoFile.VtsPGCs(title))
+ {
+ for (int i =3D 0; i < ifoFile.VtsTitles(title)->nr_of_srpts; i++)
+ {
+ uint64_t start_time =3D uint64_t(-1), found_time;
+ =09
+ chapterAtomParentElement.appendChild(document.createComment(QString("=
Title TTU_%1").arg(i + 1)));
+ QDomElement chapterAtomElement =3D document.createElement("ChapterAto=
m");
+ chapterAtomParentElement.appendChild(chapterAtomElement);
+ =09
+ QString _myUIDa =3D Utilities::CreateUID();
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterUID=
", _myUIDa));
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterFla=
gHidden", "1"));
+
+ // get the Title -> VTS+TTN map
+ const tt_srpt_t *p_map =3D ifoFile.TitleMap();
+
+ if ( p_map !=3D NULL )
+ {
+ for (int j=3D0; j<p_map->nr_of_srpts; j++)
+ {
+ if ( ( p_map->title[j].title_set_nr =3D=3D title ) && ( p_map->titl=
e[j].vts_ttn =3D=3D i+1 ) )
+ {
+ _PrivateTT[1] =3D (j+1) >> 8; // Title#
+ _PrivateTT[2] =3D (j+1) & 0xFF;
+ _PrivateTT[3] =3D i+1; // VTS_TTN#
+ AddCodecPrivateData(document, chapterAtomElement, _PrivateTT, 4);
+ break;
+ }
+ }
+ }
+
+ // add the PGC that match this PTT
+ for (int k =3D 0; k < ifoFile.VtsPGCs(title)->nr_of_pgci_srp; k++)
+ {
+ if ((ifoFile.VtsPGCs(title)->pgci_srp[k].entry_id & 0x7F) =3D=3D i+1=
)
+ {
+ found_time =3D AddPGC(document, chapterAtomElement,=20
+ ifoFile.VtsPGCs(title)->pgci_srp[k].pgc, k, 0, ifoFile.CellsList(t=
itle, false), &ifoFile.VtsTitles(title)->title[i], i+1);
+ =09
+ if (start_time > found_time)
+ start_time =3D found_time;
+ }
+ }
+
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterTim=
eStart", Utilities::FormatTime(start_time)));
+
+ if (start_timeH > start_time)
+ start_timeH =3D start_time;
+ }
+ }
+
+ if (start_timeH =3D=3D uint64_t(-1))
+ start_timeH =3D 0;
+
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterTimeS=
tart", Utilities::FormatTime(start_timeH)));
+=09
+ QString output =3D document.toString(INDENT_COUNT);
+
+ // TODO: not a good approach
+ output.replace("-->", "-->\n");
+
+ // chapter file
+ QFile chapterFile (filename + CHAPTER_SUFFIX);
+ if (!chapterFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ throw;
+
+ chapterFile.write(output.toUtf8());
+ chapterFile.close();
+
+ return true;
+}
+
+bool ChapterManager::generateMenuScript(const IFOFile& ifoFile, const QS=
tring& filename, uint16_t title, const QString& editionUID) const
+{
+ static unsigned char _PrivateFP[] =3D {0x30, 0x00, 0x00, 0x00};
+ static unsigned char _PrivateVM[] =3D {0x30, 0xC0, 0x00, 0x00};
+ static unsigned char _PrivateVTS[] =3D {0x30, 0x40, 0x00, 0x00};
+
+ // generate info script
+ generateInfoScript(filename, title, editionUID);
+
+ // create document
+ QDomDocument document;
+
+ // specify processing info
+ document.appendChild(document.createProcessingInstruction("xml", "versi=
on=3D\"1.0\" encoding=3D\"UTF-8\" standalone=3D\"no\""));
+ document.appendChild(document.implementation().createDocumentType("Chap=
ters", QString::null, "matroskachapters.dtd"));
+
+ // create comments
+ document.appendChild(document.createComment("Created with " + Utilities=
::APPLICATION_NAME + " " + Utilities::APPLICATION_VERSION));
+
+ // create the root element
+ QDomNode parentElement =3D document.createElement("Chapters");
+ document.appendChild(parentElement);
+
+ // create Edition Entry Element and add to root
+ QDomElement currentElement =3D document.createElement("EditionEntry");
+ parentElement.appendChild(currentElement);
+=09
+ // use Edition Entry Element as parent
+ parentElement =3D currentElement;
+
+ // add EditionUID and EDitionFlagOrdered elements
+ parentElement.appendChild(CreateDOMElement(document, "EditionUID", edit=
ionUID));
+ parentElement.appendChild(CreateDOMElement(document, "EditionFlagOrdere=
d", "1"));
+
+ // Write the first play PGC
+ if (title =3D=3D 0 && ifoFile.FirstPlayPGC())
+ {
+ // add comment to Edition Entry Element
+ parentElement.appendChild(document.createComment("First Play PGC"));
+
+ // add Chapter Atom
+ QDomElement chapterAtomElement =3D document.createElement("ChapterAtom=
");
+ parentElement.appendChild(chapterAtomElement);
+
+ // create and set UID
+ QString _myUID =3D Utilities::CreateUID();
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterUID"=
, _myUID));
+
+ // the First Play PGC has no cell and no time
+ AddChapterTime(document, chapterAtomElement, 0, 0);
+
+ // display the UID for the lazy rippers
+ chapterAtomElement.appendChild(document.createComment("Enter your text=
here"));
+
+ // add Chapter Display Element
+ currentElement =3D document.createElement("ChapterDisplay");
+ currentElement.appendChild(CreateDOMElement(document, "ChapterString",=
"First Play PGC " + _myUID));
+ chapterAtomElement.appendChild(currentElement);
+
+ // add Chapter Process Element
+ currentElement =3D document.createElement("ChapterProcess");
+ AddCodecPrivateData(document, currentElement, _PrivateFP, 4, false);
+ chapterAtomElement.appendChild(currentElement);
+
+ // PGC commands
+ AddPGCCommands(document, currentElement, ifoFile.FirstPlayPGC()->comma=
nd_tbl);
+ }
+
+ // Write the Video Title Set
+ if (ifoFile.LanguageUnits(title) && ifoFile.LanguageUnits(title)->nr_of=
_lus)
+ {
+ // VTS Menu
+ if ( title =3D=3D 0 )
+ parentElement.appendChild(document.createComment("Video Manager"));
+ else
+ parentElement.appendChild(document.createComment("Video Title Set"));
+
+ // add another Chapter Atom element
+ currentElement =3D document.createElement("ChapterAtom");
+ parentElement.appendChild(currentElement);
+
+ QString _myUIDa =3D Utilities::CreateUID();
+ currentElement.appendChild(CreateDOMElement(document, "ChapterUID", _m=
yUIDa));
+ currentElement.appendChild(CreateDOMElement(document, "ChapterFlagHidd=
en", "1"));
+
+ if ( title !=3D 0 )
+ {
+ _PrivateVTS[2] =3D title >> 8;
+ _PrivateVTS[3] =3D title & 0xFF;
+ AddCodecPrivateData(document, currentElement, _PrivateVTS, 4);
+ }
+ else
+ {
+ _PrivateVM[2] =3D 0;
+ _PrivateVM[3] =3D 0;
+ AddCodecPrivateData(document, currentElement, _PrivateVM, 4);
+ }
+
+ uint64_t start_time =3D HandleLanguageUnit(document, currentElement, i=
foFile, title);
+
+ currentElement.appendChild(CreateDOMElement(document, "ChapterTimeStar=
t", Utilities::FormatTime(start_time)));
+ }
+
+ // get document string representation
+ QString output =3D document.toString(INDENT_COUNT);
+
+ // TODO: not a good approach
+ output.replace("-->", "-->\n");
+
+ // chapter file
+ QFile chapterFile(filename + CHAPTER_SUFFIX);
+ if (!chapterFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ throw;
+=09
+ // write to the chpater file
+ chapterFile.write(output.toUtf8());
+ chapterFile.close();
+
+ return true;
+}
+
+void ChapterManager::generateInfoScript(const QString &filename, uint16_=
t title, const QString &editionUID) const
+{
+ // create document
+ QDomDocument document;
+
+ // specify xml version, encoding and DocType
+ document.appendChild(document.createProcessingInstruction("xml", "versi=
on=3D\"1.0\" encoding=3D\"UTF-8\" standalone=3D\"no\""));
+ document.appendChild(document.implementation().createDocumentType("Chap=
ters", QString::null, "matroskainfos.dtd"));
+
+ // add root element
+ QDomNode parentElement =3D document.createElement("Info");
+ document.appendChild(parentElement);
+
+ // add Segment Family Element
+ QDomElement currentElement =3D CreateDOMElement(document, "SegmentFamil=
y", Utilities::EncodeHex(familyUID, 16));
+ currentElement.setAttribute("format", "hex");
+ parentElement.appendChild(currentElement);
+
+ // add Chapter Translate Element
+ currentElement =3D document.createElement("ChapterTranslate");
+ currentElement.appendChild(CreateDOMElement(document, "ChapterTranslate=
EditionUID", editionUID));
+ currentElement.appendChild(CreateDOMElement(document, "ChapterTranslate=
Codec", "1"));
+=09
+ QDomElement chapterTranslateIDElement =3D CreateDOMElement(document, "C=
hapterTranslateID", QString("%1 00").arg(title, 2, 16, QChar('0')));
+ chapterTranslateIDElement.setAttribute("format", "hex");
+ currentElement.appendChild(chapterTranslateIDElement);
+
+ parentElement.appendChild(currentElement);
+
+ // get document string representation
+ QString output =3D document.toString(INDENT_COUNT);
+=09
+ // segment file
+ QFile segementFile(filename + INFO_SUFFIX);
+ if (!segementFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ throw;
+=09
+ segementFile.write(output.toUtf8());
+ segementFile.close();
+}
+
+QDomElement ChapterManager::CreateDOMElement(QDomDocument &document, con=
st QString &elementName, const QString &content)
+{
+ QDomElement element =3D document.createElement(elementName);
+ element.appendChild(document.createTextNode(content));
+
+ return element;
+}
+
+void ChapterManager::AddChapterTime(QDomDocument& document, QDomNode& pa=
rent, uint64_t start_time, uint64_t end_time)
+{
+ parent.appendChild(CreateDOMElement(document, "ChapterTimeStart", Utili=
ties::FormatTime(start_time)));
+ parent.appendChild(CreateDOMElement(document, "ChapterTimeEnd", Utiliti=
es::FormatTime(end_time)));
+}
+
+void ChapterManager::AddCodecPrivateData(QDomDocument& document, QDomNod=
e& parent, const unsigned char *buffer, unsigned int size, bool createCha=
pterProcessElement /*=3D true*/)
+{
+ QDomNode node =3D parent;
+
+ if (createChapterProcessElement)
+ {
+ QDomElement chapterProcessElement =3D document.createElement("ChapterP=
rocess");
+ parent.appendChild(chapterProcessElement);
+ node =3D chapterProcessElement;
+ }
+
+ node.appendChild(CreateDOMElement(document, "ChapterProcessCodecID", "1=
"));
+
+ QDomElement chapterProcessPrivateElement =3D CreateDOMElement(document,=
"ChapterProcessPrivate", Utilities::EncodeHex(buffer, size));
+ chapterProcessPrivateElement.setAttribute("format", "hex");
+ node.appendChild(chapterProcessPrivateElement);
+}
+
+void ChapterManager::AddPGCCommands(QDomDocument& document, QDomNode& pa=
rent, const pgc_command_tbl_t * command_tbl)
+{
+ if (command_tbl)
+ {
+ unsigned char *_buf =3D (unsigned char*) malloc(1 + command_tbl->nr_of=
_pre * 8);
+
+ // Pre-commands
+ if (command_tbl->nr_of_pre)
+ {
+ parent.appendChild(document.createComment("Pre commands"));
+
+ QDomElement chapterCommandElement =3D document.createElement("Chapter=
ProcessCommand");
+ parent.appendChild(chapterCommandElement);
+
+ QDomElement chapterProcessTimeElement =3D document.createElement("Cha=
pterProcessTime");
+ chapterProcessTimeElement.appendChild(document.createTextNode("1"));
+ chapterCommandElement.appendChild(chapterProcessTimeElement);
+
+ _buf[0] =3D command_tbl->nr_of_pre;
+ memcpy(&_buf[1], command_tbl->pre_cmds, command_tbl->nr_of_pre * 8);
+
+ QDomElement chapterProcessDataElement =3D CreateDOMElement(document, =
"ChapterProcessData", Utilities::EncodeHex(_buf, 1 + command_tbl->nr_of_p=
re * 8));
+ chapterProcessDataElement.setAttribute("format", "hex");
+ chapterCommandElement.appendChild(chapterProcessDataElement);
+ }
+
+ // Cell commands
+ if (command_tbl->nr_of_cell)
+ {
+ parent.appendChild(document.createComment("Cell commands"));
+
+ QDomElement chapterCommandElement =3D document.createElement("Chapter=
ProcessCommand");
+ parent.appendChild(chapterCommandElement);
+
+ QDomElement chapterProcessTimeElement =3D document.createElement("Cha=
pterProcessTime");
+ chapterProcessTimeElement.appendChild(document.createTextNode("0"));
+ chapterCommandElement.appendChild(chapterProcessTimeElement);
+
+ _buf =3D (unsigned char*) realloc(_buf, 1 + command_tbl->nr_of_cell *=
8);
+ _buf[0] =3D command_tbl->nr_of_cell;
+ memcpy(&_buf[1], command_tbl->cell_cmds, command_tbl->nr_of_cell * 8)=
;
+
+ QDomElement chapterProcessDataElement =3D CreateDOMElement(document, =
"ChapterProcessData", Utilities::EncodeHex(_buf, 1 + command_tbl->nr_of_c=
ell * 8));
+ chapterProcessDataElement.setAttribute("format", "hex");
+ chapterCommandElement.appendChild(chapterProcessDataElement);
+ }
+
+ // Post commands
+ if (command_tbl->nr_of_post)
+ {
+ parent.appendChild(document.createComment("Post commands"));
+
+ QDomElement chapterCommandElement =3D document.createElement("Chapter=
ProcessCommand");
+ parent.appendChild(chapterCommandElement);
+
+ QDomElement chapterProcessTimeElement =3D document.createElement("Cha=
pterProcessTime");
+ chapterProcessTimeElement.appendChild(document.createTextNode("2"));
+ chapterCommandElement.appendChild(chapterProcessTimeElement);
+
+ _buf =3D (unsigned char*) realloc(_buf, 1+command_tbl->nr_of_post * 8=
);
+ _buf[0] =3D command_tbl->nr_of_post;
+ memcpy(&_buf[1], command_tbl->post_cmds, command_tbl->nr_of_post * 8)=
;
+
+ QDomElement chapterProcessDataElement =3D CreateDOMElement(document, =
"ChapterProcessData", Utilities::EncodeHex(_buf, 1 + command_tbl->nr_of_p=
ost * 8));
+ chapterProcessDataElement.setAttribute("format", "hex");
+ chapterCommandElement.appendChild(chapterProcessDataElement);
+ }
+
+ free(_buf);
+ }
+}
+
+uint64_t ChapterManager::HandleLanguageUnit(QDomDocument& document, QDom=
Node& parent, const IFOFile& _ifo, int title)
+{
+ static unsigned char _PrivateLU[] =3D {0x2A, 0x00, 0x00, 0x00};
+ uint64_t start_time =3D uint64_t(-1), found_time;
+
+ QDomElement chapterAtomElement;
+ QDomElement chapterDisplayElement;
+ QDomElement chapterStringElement;
+ QDomElement chapterTimeStartElement;
+
+ for (int i =3D 0 ;i < _ifo.LanguageUnits(title)->nr_of_lus; i++)
+ {
+ parent.appendChild(document.createComment("Language Units"));
+
+ chapterAtomElement =3D document.createElement("ChapterAtom");
+ parent.appendChild(chapterAtomElement);
+
+ // the Language Unit for a given language
+ const pgci_lu_t &_LU =3D _ifo.LanguageUnits(title)->lu[i];
+ _PrivateLU[1] =3D _LU.lang_code >> 8;
+ _PrivateLU[2] =3D _LU.lang_code & 0xFF;
+ _PrivateLU[3] =3D _LU.lang_extension;
+
+ chapterAtomElement.appendChild(document.createComment(QString("Languag=
e: %1%2").arg(QChar(_LU.lang_code >> 8)).arg(QChar(_LU.lang_code & 0xFF))=
));
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterUID"=
, Utilities::CreateUID()));
+
+ AddCodecPrivateData(document, chapterAtomElement, _PrivateLU, 4);
+
+ // display the UID for the lazy rippers
+ chapterAtomElement.appendChild(document.createComment("Enter your text=
here"));
+
+ chapterDisplayElement =3D document.createElement("ChapterDisplay");
+ chapterAtomElement.appendChild(chapterDisplayElement);
+
+ chapterStringElement =3D document.createElement("ChapterString");
+ chapterStringElement.appendChild(document.createTextNode(QString("Lang=
uage Unit for %1%2").arg(QChar(_LU.lang_code >> 8)).arg(QChar(_LU.lang_co=
de & 0xFF))));
+ chapterDisplayElement.appendChild(chapterStringElement);
+
+ if (_LU.pgcit)
+ {
+ for (int j =3D 0; j < _LU.pgcit->nr_of_pgci_srp; j++)
+ {
+ const pgci_srp_t &_PGC_SRP =3D _LU.pgcit->pgci_srp[j];
+
+ found_time =3D AddPGC(document, chapterAtomElement, _PGC_SRP.pgc, j,=
_PGC_SRP.entry_id, _ifo.CellsList(title, true), NULL, 0);
+
+ if (start_time > found_time)
+ start_time =3D found_time;
+ }
+ }
+
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterTime=
Start", Utilities::FormatTime(start_time)));
+ }
+
+ return start_time;
+}
+
+
+uint64_t ChapterManager::AddPGC(QDomDocument& document, QDomNode& parent=
, const pgc_t * pgc, uint16_t pgc_num, unsigned char pgc_type, const Cell=
sListType & cell_list, const ttu_t * ptts, int title)
+{
+ static unsigned char _PrivatePGC[] =3D {0x20, 0x00, 0x00, 0x00, 0x00, 0=
x00, 0x00, 0x00};
+ uint64_t start_time =3D uint64_t(-1), found_time;
+
+ parent.appendChild(document.createComment(QString("PGC PGC_%1 type %2")=
.arg(pgc_num+1).arg(GetPGCType(pgc_type))));
+
+ QDomElement chapterAtomElement =3D document.createElement("ChapterAtom"=
);
+ parent.appendChild(chapterAtomElement);
+
+ QString _myUID =3D Utilities::CreateUID();
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterUID",=
_myUID));
+
+ // display the UID for the lazy rippers
+ if (ptts =3D=3D NULL)
+ {
+ chapterAtomElement.appendChild(document.createComment("Enter your text=
here"));
+
+ QDomElement chapterDisplayElement =3D document.createElement("ChapterD=
isplay");
+ chapterDisplayElement.appendChild(CreateDOMElement(document, "ChapterS=
tring", "PGC type " + GetPGCType(pgc_type)));
+ chapterAtomElement.appendChild(chapterDisplayElement);
+ }
+ else
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterFlag=
Hidden", "1"));
+
+ // PGC commands
+ QDomElement chapterProcessElement =3D document.createElement("ChapterPr=
ocess");
+ chapterAtomElement.appendChild(chapterProcessElement);
+
+ _PrivatePGC[1] =3D (pgc_num+1) >> 8;
+ _PrivatePGC[2] =3D (pgc_num+1) & 0xFF;
+ _PrivatePGC[3] =3D pgc_type;
+ _PrivatePGC[4] =3D ((uint8_t*) &pgc->prohibited_ops)[0];
+ _PrivatePGC[5] =3D ((uint8_t*) &pgc->prohibited_ops)[1];
+ _PrivatePGC[6] =3D ((uint8_t*) &pgc->prohibited_ops)[2];
+ _PrivatePGC[7] =3D ((uint8_t*) &pgc->prohibited_ops)[3];
+
+ AddCodecPrivateData(document, chapterProcessElement, _PrivatePGC, 8, fa=
lse);
+ AddPGCCommands(document, chapterProcessElement, pgc->command_tbl);
+
+ // Handle the Programs/Cells in the PGC
+ for (int i=3D0; i< pgc->nr_of_programs; i++)
+ {
+ found_time =3D AddProgram(document, chapterAtomElement, pgc, _myUID, i=
, cell_list, pgc_num, ptts, title);
+
+ if (start_time > found_time)
+ start_time =3D found_time;
+ }
+
+ if (start_time =3D=3D uint64_t(-1))
+ start_time =3D 0;
+
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterTimeS=
tart", Utilities::FormatTime(start_time)));
+
+ return start_time;
+}
+
+
+QString ChapterManager::GetPGCType(unsigned char entry_id)
+{
+ QString result ("0x%1 =3D ");
+ result =3D result.arg(entry_id, 2, 16, QChar('0'));
+
+ if (entry_id & 0x80)
+ result +=3D "Entry + ";
+
+ switch (entry_id & ~0x80)
+ {
+ case 2:
+ result +=3D "Title-Menu";
+ break;
+ case 3:
+ result +=3D "Root Menu";
+ break;
+ case 4:
+ result +=3D "Subpicture Menu";
+ break;
+ case 5:
+ result +=3D "Audio Menu";
+ break;
+ case 6:
+ result +=3D "Angle Menu";
+ break;
+ case 7:
+ result +=3D "Chapter Menu";
+ break;
+ default:
+ result +=3D "Basic PGC";
+ break;
+ }
+
+ return result;
+}
+
+
+uint64_t ChapterManager::AddProgram(QDomDocument& document, QDomNode& pa=
rent, const pgc_t *pgc, const QString& PgcUID, int program_number, const =
CellsListType& cell_list, int16_t pgc_num, const ttu_t *ptts, int title)
+{
+ static unsigned char _PrivatePTT[] =3D {0x10, 0x00};
+ static unsigned char _PrivatePG[] =3D {0x18, 0x00, 0x00};
+ static unsigned char _PrivateCN[] =3D {0x08, 0x00, 0x00, 0x00, 0x00};
+
+ uint64_t start_time =3D uint64_t(-1), end_time =3D 0;
+
+ // for this program
+ int entry_cell_num =3D pgc->program_map[program_number];
+ int program_last_cell_num =3D pgc->nr_of_cells;
+ int next_program_entry_cell_num;
+ if (program_number+1 =3D=3D pgc->nr_of_programs)
+ next_program_entry_cell_num =3D program_last_cell_num+1;
+ else
+ next_program_entry_cell_num =3D pgc->program_map[program_number+1];
+
+ parent.appendChild(document.createComment(QString("Program PGN# %1 in t=
his PGC").arg(program_number + 1)));
+
+ QDomElement chapterAtomElement =3D document.createElement("ChapterAtom"=
);
+ parent.appendChild(chapterAtomElement);
+
+ QString _myUID =3D Utilities::CreateUID();
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterUID",=
_myUID));
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterFlagH=
idden", "1"));
+
+ _PrivatePG[1] =3D (program_number+1) >> 8;
+ _PrivatePG[2] =3D (program_number+1) & 0xFF;
+ AddCodecPrivateData(document, chapterAtomElement, _PrivatePG, 3);
+
+ if (ptts !=3D NULL)
+ {
+ // find all PTT corresponding to this PGC/PG number
+ for (uint16_t pppt_nr =3D 0; pppt_nr < ptts->nr_of_ptts; pppt_nr++)
+ {
+ // use the start timecode of the first cell
+ const cell_position_t & position =3D pgc->cell_position[entry_cell_nu=
m-1];
+ const CellListElem * cell_elt =3D cell_list.at(position);
+ // TODO: make this test optional (create chapters even without the co=
ntent)
+ if (cell_elt =3D=3D NULL || !cell_elt->found)
+ continue;
+ =09
+ start_time =3D cell_elt->start_time;
+
+ QDomElement chapterAtomParent =3D chapterAtomElement;
+
+ if (ptts->ptt[pppt_nr].pgcn =3D=3D pgc_num+1 && ptts->ptt[pppt_nr].pg=
n =3D=3D program_number+1)
+ {
+ chapterAtomElement.appendChild(document.createComment(QString("Chapt=
er PTT#%1 [%2.%3]").arg(pppt_nr+1).arg(pgc_num+1, 2, 10, QChar('0')).arg(=
program_number+1, 2, 10, QChar('0'))));
+ =09
+ chapterAtomElement =3D document.createElement("ChapterAtom");
+ chapterAtomParent.appendChild(chapterAtomElement);
+
+ chapterAtomElement.appendChild(document.createComment("Enter your te=
xt here"));
+
+ QDomElement chapterDisplayElement =3D document.createElement("Chapte=
rDisplay");
+ chapterDisplayElement.appendChild(CreateDOMElement(document, "Chapte=
rString", QString("Your Name Here For Chapter #%1.%2").arg(title).arg(ppp=
t_nr + 1)));
+ chapterAtomElement.appendChild(chapterDisplayElement);
+
+ QString _myUID =3D Utilities::CreateUID();
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterUI=
D", _myUID));
+
+ _PrivatePTT[1] =3D pppt_nr+1;
+ AddCodecPrivateData(document, chapterAtomElement, _PrivatePTT, 2);
+
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterTi=
meStart", Utilities::FormatTime(start_time)));
+ }
+
+ chapterAtomElement =3D chapterAtomParent;
+ }
+ }
+
+ QDomElement parentChapterAtom =3D chapterAtomElement;
+
+ for (int cell_num =3D entry_cell_num; cell_num<next_program_entry_cell_=
num; cell_num++)
+ {
+ // Output cells
+ const cell_position_t & position =3D pgc->cell_position[cell_num-1];
+ const cell_playback_t & cell =3D pgc->cell_playback[cell_num-1];
+
+ if (cell.block_mode !=3D 0 && cell.block_mode !=3D 3)
+ continue;
+
+ const CellListElem * cell_elt =3D cell_list.at(position);
+ // TODO: make this test optional (create chapters even without the con=
tent)
+ if (cell_elt =3D=3D NULL || !cell_elt->found)
+ continue;
+
+ QString prefix =3D QString("Program %1 Cell CN#%2 ").arg( (cell_num =3D=
=3D entry_cell_num) ? "Entry" : "", QString::number(cell_num));
+ QString middle =3D QString("[%1.%2]").arg(cell_elt->vobid, 2, 10, QCha=
r('0')).arg(cell_elt->cellid);
+ QString suffix =3D QString(" (%1 angles)").arg(cell_num - entry_cell_n=
um + 1);
+
+ if (cell.block_mode) // multi-angle
+ parentChapterAtom.appendChild(document.createComment(prefix + middle =
+ suffix));
+ else
+ parentChapterAtom.appendChild(document.createComment(prefix + middle)=
);
+
+ QDomElement chapterAtomElement =3D document.createElement("ChapterAtom=
");
+ parentChapterAtom.appendChild(chapterAtomElement);
+
+ QString _myUID =3D Utilities::CreateUID();
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterUID"=
, _myUID));
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterFlag=
Hidden", "1"));
+
+ _PrivateCN[1] =3D (position.vob_id_nr) >> 8;
+ _PrivateCN[2] =3D (position.vob_id_nr) & 0xFF;
+ _PrivateCN[3] =3D position.cell_nr;
+ _PrivateCN[4] =3D cell_num - entry_cell_num + 1; // Number of angles
+ AddCodecPrivateData(document, chapterAtomElement, _PrivateCN, 5);
+
+ if (cell.still_time =3D=3D 0xFF) {
+ chapterAtomElement.appendChild(document.createComment("Infinite loop =
Still Cell"));
+
+ // output an additional tag to specify this is cell should loop infin=
itely
+ // post-process command, jump to timecode "st_time"
+ QDomElement chapterProcessElement =3D document.createElement("Chapter=
Process");
+ chapterProcessElement.appendChild(CreateDOMElement(document, "Chapter=
ProcessCodecID", "0"));
+ chapterProcessElement.appendChild(document.createComment("Post comman=
d: replay this cell"));
+ chapterAtomElement.appendChild(chapterProcessElement);
+
+ QDomElement chapterProcessCommand =3D document.createElement("Chapter=
ProcessCommand");
+ chapterProcessCommand.appendChild(CreateDOMElement(document, "Chapter=
ProcessTime", "2"));
+ chapterProcessElement.appendChild(chapterProcessCommand);
+
+ QDomElement chapterProcessDataElement =3D CreateDOMElement(document, =
"ChapterProcessData", "GotoAndPlay(" + PgcUID + ");" );
+ chapterProcessDataElement.setAttribute("format", "ascii");
+ chapterProcessCommand.appendChild(chapterProcessDataElement);
+ } else if (cell.still_time !=3D 0)
+ chapterAtomElement.appendChild(document.createComment(QString("Still =
cell (%1s)").arg(cell.still_time)));
+
+ AddChapterTime(document, chapterAtomElement, cell_elt->start_time, cel=
l_elt->start_time + cell_elt->duration);
+
+ if (end_time < cell_elt->start_time + cell_elt->duration)
+ end_time =3D cell_elt->start_time + cell_elt->duration;
+
+ if (start_time > cell_elt->start_time)
+ start_time =3D cell_elt->start_time;
+ }
+
+ if (start_time =3D=3D uint64_t(-1))
+ start_time =3D 0; // unknown
+
+ parentChapterAtom.appendChild(CreateDOMElement(document, "ChapterTimeSt=
art", Utilities::FormatTime(start_time)));
+
+ return start_time;
+}
Added: trunk/DvdMenuXtractor/dmx/chaptermanager.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/DvdMenuXtractor/dmx/chaptermanager.h 2007-03-08 09:04:37 UTC (r=
ev 1280)
+++ trunk/DvdMenuXtractor/dmx/chaptermanager.h 2007-03-10 21:14:50 UTC (r=
ev 1281)
@@ -0,0 +1,40 @@
+#ifndef CHAPTER_MANAGER_H
+#define CHAPTER_MANAGER_H
+
+#include <QString>
+#include "vobparser/IFOFile.h"
+
+class QDomNode;
+class QDomElement;
+class QDomDocument;
+
+class ChapterManager
+{
+public:
+ ChapterManager(unsigned xmlIdentCount)
+ : INDENT_COUNT(xmlIdentCount)
+ {
+ }
+ bool generateScript(const IFOFile& ifoFile, const QString& filename, ui=
nt16_t title, const QString& editionUID) const;
+ bool generateMenuScript(const IFOFile& ifoFile, const QString& filename=
, uint16_t title, const QString& editionUID) const;
+
+ static QString INFO_SUFFIX;
+ static QString CHAPTER_SUFFIX;
+
+private:
+ void generateInfoScript(const QString& filename, uint16_t title, const =
QString& editionUID) const;
+=09
+ static QDomElement CreateDOMElement(QDomDocument& document, const QStri=
ng& elementName, const QString& content);
+ static void AddChapterTime(QDomDocument& document, QDomNode& parent, ui=
nt64_t start_time, uint64_t end_time);
+ static void AddCodecPrivateData(QDomDocument& document, QDomNode& paren=
t, const unsigned char *buffer, unsigned int size, bool createChapterProc=
essElement =3D true);
+ static void AddPGCCommands(QDomDocument& document, QDomNode& parent, co=
nst pgc_command_tbl_t * command_tbl);
+ static uint64_t AddProgram(QDomDocument& document, QDomNode& parent, co=
nst pgc_t *pgc, const QString& PgcUID, int program_number, const CellsLis=
tType& cell_list, int16_t pgc_num, const ttu_t *ptts, int title);
+ static uint64_t AddPGC(QDomDocument& document, QDomNode& parent, const =
pgc_t * pgc, uint16_t pgc_num, unsigned char pgc_type, const CellsListTyp=
e & cell_list, const ttu_t * ptts, int title);
+ static QString GetPGCType(unsigned char entry_id);
+ static uint64_t HandleLanguageUnit(QDomDocument& document, QDomNode& pa=
rent, const IFOFile& _ifo, int title);
+
+ uint8_t familyUID[16];
+ unsigned INDENT_COUNT;
+};
+
+#endif //CHAPTER_MANAGER_H
Added: trunk/DvdMenuXtractor/dmx/dmx.cpp
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/DvdMenuXtractor/dmx/dmx.cpp 2007-03-08 09:04:37 UTC (rev 1280)
+++ trunk/DvdMenuXtractor/dmx/dmx.cpp 2007-03-10 21:14:50 UTC (rev 1281)
@@ -0,0 +1,491 @@
+#include "dmx.h"
+#include "utilities.h"
+#include "chaptermanager.h"
+
+#include <QDir>
+#include <QTime>
+#include <QMutexLocker>
+
+DMX::DMX(bool consoleMode)
+ : ifoFile_(0), consoleMode_(consoleMode), needsAbort_(false)
+{
+}
+
+DMX::~DMX()
+{
+ abort();
+
+ QMutex mutex;
+ mutex.lock();
+ delete ifoFile_;
+ ifoFile_ =3D 0;
+ mutex.unlock();
+}
+
+void DMX::abort()
+{
+ QMutex mutex;
+ mutex.lock();
+ needsAbort_ =3D true;
+ mutex.unlock();
+
+ wait();
+}
+
+void DMX::processTitle(int16_t title, int index)
+{
+ const QString editionUID =3D Utilities::CreateUID();
+
+ unsigned stepIndex =3D 0;
+ bool menu =3D ((index < 0) && !title) || ((index >=3D 0) && selection_[=
index].isMenu());
+ VobParser *aVobParser =3D 0;
+ QString str;
+ QString text =3D "Step %1 of 2: %3...";
+
+ do
+ {
+ printf("Treating Title %d %s VOB file(s)\n", title, menu ? "Menu" : ""=
);
+
+ stepIndex =3D 1;
+
+ str =3D text.arg(stepIndex++).arg("Building VOB map");
+ =09
+ if (consoleMode_)
+ printf(qPrintable(str + '\n'));
+ else
+ emit stepChanged(str);
+
+ aVobParser =3D buildVobParser(title, menu);
+
+ str =3D text.arg(stepIndex++).arg("Splitting and demuxing");
+ =09
+ if (consoleMode_)
+ printf(qPrintable(str + '\n'));
+ else
+ emit stepChanged(str);
+ =09
+ demux(aVobParser, index, editionUID, title, menu);
+
+ delete aVobParser;
+ =09
+ menu =3D !menu && ((index < 0) || selection_[index].isMenu());
+ } while (menu && !needsAbort_);
+}
+
+bool DMX::setExtractionParameters(const QString& sourcePath, const QStri=
ng& destinationPath, const QString& toolsPath, const SelectionType& selec=
tedItems)
+{
+ QMutex mutex;
+ QMutexLocker locker (&mutex);
+
+ // set selection
+ selection_ =3D selectedItems;
+
+ // check if require directories exist
+ if (!QFile::exists(sourcePath))
+ {
+ fprintf(stderr, "Input folder '%s' doesn't exist\n", qPrintable(source=
Path));
+ return false;
+ }
+
+ // set source path
+ sourcePath_ =3D sourcePath;
+
+ if (!QFile::exists(toolsPath))
+ {
+ fprintf(stderr, "Tools folder '%s' doesn't exist\n", qPrintable(toolsP=
ath));
+ return false;
+ }
+
+ // set "mkvmerge.exe" path
+ toolsPath_ =3D toolsPath;
+
+ // try to create output
+ if (!QFile::exists(destinationPath))
+ {
+ if (!QDir().mkpath(destinationPath))
+ {
+ fprintf(stderr,"Cannot create output folder: '%s'\n", qPrintable(dest=
inationPath));
+ return false;
+ }
+ else
+ printf("Successfully created output folder: '%s'\n", qPrintable(desti=
nationPath));
+ }
+
+ // set output path
+ destinationPath_ =3D destinationPath;
+
+ return true;
+}
+
+void DMX::run()
+{
+ // try to open input file
+ if (!sourcePath_.size() || !loadIFOFile(sourcePath_))
+ return;
+
+ QMutex mutex;
+ mutex.lock();
+ needsAbort_ =3D false;
+ mutex.unlock();
+
+ if (selection_.size()) // if selection is available
+ {
+ for (size_t index =3D 0; index < selection_.size(); ++index)
+ {
+ int16_t title =3D selection_[index].title();
+ =09
+ if (title >=3D 0)
+ processTitle(title, index);
+ }
+ }=20
+ else // if no selection is available, process all titles
+ {
+ for (int16_t title =3D 0; title <=3D ifoFile_->NumberOfTitles(); ++tit=
le)
+ processTitle(title, -1);
+ }
+}
+
+IFOFile* DMX::OpenIFOFile(const QString& path)
+{
+ IFOFile *file =3D 0;
+ QDir info (path);
+=09
+ try
+ {
+ file =3D new IFOFile(info.absolutePath());
+ }
+ catch(IFOException&)
+ {
+ // TODO : More verbose
+ fprintf(stderr, "Couldn't use input directory as DVD source\n");
+ return 0;
+ }
+
+ return file;
+}
+
+bool DMX::loadIFOFile(const QString& path)
+{
+ QMutex mutex;
+ QMutexLocker locker (&mutex);
+=09
+ // reset last file
+ delete ifoFile_;
+ ifoFile_ =3D OpenIFOFile(path);
+
+ if (ifoFile_ =3D=3D 0)
+ return false;
+
+ return true;
+}
+
+VobParser* DMX::buildVobParser(int16_t title, bool menu)
+{
+ VobParser* parser =3D 0;
+ if (consoleMode_)
+ printf("0%%");
+ else
+ emit progressChanged(0);
+=09
+ try=20
+ {
+ parser =3D new VobParser(qPrintable(sourcePath_), title, menu);
+ } catch (...)
+ {
+ if (consoleMode_)
+ printf("\r\r");
+ =09
+ printf("No VOB file(s) found in %s for Title %d\n", qPrintable(sourceP=
ath_), title);
+ }
+
+ if (consoleMode_)
+ printf("\r\r100%%\n");
+ else
+ emit progressChanged(100);
+=09
+ return parser;
+}
+
+void DMX::demuxAudioTrack(int16_t title, bool isMenu, const AudioTrackLi=
st& _audioTracks, size_t _stream, CompositeDemuxWriter& demuxer, const QS=
tring& filename)
+{
+ if (_stream < 0 || _stream >=3D _audioTracks.size())
+ return;
+
+ static const QString muxArgumentsFormat(" --track-name 0:\"aud-%1\" --l=
anguage 0:%2 --timecodes 0:\"%3_%4.tmc\" \"%3.%4\"");
+
+ const audio_attr_t *_attr =3D _audioTracks[_stream];
+ // get the possible ID for this track for this Cell/PGC
+ uint8_t _ID =3D ifoFile_->GetAudioId(_stream, title, isMenu);
+
+ Writer *_muxer =3D 0;
+=09
+ // if (_attr->lang_code =3D=3D 0)
+ QString lang ("und");
+ QString langSuffix (QString("_%1_un").arg(_ID));
+=09
+ if (_attr->lang_code !=3D 0)
+ {
+ lang =3D QString("%1%2").arg(QString(_attr->lang_code >> 8), QString(_=
attr->lang_code & 0xFF));
+ langSuffix =3D QString("_%1_%2%3").arg(QString::number(_ID), QString(_=
attr->lang_code >> 8), QString(_attr->lang_code & 0xFF));
+ }
+
+ QString muxArguments;
+ QString fullPrefix (destinationPath_ + QDir::separator() + filename + l=
angSuffix);
+
+ switch (_audioTracks[_stream]->audio_format)
+ {
+ case 0:
+ muxArguments =3D muxArgumentsFormat.arg(filename + langSuffix, lang, f=
ullPrefix, "ac3"); =09
+ _muxer =3D new AC3DemuxWriter(fullPrefix, 0x80 + _ID);
+
+ if (!demuxer.AddDemuxer(SUBSTREAM_AC3_LOW + _ID, _muxer, muxArguments)=
)
+ delete _muxer;
+ break;
+
+ case 2:
+ case 3:
+ muxArguments =3D muxArgumentsFormat.arg(filename + langSuffix, lang, f=
ullPrefix, "mpa"); =09
+ _muxer =3D new MPADemuxWriter(fullPrefix, 0xC0 + _ID);
+
+ if (!demuxer.AddDemuxer(AUDIO_STREAM + _ID, _muxer, muxArguments))
+ delete _muxer;
+ break;
+
+ case 4:
+ // TODO possibly other LPCM formats ?
+ muxArguments =3D muxArgumentsFormat.arg(filename + langSuffix, lang, f=
ullPrefix, "wav");
+ _muxer =3D new LPCMDemuxWriter(fullPrefix, 0xA0 + _ID, 48000, 16, 2);
+
+ if (!demuxer.AddDemuxer(SUBSTREAM_PCM_LOW + _ID, _muxer, muxArguments)=
)
+ delete _muxer;
+ break;
+
+ case 6:
+ muxArguments =3D muxArgumentsFormat.arg(filename + langSuffix, lang, f=
ullPrefix, "dts");
+ _muxer =3D new DTSDemuxWriter(fullPrefix, 0x88 + _ID);
+
+ if (!demuxer.AddDemuxer(SUBSTREAM_DTS_LOW + _ID, _muxer, muxArguments)=
)
+ delete _muxer;
+ break;
+
+ default:
+ fprintf(stderr, "Unknown Audio Format: %d\n", _attr->audio_format);
+ }
+}
+
+void DMX::demuxSubtitleTrack(int16_t title, bool isMenu, const SubtitleT=
rackList& _subTracks, size_t _stream, CompositeDemuxWriter& demuxer, con=
st QString& filename, const uint32_t *_palette, uint16_t _width, uint16_t=
_height)
+{
+ if (_stream < 0 || _stream >=3D _subTracks.size())
+ return;
+
+ static const QString subMuxArgumentsFormat(" --track-name 0:\"sub-%1\" =
--language 0:%2 \"%3\"");
+
+ const subp_attr_t *_attr =3D 0;
+ const QString prefix =3D destinationPath_ + QDir::separator() + filenam=
e;
+
+ // get the possible IDs for this track for this Cell/PGC
+ IdArray _IDs =3D ifoFile_->GetSubsId(_stream, title, isMenu);
+
+ Writer * _muxer =3D 0;
+ QString lang, langSuffix;
+=09
+ for (IdArray::size_type _IDidx=3D0; _IDidx < _IDs.size(); ++_IDidx)
+ {
+ _attr =3D _subTracks[_stream];
+ =09
+ // if (_attr->lang_code =3D=3D 0)
+ lang =3D "und";
+ langSuffix =3D QString("_%1_un").arg(_IDs.at(_IDidx));
+
+ if (_attr->lang_code !=3D 0)
+ {
+ lang =3D QString("%1%2 \"").arg(QString(_attr->lang_code >> 8), QStri=
ng(_attr->lang_code & 0xFF));
+ langSuffix =3D QString("_%1_%2%3").arg(QString::number(_IDs.at(_IDidx=
)), QString(_attr->lang_code >> 8), QString(_attr->lang_code & 0xFF)); =09
+ }
+
+ _muxer =3D new SubDemuxWriter(QString(prefix + langSuffix), 0x20 + _ID=
s.at(_IDidx), _width, _height, _palette, _attr->lang_code, _attr->lang_ex=
tension =3D=3D 9);
+
+ QString commandLine =3D subMuxArgumentsFormat.arg(filename + langSuffi=
x, lang, prefix + langSuffix + ".idx");
+ if (!demuxer.AddDemuxer(SUBSTREAM_SUB_LOW + _IDs.at(_IDidx), _muxer, c=
ommandLine))
+ delete _muxer;
+ }
+}
+
+void DMX::demux(VobParser *aVobParser, int selectionIndex, const QString=
&editionUID, int16_t title, bool menu)
+{
+ // get list of all cells
+ const CellsListType *CellsList =3D ifoFile_->GetCellsList(title, menu);
+=09
+ if (!CellsList)
+ return;
+
+ static const QString demuxArguments (" --track-name 0:\"video\" --timec=
odes 0:\"%1_m2v.tmc\" \"%1.m2v\"");
+ static const QString btnMuxArgumentsFormat(" --track-name 0:\"btn-%1\" =
--timecodes 0:\"%2_btn.tmc\" \"%2.btn\"");
+=09
+ try
+ {
+ QString filename;
+ QString muxCommand;
+
+ // init filename
+ if (title =3D=3D 0)
+ filename =3D "VMG";
+ else
+ filename =3D QString("VTS%1%2").arg(menu ? "M" : "").arg(title, 2, 10=
, QChar('0'));
+
+ const QString prefix =3D destinationPath_ + QDir::separator() + filena=
me;
+
+ if (aVobParser !=3D 0)
+ {
+ aVobParser->Reset();
+
+ const AudioTrackList & _audioTracks =3D ifoFile_->AudioTracks(title, =
menu);
+ const SubtitleTrackList & _subTracks =3D ifoFile_->SubsTracks(title, =
menu);
+ =09
+ double _fps =3D 0;
+ uint16_t _width =3D 0, _height =3D 0;
+ =09
+ ifoFile_->VideoSize(title, menu, _width, _height, _fps);
+ const uint32_t * _palette =3D ifoFile_->GetPalette(title, menu);
+
+ CompositeDemuxWriter &demuxer =3D aVobParser->GetDemuxer();
+
+ //emit progressChanged(CellsList->count());
+
+ demuxer.Reset();
+ printf("Processing %s\n", qPrintable(filename));
+
+ if ((selectionIndex < 0) || (selection_[selectionIndex].isVideoEnable=
d()))
+ {
+ Writer *_muxer =3D new VideoDemuxWriter(prefix, _fps);
+
+ QString commandLine =3D demuxArguments.arg(prefix);
+ if (!demuxer.AddDemuxer(VIDEO_STREAM, _muxer, commandLine))
+ delete _muxer;
+ }
+
+ size_t _stream =3D 0;
+
+ QString lang;
+ QString langSuffix;
+
+ // decide which audio streams to process
+ if (_audioTracks.size())
+ {
+ if (selectionIndex > -1)
+ {
+ const std::vector<size_t>& selectedAudioTracks =3D selection_[selec=
tionIndex].audioTracks();
+ =09
+ for (size_t index =3D 0; index < selectedAudioTracks.size(); ++inde=
x)
+ {
+ _stream =3D selectedAudioTracks[index];
+ demuxAudioTrack(title, menu, _audioTracks, _stream, demuxer, file=
name);
+ }
+ }
+ else
+ {
+ for (_stream =3D 0; _stream < _audioTracks.size(); ++_stream)
+ demuxAudioTrack(title, menu, _audioTracks, _stream, demuxer, filen=
ame);
+ }
+ }
+
+ // process subtitle streams
+ if (_subTracks.size())
+ {
+ if (selectionIndex > -1)
+ {
+ const std::vector<size_t>& selectedSubTracks =3D selection_[selecti=
onIndex].subtitleTracks();
+
+ for (size_t index =3D 0; index < selectedSubTracks.size(); ++index)
+ {
+ _stream =3D selectedSubTracks[index];
+ demuxSubtitleTrack(title, menu, _subTracks, _stream, demuxer, fil=
ename, _palette, _width, _height);
+ }
+ }
+ else
+ {
+ for (_stream =3D 0; _stream < _subTracks.size(); ++_stream)
+ demuxSubtitleTrack(title, menu, _subTracks, _stream, demuxer, file=
name, _palette, _width, _height);
+ }
+
+ // create a possible button demuxer too
+ Writer *_muxer =3D new BtnDemuxWriter(prefix, _width, _height);
+
+ QString commandLine =3D btnMuxArgumentsFormat.arg(filename, prefix);
+ if (!demuxer.AddDemuxer(SUBSTREAM_PCI, _muxer, commandLine))
+ delete _muxer;
+ }
+
+ if (consoleMode_)
+ printf("0%%");
+ else
+ emit progressChanged(0);
+ =09
+ const uint32_t maximum =3D aVobParser->GetPacketCount();
+ =09
+ while(aVobParser->ParseNextPacket(*CellsList) && !needsAbort_)
+ {
+ if (consoleMode_)
+ printf("\r\r\r%d%%", aVobParser->GetPacketIndex() * 100 / maximum);
+ else
+ emit progressChanged(aVobParser->GetPacketIndex() * 100 / maximum);
+ }
+ printf("\n");
+
+ // create the command line using the list of used files
+ // always put video first
+ if (demuxer.FileExists(VIDEO_STREAM))
+ muxCommand +=3D demuxer.GetString(VIDEO_STREAM);
+
+ for (_stream =3D 0; _stream < 256; _stream++)
+ {
+ if (_stream =3D=3D VIDEO_STREAM)
+ continue;
+ =09
+ if (demuxer.FileExists(_stream))
+ muxCommand +=3D demuxer.GetString(_stream);
+ }
+ }
+
+ CellsListType *CellsListDone =3D (CellsListType *)CellsList;
+ CellsListDone->arrange();
+
+ bool addChapters =3D false;
+ ChapterManager chapterEditor(2 /*indent count*/);
+
+ if (menu)
+ addChapters =3D chapterEditor.generateMenuScript(*ifoFile_, prefix, t=
itle, editionUID);
+ else
+ addChapters =3D chapterEditor.generateScript(*ifoFile_, prefix, title=
, editionUID);
+
+ if (addChapters)
+ {
+ muxCommand +=3D " --chapters \"" + prefix + ChapterManager::CHAPTER_S=
UFFIX + "\"";
+ muxCommand +=3D " --segmentinfo \"" + prefix + ChapterManager::INFO_S=
UFFIX + "\"";
+ }
+
+#if (defined(WIN32) || defined(WIN64))
+ muxCommand.insert(0, "\"" + toolsPath_ + "\\mkvmerge\" -o \"" + prefix=
+ ".mkv\"");
+ QFile muxBatchFile(prefix + "_" + QTime::currentTime().toString("hh_mm=
_ss") + ".bat");
+#else
+ muxCommand.insert(0, toolsPath_ + "/mkvmerge -o \"" + prefix + ".mkv\"=
");
+ QFile muxBatchFile(prefix + "_" + QTime::currentTime().toString("hh_mm=
_ss") + ".sh");
+#endif
+
+ if (!muxBatchFile.open( QIODevice::WriteOnly | QIODevice::Text ))
+ fprintf(stderr, "Could not create batch file\n");
+ =09
+#if !defined(WIN32) && !defined(WIN64)
+ QString shellString ("#!/bin/sh\n\n");
+ muxBatchFile.write(shellString.toUtf8());
+#endif
+ muxBatchFile.write(muxCommand.toUtf8());
+ muxBatchFile.close();
+
+ printf("Done demuxing %s\n", qPrintable(filename));
+ }
+ catch(VobParserException e)
+ {
+ fprintf(stderr, "Vob Parser Exception Occurred: %s\n", e.what());
+ }
+}
Added: trunk/DvdMenuXtractor/dmx/dmx.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/DvdMenuXtractor/dmx/dmx.h 2007-03-08 09:04:37 UTC (rev 1280)
+++ trunk/DvdMenuXtractor/dmx/dmx.h 2007-03-10 21:14:50 UTC (rev 1281)
@@ -0,0 +1,54 @@
+#ifndef DMX_H
+#define DMX_H
+
+#include <vector>
+#include <QThread>
+#include "dmxselectionitem.h"
+#include "vobparser/IFOFile.h"
+
+class DMX : public QThread
+{
+ Q_OBJECT
+
+public:
+ typedef std::vector<DMXSelectionItem> SelectionType;
+
+ DMX(bool consoleMode =3D false);
+ ~DMX();
+
+ static IFOFile* OpenIFOFile(const QString& path);
+ bool setExtractionParameters(const QString& sourcePath, const QString& =
destinationPath, const QString& toolsPath, const SelectionType& selection=
);
+=09
+signals:
+ // Signal is emitted when the current step progress is changed
+ void progressChanged(int);
+
+ // Signal is emitted when the step is changed
+ void stepChanged(const QString&);
+
+public slots:
+ void abort();
+
+protected:
+ void run();
+
+private:
+ IFOFile *ifoFile_;
+ bool consoleMode_;
+ QString toolsPath_;
+ QString sourcePath_;
+ QString destinationPath_;
+ SelectionType selection_;
+ volatile bool needsAbort_;
+=09
+ bool loadIFOFile(const QString& path);
+ void processTitle(int16_t title, int index);
+
+ VobParser* buildVobParser(int16_t title, bool isMenu);
+=09
+ void demux(VobParser* aVobParser, int selectionIndex, const QString& ed=
itionUID, int16_t title, bool isMenu);
+ void demuxAudioTrack(int16_t title, bool isMenu, const AudioTrackList& =
_audioTracks, size_t _stream, CompositeDemuxWriter& demuxer, const QStrin=
g& filename);
+ void demuxSubtitleTrack(int16_t title, bool isMenu, const SubtitleTrack=
List& _subTracks, size_t _stream, CompositeDemuxWriter& demuxer, const Q=
String& filename, const uint32_t *_palette, uint16_t _width, uint16_t _he=
ight);
+};
+
+#endif // DMX_H
Added: trunk/DvdMenuXtractor/dmx/dmx.proj
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/DvdMenuXtractor/dmx/dmx.proj 2007-03-08 09:04:37 UTC (rev 1280)
+++ trunk/DvdMenuXtractor/dmx/dmx.proj 2007-03-10 21:14:50 UTC (rev 1281)
@@ -0,0 +1,38 @@
+#include "*/*.proj"
+
+LIB dmx
+{
+ PROJECT_VERSION 0.9.9
+ PROJECT_BUILD 1278
+ PROJECT_NAME "DMX"
+ PROJECT_VENDOR qdmx
+ PROJECT_FOURCC QDMX
+ =20
+ USE dvdread
+ USE vobparser
+ USE mpegparser
+ =20
+ DEFINE __STDC_LIMIT_MACROS
+ DEFINE(QT_NO_DEBUG) QT_NO_DEBUG_STREAM
+
+ INCLUDE libdvdread
+ =20
+ INCLUDE(COMPILER_MSVC) libdvdread/win32
+ INCLUDE(COMPILER_MSVC) "$(QTDIR)/include"
+ INCLUDE(COMPILER_MSVC) "$(QTDIR)/include/QtXml"
+ INCLUDE(COMPILER_MSVC) "$(QTDIR)/include/QtCore"
+ =20
+ INCLUDE(COMPILER_GCC) "$(QTDIR)/include/qt4/"
+ INCLUDE(COMPILER_GCC) "$(QTDIR)/include/qt4/QtXml"
+ INCLUDE(COMPILER_GCC) "$(QTDIR)/include/qt4/QtCore"
+ =20
+ SOURCE dmx.cpp
+ SOURCE utilities.cpp
+ SOURCE chaptermanager.cpp
+ SOURCE dmxselectionitem.cpp
+
+ HEADER_QT4 dmx.h
+ HEADER utilities.h
+ HEADER chaptermanager.h
+ HEADER dmxselectionitem.h
+}
Added: trunk/DvdMenuXtractor/dmx/dmx_project.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/DvdMenuXtractor/dmx/dmx_project.h 2007-03-08 09:04:37 UTC (rev =
1280)
+++ trunk/DvdMenuXtractor/dmx/dmx_project.h 2007-03-10 21:14:50 UTC (rev =
1281)
@@ -0,0 +1,8 @@
+/* GENERATED FILE, DO NOT EDIT */
+
+#define PROJECT_VERSION _T("0.9.9")
+#define PROJECT_BUILD 1278
+#define PROJECT_NAME _T("DMX")
+#define PROJECT_VENDOR _T("qdmx")
+#define PROJECT_FOURCC _T("QDMX")
+
Added: trunk/DvdMenuXtractor/dmx/dmxselectionitem.cpp
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/DvdMenuXtractor/dmx/dmxselectionitem.cpp 2007-03-08 09:04:37 UT=
C (rev 1280)
+++ trunk/DvdMenuXtractor/dmx/dmxselectionitem.cpp 2007-03-10 21:14:50 UT=
C (rev 1281)
@@ -0,0 +1,68 @@
+#include <algorithm>
+#include "dmxselectionitem.h"
+
+DMXSelectionItem::DMXSelectionItem(int16_t title, bool isMenu, bool enab=
leVideo =3D 1)
+ : title_(title), menu_(isMenu), videoEnabled_(enableVideo)
+{
+ // override for VMG
+ if (!title)
+ menu_ =3D true;
+}
+
+DMXSelectionItem::~DMXSelectionItem()
+{
+}
+
+int16_t DMXSelectionItem::title() const
+{
+ return title_;
+}
+
+bool DMXSelectionItem::isMenu() const
+{
+ return menu_;
+}
+
+bool DMXSelectionItem::isVideoEnabled() const
+{
+ return videoEnabled_;
+}
+
+const DMXSelectionItem::TracksContainerType& DMXSelectionItem::audioTrac=
ks() const
+{
+ return audioTracks_;
+}
+
+const DMXSelectionItem::TracksContainerType& DMXSelectionItem::subtitleT=
racks() const
+{
+ return subtitleTracks_;
+}
+
+void DMXSelectionItem::disableVideo()
+{
+ videoEnabled_ =3D false;
+}
+
+bool DMXSelectionItem::addAudioTrack(size_t trackIndex)
+{
+ // check if audio track was already selected
+ if (std::count(audioTracks_.begin(), audioTracks_.end(), trackIndex) =3D=
=3D 0)
+ {
+ audioTracks_.push_back(trackIndex);
+ return true;
+ }
+
+ return false;
+}
+
+bool DMXSelectionItem::addSubtitleTrack(size_t trackIndex)
+{
+ // check if audio track was already selected
+ if (std::count(subtitleTracks_.begin(), subtitleTracks_.end(), trackInd=
ex) =3D=3D 0)
+ {
+ subtitleTracks_.push_back(trackIndex);
+ return true;
+ }
+
+ return false;
+}
Added: trunk/DvdMenuXtractor/dmx/dmxselectionitem.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/DvdMenuXtractor/dmx/dmxselectionitem.h 2007-03-08 09:04:37 UTC =
(rev 1280)
+++ trunk/DvdMenuXtractor/dmx/dmxselectionitem.h 2007-03-10 21:14:50 UTC =
(rev 1281)
@@ -0,0 +1,38 @@
+#ifndef DMX_SELECTION_ITEM
+#define DMX_SELECTION_ITEM
+
+#include <vector>
+#include <stdint.h>
+
+// Denotes each selected title
+class DMXSelectionItem
+{
+public:
+
+ typedef std::vector<size_t> TracksContainerType;
+
+ DMXSelectionItem(int16_t title, bool isMenu, bool enableVideo);
+ ~DMXSelectionItem();
+
+ // Getters
+ bool isMenu() const;
+ int16_t title() const;
+ bool isVideoEnabled() const;
+ const TracksContainerType& audioTracks() const;
+ const TracksContainerType& subtitleTracks() const;
+
+ void disableVideo();
+ bool addAudioTrack(size_t trackIndex);
+ bool addSubtitleTrack(size_t trackIndex);
+
+private:
+ int16_t title_;
+ bool menu_;
+ bool videoEnabled_;
+
+ TracksContainerType audioTracks_;
+ TracksContainerType subtitleTracks_;
+};
+
+
+#endif // DMX_SELECTION_ITEM
Added: trunk/DvdMenuXtractor/dmx/utilities.cpp
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/DvdMenuXtractor/dmx/utilities.cpp 2007-03-08 09:04:37 UTC (rev =
1280)
+++ trunk/DvdMenuXtractor/dmx/utilities.cpp 2007-03-10 21:14:50 UTC (rev =
1281)
@@ -0,0 +1,32 @@
+#include <iostream>
+#include "utilities.h"
+
+QString Utilities::CreateUID()
+{
+ return QString("%1").arg( uint32_t(rand() | (rand() << 16)) );
+}
+
+QString Utilities::FormatTime(uint64_t a_time)
+{
+ int hour, min, sec;
+ uint32_t nano;
+
+ nano =3D a_time % 1000000000;
+ a_time /=3D 1000000000;
+ sec =3D a_time % 60;
+ a_time /=3D 60;
+ min =3D a_time % 60;
+ hour =3D a_time / 60;
+
+ return QString("%1:%2:%3.%4").arg(hour, 2, 10, QChar('0')).arg(min, 2, =
10, QChar('0')).arg(sec, 2, 10, QChar('0')).arg(nano, 9, 10, QChar('0'));
+}
+
+QString Utilities::EncodeHex(const unsigned char *buffer, unsigned int s=
ize)
+{
+ QString result;
+
+ for (unsigned i =3D 0; i < size; ++i)
+ result +=3D QString("%1 ").arg(buffer[i], 2, 16, QChar('0'));
+
+ return result.trimmed();
+}
Added: trunk/DvdMenuXtractor/dmx/utilities.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/DvdMenuXtractor/dmx/utilities.h 2007-03-08 09:04:37 UTC (rev 12=
80)
+++ trunk/DvdMenuXtractor/dmx/utilities.h 2007-03-10 21:14:50 UTC (rev 12=
81)
@@ -0,0 +1,20 @@
+#ifndef UTILITIES_H
+#define UTILITIES_H
+
+#include <QString>
+#include <stdint.h>
+#include "dmx_project.h"
+
+#define _T(x) (x)
+
+namespace Utilities
+{
+ static const QString APPLICATION_NAME ("DVDMenuXtractor: DMX - a fai=
r-use tool");
+ static const QString APPLICATION_VERSION PROJECT_VERSION;
+
+ QString CreateUID();
+ QString FormatTime(uint64_t a_time);
+ QString EncodeHex(const unsigned char *buffer, unsigned size);
+}
+
+#endif // UTILITIES_H