[Matroska-cvs] [matroska] r1281 - in trunk/DvdMenuXtractor: . dmx
smssms at matroska.org
smssms at matroska.org
Sat Mar 10 22:15:13 CET 2007
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
===================================================================
--- 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 QString& filename, uint16_t title, const QString& editionUID) const
+{
+ static unsigned char _PrivateVTS[] = {0x30, 0x80, 0x00, 0x00};
+ static unsigned char _PrivateTT[] = {0x28, 0x00, 0x00, 0x00};
+ uint64_t start_timeH = uint64_t(-1);
+
+ generateInfoScript(filename, title, editionUID);
+
+ // create document
+ QDomDocument document;
+
+ // specify processing info
+ document.appendChild(document.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\""));
+ document.appendChild(document.implementation().createDocumentType("Chapters", QString::null, "matroskachapters.dtd"));
+
+ // create comments
+ document.appendChild(document.createComment("Created with " + Utilities::APPLICATION_NAME + " " + Utilities::APPLICATION_VERSION));
+
+ // create the root element
+ QDomNode root = document.createElement("Chapters");
+ document.appendChild(root);
+
+ // VTS content
+ QDomElement editionEntryElement = document.createElement("EditionEntry");
+ root.appendChild(editionEntryElement);
+
+ editionEntryElement.appendChild(CreateDOMElement(document, "EditionUID", editionUID));
+ editionEntryElement.appendChild(CreateDOMElement(document, "EditionFlagOrdered", "1"));
+ editionEntryElement.appendChild(document.createComment("Video Title Set"));
+
+ QDomElement chapterAtomElement = document.createElement("ChapterAtom");
+ editionEntryElement.appendChild(chapterAtomElement);
+
+ QString _myUIDa = Utilities::CreateUID();
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterUID", _myUIDa));
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterFlagHidden", "1"));
+
+ _PrivateVTS[2] = title >> 8;
+ _PrivateVTS[3] = title & 0xFF;
+ AddCodecPrivateData(document, chapterAtomElement, _PrivateVTS, 4);
+
+ QDomNode chapterAtomParentElement = chapterAtomElement;
+
+ if (ifoFile.VtsTitles(title) && ifoFile.VtsPGCs(title))
+ {
+ for (int i = 0; i < ifoFile.VtsTitles(title)->nr_of_srpts; i++)
+ {
+ uint64_t start_time = uint64_t(-1), found_time;
+
+ chapterAtomParentElement.appendChild(document.createComment(QString("Title TTU_%1").arg(i + 1)));
+ QDomElement chapterAtomElement = document.createElement("ChapterAtom");
+ chapterAtomParentElement.appendChild(chapterAtomElement);
+
+ QString _myUIDa = Utilities::CreateUID();
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterUID", _myUIDa));
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterFlagHidden", "1"));
+
+ // get the Title -> VTS+TTN map
+ const tt_srpt_t *p_map = ifoFile.TitleMap();
+
+ if ( p_map != NULL )
+ {
+ for (int j=0; j<p_map->nr_of_srpts; j++)
+ {
+ if ( ( p_map->title[j].title_set_nr == title ) && ( p_map->title[j].vts_ttn == i+1 ) )
+ {
+ _PrivateTT[1] = (j+1) >> 8; // Title#
+ _PrivateTT[2] = (j+1) & 0xFF;
+ _PrivateTT[3] = i+1; // VTS_TTN#
+ AddCodecPrivateData(document, chapterAtomElement, _PrivateTT, 4);
+ break;
+ }
+ }
+ }
+
+ // add the PGC that match this PTT
+ for (int k = 0; k < ifoFile.VtsPGCs(title)->nr_of_pgci_srp; k++)
+ {
+ if ((ifoFile.VtsPGCs(title)->pgci_srp[k].entry_id & 0x7F) == i+1)
+ {
+ found_time = AddPGC(document, chapterAtomElement,
+ ifoFile.VtsPGCs(title)->pgci_srp[k].pgc, k, 0, ifoFile.CellsList(title, false), &ifoFile.VtsTitles(title)->title[i], i+1);
+
+ if (start_time > found_time)
+ start_time = found_time;
+ }
+ }
+
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterTimeStart", Utilities::FormatTime(start_time)));
+
+ if (start_timeH > start_time)
+ start_timeH = start_time;
+ }
+ }
+
+ if (start_timeH == uint64_t(-1))
+ start_timeH = 0;
+
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterTimeStart", Utilities::FormatTime(start_timeH)));
+
+ QString output = 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 QString& filename, uint16_t title, const QString& editionUID) const
+{
+ static unsigned char _PrivateFP[] = {0x30, 0x00, 0x00, 0x00};
+ static unsigned char _PrivateVM[] = {0x30, 0xC0, 0x00, 0x00};
+ static unsigned char _PrivateVTS[] = {0x30, 0x40, 0x00, 0x00};
+
+ // generate info script
+ generateInfoScript(filename, title, editionUID);
+
+ // create document
+ QDomDocument document;
+
+ // specify processing info
+ document.appendChild(document.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\""));
+ document.appendChild(document.implementation().createDocumentType("Chapters", QString::null, "matroskachapters.dtd"));
+
+ // create comments
+ document.appendChild(document.createComment("Created with " + Utilities::APPLICATION_NAME + " " + Utilities::APPLICATION_VERSION));
+
+ // create the root element
+ QDomNode parentElement = document.createElement("Chapters");
+ document.appendChild(parentElement);
+
+ // create Edition Entry Element and add to root
+ QDomElement currentElement = document.createElement("EditionEntry");
+ parentElement.appendChild(currentElement);
+
+ // use Edition Entry Element as parent
+ parentElement = currentElement;
+
+ // add EditionUID and EDitionFlagOrdered elements
+ parentElement.appendChild(CreateDOMElement(document, "EditionUID", editionUID));
+ parentElement.appendChild(CreateDOMElement(document, "EditionFlagOrdered", "1"));
+
+ // Write the first play PGC
+ if (title == 0 && ifoFile.FirstPlayPGC())
+ {
+ // add comment to Edition Entry Element
+ parentElement.appendChild(document.createComment("First Play PGC"));
+
+ // add Chapter Atom
+ QDomElement chapterAtomElement = document.createElement("ChapterAtom");
+ parentElement.appendChild(chapterAtomElement);
+
+ // create and set UID
+ QString _myUID = 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 = document.createElement("ChapterDisplay");
+ currentElement.appendChild(CreateDOMElement(document, "ChapterString", "First Play PGC " + _myUID));
+ chapterAtomElement.appendChild(currentElement);
+
+ // add Chapter Process Element
+ currentElement = document.createElement("ChapterProcess");
+ AddCodecPrivateData(document, currentElement, _PrivateFP, 4, false);
+ chapterAtomElement.appendChild(currentElement);
+
+ // PGC commands
+ AddPGCCommands(document, currentElement, ifoFile.FirstPlayPGC()->command_tbl);
+ }
+
+ // Write the Video Title Set
+ if (ifoFile.LanguageUnits(title) && ifoFile.LanguageUnits(title)->nr_of_lus)
+ {
+ // VTS Menu
+ if ( title == 0 )
+ parentElement.appendChild(document.createComment("Video Manager"));
+ else
+ parentElement.appendChild(document.createComment("Video Title Set"));
+
+ // add another Chapter Atom element
+ currentElement = document.createElement("ChapterAtom");
+ parentElement.appendChild(currentElement);
+
+ QString _myUIDa = Utilities::CreateUID();
+ currentElement.appendChild(CreateDOMElement(document, "ChapterUID", _myUIDa));
+ currentElement.appendChild(CreateDOMElement(document, "ChapterFlagHidden", "1"));
+
+ if ( title != 0 )
+ {
+ _PrivateVTS[2] = title >> 8;
+ _PrivateVTS[3] = title & 0xFF;
+ AddCodecPrivateData(document, currentElement, _PrivateVTS, 4);
+ }
+ else
+ {
+ _PrivateVM[2] = 0;
+ _PrivateVM[3] = 0;
+ AddCodecPrivateData(document, currentElement, _PrivateVM, 4);
+ }
+
+ uint64_t start_time = HandleLanguageUnit(document, currentElement, ifoFile, title);
+
+ currentElement.appendChild(CreateDOMElement(document, "ChapterTimeStart", Utilities::FormatTime(start_time)));
+ }
+
+ // get document string representation
+ QString output = 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;
+
+ // 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", "version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\""));
+ document.appendChild(document.implementation().createDocumentType("Chapters", QString::null, "matroskainfos.dtd"));
+
+ // add root element
+ QDomNode parentElement = document.createElement("Info");
+ document.appendChild(parentElement);
+
+ // add Segment Family Element
+ QDomElement currentElement = CreateDOMElement(document, "SegmentFamily", Utilities::EncodeHex(familyUID, 16));
+ currentElement.setAttribute("format", "hex");
+ parentElement.appendChild(currentElement);
+
+ // add Chapter Translate Element
+ currentElement = document.createElement("ChapterTranslate");
+ currentElement.appendChild(CreateDOMElement(document, "ChapterTranslateEditionUID", editionUID));
+ currentElement.appendChild(CreateDOMElement(document, "ChapterTranslateCodec", "1"));
+
+ QDomElement chapterTranslateIDElement = CreateDOMElement(document, "ChapterTranslateID", 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 = document.toString(INDENT_COUNT);
+
+ // segment file
+ QFile segementFile(filename + INFO_SUFFIX);
+ if (!segementFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ throw;
+
+ segementFile.write(output.toUtf8());
+ segementFile.close();
+}
+
+QDomElement ChapterManager::CreateDOMElement(QDomDocument &document, const QString &elementName, const QString &content)
+{
+ QDomElement element = document.createElement(elementName);
+ element.appendChild(document.createTextNode(content));
+
+ return element;
+}
+
+void ChapterManager::AddChapterTime(QDomDocument& document, QDomNode& parent, uint64_t start_time, uint64_t end_time)
+{
+ parent.appendChild(CreateDOMElement(document, "ChapterTimeStart", Utilities::FormatTime(start_time)));
+ parent.appendChild(CreateDOMElement(document, "ChapterTimeEnd", Utilities::FormatTime(end_time)));
+}
+
+void ChapterManager::AddCodecPrivateData(QDomDocument& document, QDomNode& parent, const unsigned char *buffer, unsigned int size, bool createChapterProcessElement /*= true*/)
+{
+ QDomNode node = parent;
+
+ if (createChapterProcessElement)
+ {
+ QDomElement chapterProcessElement = document.createElement("ChapterProcess");
+ parent.appendChild(chapterProcessElement);
+ node = chapterProcessElement;
+ }
+
+ node.appendChild(CreateDOMElement(document, "ChapterProcessCodecID", "1"));
+
+ QDomElement chapterProcessPrivateElement = CreateDOMElement(document, "ChapterProcessPrivate", Utilities::EncodeHex(buffer, size));
+ chapterProcessPrivateElement.setAttribute("format", "hex");
+ node.appendChild(chapterProcessPrivateElement);
+}
+
+void ChapterManager::AddPGCCommands(QDomDocument& document, QDomNode& parent, const pgc_command_tbl_t * command_tbl)
+{
+ if (command_tbl)
+ {
+ unsigned char *_buf = (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 = document.createElement("ChapterProcessCommand");
+ parent.appendChild(chapterCommandElement);
+
+ QDomElement chapterProcessTimeElement = document.createElement("ChapterProcessTime");
+ chapterProcessTimeElement.appendChild(document.createTextNode("1"));
+ chapterCommandElement.appendChild(chapterProcessTimeElement);
+
+ _buf[0] = command_tbl->nr_of_pre;
+ memcpy(&_buf[1], command_tbl->pre_cmds, command_tbl->nr_of_pre * 8);
+
+ QDomElement chapterProcessDataElement = CreateDOMElement(document, "ChapterProcessData", Utilities::EncodeHex(_buf, 1 + command_tbl->nr_of_pre * 8));
+ chapterProcessDataElement.setAttribute("format", "hex");
+ chapterCommandElement.appendChild(chapterProcessDataElement);
+ }
+
+ // Cell commands
+ if (command_tbl->nr_of_cell)
+ {
+ parent.appendChild(document.createComment("Cell commands"));
+
+ QDomElement chapterCommandElement = document.createElement("ChapterProcessCommand");
+ parent.appendChild(chapterCommandElement);
+
+ QDomElement chapterProcessTimeElement = document.createElement("ChapterProcessTime");
+ chapterProcessTimeElement.appendChild(document.createTextNode("0"));
+ chapterCommandElement.appendChild(chapterProcessTimeElement);
+
+ _buf = (unsigned char*) realloc(_buf, 1 + command_tbl->nr_of_cell * 8);
+ _buf[0] = command_tbl->nr_of_cell;
+ memcpy(&_buf[1], command_tbl->cell_cmds, command_tbl->nr_of_cell * 8);
+
+ QDomElement chapterProcessDataElement = CreateDOMElement(document, "ChapterProcessData", Utilities::EncodeHex(_buf, 1 + command_tbl->nr_of_cell * 8));
+ chapterProcessDataElement.setAttribute("format", "hex");
+ chapterCommandElement.appendChild(chapterProcessDataElement);
+ }
+
+ // Post commands
+ if (command_tbl->nr_of_post)
+ {
+ parent.appendChild(document.createComment("Post commands"));
+
+ QDomElement chapterCommandElement = document.createElement("ChapterProcessCommand");
+ parent.appendChild(chapterCommandElement);
+
+ QDomElement chapterProcessTimeElement = document.createElement("ChapterProcessTime");
+ chapterProcessTimeElement.appendChild(document.createTextNode("2"));
+ chapterCommandElement.appendChild(chapterProcessTimeElement);
+
+ _buf = (unsigned char*) realloc(_buf, 1+command_tbl->nr_of_post * 8);
+ _buf[0] = command_tbl->nr_of_post;
+ memcpy(&_buf[1], command_tbl->post_cmds, command_tbl->nr_of_post * 8);
+
+ QDomElement chapterProcessDataElement = CreateDOMElement(document, "ChapterProcessData", Utilities::EncodeHex(_buf, 1 + command_tbl->nr_of_post * 8));
+ chapterProcessDataElement.setAttribute("format", "hex");
+ chapterCommandElement.appendChild(chapterProcessDataElement);
+ }
+
+ free(_buf);
+ }
+}
+
+uint64_t ChapterManager::HandleLanguageUnit(QDomDocument& document, QDomNode& parent, const IFOFile& _ifo, int title)
+{
+ static unsigned char _PrivateLU[] = {0x2A, 0x00, 0x00, 0x00};
+ uint64_t start_time = uint64_t(-1), found_time;
+
+ QDomElement chapterAtomElement;
+ QDomElement chapterDisplayElement;
+ QDomElement chapterStringElement;
+ QDomElement chapterTimeStartElement;
+
+ for (int i = 0 ;i < _ifo.LanguageUnits(title)->nr_of_lus; i++)
+ {
+ parent.appendChild(document.createComment("Language Units"));
+
+ chapterAtomElement = document.createElement("ChapterAtom");
+ parent.appendChild(chapterAtomElement);
+
+ // the Language Unit for a given language
+ const pgci_lu_t &_LU = _ifo.LanguageUnits(title)->lu[i];
+ _PrivateLU[1] = _LU.lang_code >> 8;
+ _PrivateLU[2] = _LU.lang_code & 0xFF;
+ _PrivateLU[3] = _LU.lang_extension;
+
+ chapterAtomElement.appendChild(document.createComment(QString("Language: %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 = document.createElement("ChapterDisplay");
+ chapterAtomElement.appendChild(chapterDisplayElement);
+
+ chapterStringElement = document.createElement("ChapterString");
+ chapterStringElement.appendChild(document.createTextNode(QString("Language Unit for %1%2").arg(QChar(_LU.lang_code >> 8)).arg(QChar(_LU.lang_code & 0xFF))));
+ chapterDisplayElement.appendChild(chapterStringElement);
+
+ if (_LU.pgcit)
+ {
+ for (int j = 0; j < _LU.pgcit->nr_of_pgci_srp; j++)
+ {
+ const pgci_srp_t &_PGC_SRP = _LU.pgcit->pgci_srp[j];
+
+ found_time = AddPGC(document, chapterAtomElement, _PGC_SRP.pgc, j, _PGC_SRP.entry_id, _ifo.CellsList(title, true), NULL, 0);
+
+ if (start_time > found_time)
+ start_time = found_time;
+ }
+ }
+
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterTimeStart", 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 CellsListType & cell_list, const ttu_t * ptts, int title)
+{
+ static unsigned char _PrivatePGC[] = {0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ uint64_t start_time = 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 = document.createElement("ChapterAtom");
+ parent.appendChild(chapterAtomElement);
+
+ QString _myUID = Utilities::CreateUID();
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterUID", _myUID));
+
+ // display the UID for the lazy rippers
+ if (ptts == NULL)
+ {
+ chapterAtomElement.appendChild(document.createComment("Enter your text here"));
+
+ QDomElement chapterDisplayElement = document.createElement("ChapterDisplay");
+ chapterDisplayElement.appendChild(CreateDOMElement(document, "ChapterString", "PGC type " + GetPGCType(pgc_type)));
+ chapterAtomElement.appendChild(chapterDisplayElement);
+ }
+ else
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterFlagHidden", "1"));
+
+ // PGC commands
+ QDomElement chapterProcessElement = document.createElement("ChapterProcess");
+ chapterAtomElement.appendChild(chapterProcessElement);
+
+ _PrivatePGC[1] = (pgc_num+1) >> 8;
+ _PrivatePGC[2] = (pgc_num+1) & 0xFF;
+ _PrivatePGC[3] = pgc_type;
+ _PrivatePGC[4] = ((uint8_t*) &pgc->prohibited_ops)[0];
+ _PrivatePGC[5] = ((uint8_t*) &pgc->prohibited_ops)[1];
+ _PrivatePGC[6] = ((uint8_t*) &pgc->prohibited_ops)[2];
+ _PrivatePGC[7] = ((uint8_t*) &pgc->prohibited_ops)[3];
+
+ AddCodecPrivateData(document, chapterProcessElement, _PrivatePGC, 8, false);
+ AddPGCCommands(document, chapterProcessElement, pgc->command_tbl);
+
+ // Handle the Programs/Cells in the PGC
+ for (int i=0; i< pgc->nr_of_programs; i++)
+ {
+ found_time = AddProgram(document, chapterAtomElement, pgc, _myUID, i, cell_list, pgc_num, ptts, title);
+
+ if (start_time > found_time)
+ start_time = found_time;
+ }
+
+ if (start_time == uint64_t(-1))
+ start_time = 0;
+
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterTimeStart", Utilities::FormatTime(start_time)));
+
+ return start_time;
+}
+
+
+QString ChapterManager::GetPGCType(unsigned char entry_id)
+{
+ QString result ("0x%1 = ");
+ result = result.arg(entry_id, 2, 16, QChar('0'));
+
+ if (entry_id & 0x80)
+ result += "Entry + ";
+
+ switch (entry_id & ~0x80)
+ {
+ case 2:
+ result += "Title-Menu";
+ break;
+ case 3:
+ result += "Root Menu";
+ break;
+ case 4:
+ result += "Subpicture Menu";
+ break;
+ case 5:
+ result += "Audio Menu";
+ break;
+ case 6:
+ result += "Angle Menu";
+ break;
+ case 7:
+ result += "Chapter Menu";
+ break;
+ default:
+ result += "Basic PGC";
+ break;
+ }
+
+ return result;
+}
+
+
+uint64_t ChapterManager::AddProgram(QDomDocument& document, QDomNode& parent, 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[] = {0x10, 0x00};
+ static unsigned char _PrivatePG[] = {0x18, 0x00, 0x00};
+ static unsigned char _PrivateCN[] = {0x08, 0x00, 0x00, 0x00, 0x00};
+
+ uint64_t start_time = uint64_t(-1), end_time = 0;
+
+ // for this program
+ int entry_cell_num = pgc->program_map[program_number];
+ int program_last_cell_num = pgc->nr_of_cells;
+ int next_program_entry_cell_num;
+ if (program_number+1 == pgc->nr_of_programs)
+ next_program_entry_cell_num = program_last_cell_num+1;
+ else
+ next_program_entry_cell_num = pgc->program_map[program_number+1];
+
+ parent.appendChild(document.createComment(QString("Program PGN# %1 in this PGC").arg(program_number + 1)));
+
+ QDomElement chapterAtomElement = document.createElement("ChapterAtom");
+ parent.appendChild(chapterAtomElement);
+
+ QString _myUID = Utilities::CreateUID();
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterUID", _myUID));
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterFlagHidden", "1"));
+
+ _PrivatePG[1] = (program_number+1) >> 8;
+ _PrivatePG[2] = (program_number+1) & 0xFF;
+ AddCodecPrivateData(document, chapterAtomElement, _PrivatePG, 3);
+
+ if (ptts != NULL)
+ {
+ // find all PTT corresponding to this PGC/PG number
+ for (uint16_t pppt_nr = 0; pppt_nr < ptts->nr_of_ptts; pppt_nr++)
+ {
+ // use the start timecode of the first cell
+ const cell_position_t & position = pgc->cell_position[entry_cell_num-1];
+ const CellListElem * cell_elt = cell_list.at(position);
+ // TODO: make this test optional (create chapters even without the content)
+ if (cell_elt == NULL || !cell_elt->found)
+ continue;
+
+ start_time = cell_elt->start_time;
+
+ QDomElement chapterAtomParent = chapterAtomElement;
+
+ if (ptts->ptt[pppt_nr].pgcn == pgc_num+1 && ptts->ptt[pppt_nr].pgn == program_number+1)
+ {
+ chapterAtomElement.appendChild(document.createComment(QString("Chapter PTT#%1 [%2.%3]").arg(pppt_nr+1).arg(pgc_num+1, 2, 10, QChar('0')).arg(program_number+1, 2, 10, QChar('0'))));
+
+ chapterAtomElement = document.createElement("ChapterAtom");
+ chapterAtomParent.appendChild(chapterAtomElement);
+
+ chapterAtomElement.appendChild(document.createComment("Enter your text here"));
+
+ QDomElement chapterDisplayElement = document.createElement("ChapterDisplay");
+ chapterDisplayElement.appendChild(CreateDOMElement(document, "ChapterString", QString("Your Name Here For Chapter #%1.%2").arg(title).arg(pppt_nr + 1)));
+ chapterAtomElement.appendChild(chapterDisplayElement);
+
+ QString _myUID = Utilities::CreateUID();
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterUID", _myUID));
+
+ _PrivatePTT[1] = pppt_nr+1;
+ AddCodecPrivateData(document, chapterAtomElement, _PrivatePTT, 2);
+
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterTimeStart", Utilities::FormatTime(start_time)));
+ }
+
+ chapterAtomElement = chapterAtomParent;
+ }
+ }
+
+ QDomElement parentChapterAtom = chapterAtomElement;
+
+ for (int cell_num = entry_cell_num; cell_num<next_program_entry_cell_num; cell_num++)
+ {
+ // Output cells
+ const cell_position_t & position = pgc->cell_position[cell_num-1];
+ const cell_playback_t & cell = pgc->cell_playback[cell_num-1];
+
+ if (cell.block_mode != 0 && cell.block_mode != 3)
+ continue;
+
+ const CellListElem * cell_elt = cell_list.at(position);
+ // TODO: make this test optional (create chapters even without the content)
+ if (cell_elt == NULL || !cell_elt->found)
+ continue;
+
+ QString prefix = QString("Program %1 Cell CN#%2 ").arg( (cell_num == entry_cell_num) ? "Entry" : "", QString::number(cell_num));
+ QString middle = QString("[%1.%2]").arg(cell_elt->vobid, 2, 10, QChar('0')).arg(cell_elt->cellid);
+ QString suffix = QString(" (%1 angles)").arg(cell_num - entry_cell_num + 1);
+
+ if (cell.block_mode) // multi-angle
+ parentChapterAtom.appendChild(document.createComment(prefix + middle + suffix));
+ else
+ parentChapterAtom.appendChild(document.createComment(prefix + middle));
+
+ QDomElement chapterAtomElement = document.createElement("ChapterAtom");
+ parentChapterAtom.appendChild(chapterAtomElement);
+
+ QString _myUID = Utilities::CreateUID();
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterUID", _myUID));
+ chapterAtomElement.appendChild(CreateDOMElement(document, "ChapterFlagHidden", "1"));
+
+ _PrivateCN[1] = (position.vob_id_nr) >> 8;
+ _PrivateCN[2] = (position.vob_id_nr) & 0xFF;
+ _PrivateCN[3] = position.cell_nr;
+ _PrivateCN[4] = cell_num - entry_cell_num + 1; // Number of angles
+ AddCodecPrivateData(document, chapterAtomElement, _PrivateCN, 5);
+
+ if (cell.still_time == 0xFF) {
+ chapterAtomElement.appendChild(document.createComment("Infinite loop Still Cell"));
+
+ // output an additional tag to specify this is cell should loop infinitely
+ // post-process command, jump to timecode "st_time"
+ QDomElement chapterProcessElement = document.createElement("ChapterProcess");
+ chapterProcessElement.appendChild(CreateDOMElement(document, "ChapterProcessCodecID", "0"));
+ chapterProcessElement.appendChild(document.createComment("Post command: replay this cell"));
+ chapterAtomElement.appendChild(chapterProcessElement);
+
+ QDomElement chapterProcessCommand = document.createElement("ChapterProcessCommand");
+ chapterProcessCommand.appendChild(CreateDOMElement(document, "ChapterProcessTime", "2"));
+ chapterProcessElement.appendChild(chapterProcessCommand);
+
+ QDomElement chapterProcessDataElement = CreateDOMElement(document, "ChapterProcessData", "GotoAndPlay(" + PgcUID + ");" );
+ chapterProcessDataElement.setAttribute("format", "ascii");
+ chapterProcessCommand.appendChild(chapterProcessDataElement);
+ } else if (cell.still_time != 0)
+ chapterAtomElement.appendChild(document.createComment(QString("Still cell (%1s)").arg(cell.still_time)));
+
+ AddChapterTime(document, chapterAtomElement, cell_elt->start_time, cell_elt->start_time + cell_elt->duration);
+
+ if (end_time < cell_elt->start_time + cell_elt->duration)
+ end_time = cell_elt->start_time + cell_elt->duration;
+
+ if (start_time > cell_elt->start_time)
+ start_time = cell_elt->start_time;
+ }
+
+ if (start_time == uint64_t(-1))
+ start_time = 0; // unknown
+
+ parentChapterAtom.appendChild(CreateDOMElement(document, "ChapterTimeStart", Utilities::FormatTime(start_time)));
+
+ return start_time;
+}
Added: trunk/DvdMenuXtractor/dmx/chaptermanager.h
===================================================================
--- trunk/DvdMenuXtractor/dmx/chaptermanager.h 2007-03-08 09:04:37 UTC (rev 1280)
+++ trunk/DvdMenuXtractor/dmx/chaptermanager.h 2007-03-10 21:14:50 UTC (rev 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, uint16_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;
+
+ static QDomElement CreateDOMElement(QDomDocument& document, const QString& elementName, const QString& content);
+ static void AddChapterTime(QDomDocument& document, QDomNode& parent, uint64_t start_time, uint64_t end_time);
+ static void AddCodecPrivateData(QDomDocument& document, QDomNode& parent, const unsigned char *buffer, unsigned int size, bool createChapterProcessElement = true);
+ static void AddPGCCommands(QDomDocument& document, QDomNode& parent, const pgc_command_tbl_t * command_tbl);
+ static uint64_t AddProgram(QDomDocument& document, QDomNode& parent, 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 uint64_t AddPGC(QDomDocument& document, QDomNode& parent, const pgc_t * pgc, uint16_t pgc_num, unsigned char pgc_type, const CellsListType & cell_list, const ttu_t * ptts, int title);
+ static QString GetPGCType(unsigned char entry_id);
+ static uint64_t HandleLanguageUnit(QDomDocument& document, QDomNode& parent, const IFOFile& _ifo, int title);
+
+ uint8_t familyUID[16];
+ unsigned INDENT_COUNT;
+};
+
+#endif //CHAPTER_MANAGER_H
Added: trunk/DvdMenuXtractor/dmx/dmx.cpp
===================================================================
--- 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_ = 0;
+ mutex.unlock();
+}
+
+void DMX::abort()
+{
+ QMutex mutex;
+ mutex.lock();
+ needsAbort_ = true;
+ mutex.unlock();
+
+ wait();
+}
+
+void DMX::processTitle(int16_t title, int index)
+{
+ const QString editionUID = Utilities::CreateUID();
+
+ unsigned stepIndex = 0;
+ bool menu = ((index < 0) && !title) || ((index >= 0) && selection_[index].isMenu());
+ VobParser *aVobParser = 0;
+ QString str;
+ QString text = "Step %1 of 2: %3...";
+
+ do
+ {
+ printf("Treating Title %d %s VOB file(s)\n", title, menu ? "Menu" : "");
+
+ stepIndex = 1;
+
+ str = text.arg(stepIndex++).arg("Building VOB map");
+
+ if (consoleMode_)
+ printf(qPrintable(str + '\n'));
+ else
+ emit stepChanged(str);
+
+ aVobParser = buildVobParser(title, menu);
+
+ str = text.arg(stepIndex++).arg("Splitting and demuxing");
+
+ if (consoleMode_)
+ printf(qPrintable(str + '\n'));
+ else
+ emit stepChanged(str);
+
+ demux(aVobParser, index, editionUID, title, menu);
+
+ delete aVobParser;
+
+ menu = !menu && ((index < 0) || selection_[index].isMenu());
+ } while (menu && !needsAbort_);
+}
+
+bool DMX::setExtractionParameters(const QString& sourcePath, const QString& destinationPath, const QString& toolsPath, const SelectionType& selectedItems)
+{
+ QMutex mutex;
+ QMutexLocker locker (&mutex);
+
+ // set selection
+ selection_ = selectedItems;
+
+ // check if require directories exist
+ if (!QFile::exists(sourcePath))
+ {
+ fprintf(stderr, "Input folder '%s' doesn't exist\n", qPrintable(sourcePath));
+ return false;
+ }
+
+ // set source path
+ sourcePath_ = sourcePath;
+
+ if (!QFile::exists(toolsPath))
+ {
+ fprintf(stderr, "Tools folder '%s' doesn't exist\n", qPrintable(toolsPath));
+ return false;
+ }
+
+ // set "mkvmerge.exe" path
+ toolsPath_ = toolsPath;
+
+ // try to create output
+ if (!QFile::exists(destinationPath))
+ {
+ if (!QDir().mkpath(destinationPath))
+ {
+ fprintf(stderr,"Cannot create output folder: '%s'\n", qPrintable(destinationPath));
+ return false;
+ }
+ else
+ printf("Successfully created output folder: '%s'\n", qPrintable(destinationPath));
+ }
+
+ // set output path
+ destinationPath_ = destinationPath;
+
+ return true;
+}
+
+void DMX::run()
+{
+ // try to open input file
+ if (!sourcePath_.size() || !loadIFOFile(sourcePath_))
+ return;
+
+ QMutex mutex;
+ mutex.lock();
+ needsAbort_ = false;
+ mutex.unlock();
+
+ if (selection_.size()) // if selection is available
+ {
+ for (size_t index = 0; index < selection_.size(); ++index)
+ {
+ int16_t title = selection_[index].title();
+
+ if (title >= 0)
+ processTitle(title, index);
+ }
+ }
+ else // if no selection is available, process all titles
+ {
+ for (int16_t title = 0; title <= ifoFile_->NumberOfTitles(); ++title)
+ processTitle(title, -1);
+ }
+}
+
+IFOFile* DMX::OpenIFOFile(const QString& path)
+{
+ IFOFile *file = 0;
+ QDir info (path);
+
+ try
+ {
+ file = 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);
+
+ // reset last file
+ delete ifoFile_;
+ ifoFile_ = OpenIFOFile(path);
+
+ if (ifoFile_ == 0)
+ return false;
+
+ return true;
+}
+
+VobParser* DMX::buildVobParser(int16_t title, bool menu)
+{
+ VobParser* parser = 0;
+ if (consoleMode_)
+ printf("0%%");
+ else
+ emit progressChanged(0);
+
+ try
+ {
+ parser = new VobParser(qPrintable(sourcePath_), title, menu);
+ } catch (...)
+ {
+ if (consoleMode_)
+ printf("\r\r");
+
+ printf("No VOB file(s) found in %s for Title %d\n", qPrintable(sourcePath_), title);
+ }
+
+ if (consoleMode_)
+ printf("\r\r100%%\n");
+ else
+ emit progressChanged(100);
+
+ return parser;
+}
+
+void DMX::demuxAudioTrack(int16_t title, bool isMenu, const AudioTrackList& _audioTracks, size_t _stream, CompositeDemuxWriter& demuxer, const QString& filename)
+{
+ if (_stream < 0 || _stream >= _audioTracks.size())
+ return;
+
+ static const QString muxArgumentsFormat(" --track-name 0:\"aud-%1\" --language 0:%2 --timecodes 0:\"%3_%4.tmc\" \"%3.%4\"");
+
+ const audio_attr_t *_attr = _audioTracks[_stream];
+ // get the possible ID for this track for this Cell/PGC
+ uint8_t _ID = ifoFile_->GetAudioId(_stream, title, isMenu);
+
+ Writer *_muxer = 0;
+
+ // if (_attr->lang_code == 0)
+ QString lang ("und");
+ QString langSuffix (QString("_%1_un").arg(_ID));
+
+ if (_attr->lang_code != 0)
+ {
+ lang = QString("%1%2").arg(QString(_attr->lang_code >> 8), QString(_attr->lang_code & 0xFF));
+ langSuffix = 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 + langSuffix);
+
+ switch (_audioTracks[_stream]->audio_format)
+ {
+ case 0:
+ muxArguments = muxArgumentsFormat.arg(filename + langSuffix, lang, fullPrefix, "ac3");
+ _muxer = new AC3DemuxWriter(fullPrefix, 0x80 + _ID);
+
+ if (!demuxer.AddDemuxer(SUBSTREAM_AC3_LOW + _ID, _muxer, muxArguments))
+ delete _muxer;
+ break;
+
+ case 2:
+ case 3:
+ muxArguments = muxArgumentsFormat.arg(filename + langSuffix, lang, fullPrefix, "mpa");
+ _muxer = new MPADemuxWriter(fullPrefix, 0xC0 + _ID);
+
+ if (!demuxer.AddDemuxer(AUDIO_STREAM + _ID, _muxer, muxArguments))
+ delete _muxer;
+ break;
+
+ case 4:
+ // TODO possibly other LPCM formats ?
+ muxArguments = muxArgumentsFormat.arg(filename + langSuffix, lang, fullPrefix, "wav");
+ _muxer = new LPCMDemuxWriter(fullPrefix, 0xA0 + _ID, 48000, 16, 2);
+
+ if (!demuxer.AddDemuxer(SUBSTREAM_PCM_LOW + _ID, _muxer, muxArguments))
+ delete _muxer;
+ break;
+
+ case 6:
+ muxArguments = muxArgumentsFormat.arg(filename + langSuffix, lang, fullPrefix, "dts");
+ _muxer = 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 SubtitleTrackList& _subTracks, size_t _stream, CompositeDemuxWriter& demuxer, const QString& filename, const uint32_t *_palette, uint16_t _width, uint16_t _height)
+{
+ if (_stream < 0 || _stream >= _subTracks.size())
+ return;
+
+ static const QString subMuxArgumentsFormat(" --track-name 0:\"sub-%1\" --language 0:%2 \"%3\"");
+
+ const subp_attr_t *_attr = 0;
+ const QString prefix = destinationPath_ + QDir::separator() + filename;
+
+ // get the possible IDs for this track for this Cell/PGC
+ IdArray _IDs = ifoFile_->GetSubsId(_stream, title, isMenu);
+
+ Writer * _muxer = 0;
+ QString lang, langSuffix;
+
+ for (IdArray::size_type _IDidx=0; _IDidx < _IDs.size(); ++_IDidx)
+ {
+ _attr = _subTracks[_stream];
+
+ // if (_attr->lang_code == 0)
+ lang = "und";
+ langSuffix = QString("_%1_un").arg(_IDs.at(_IDidx));
+
+ if (_attr->lang_code != 0)
+ {
+ lang = QString("%1%2 \"").arg(QString(_attr->lang_code >> 8), QString(_attr->lang_code & 0xFF));
+ langSuffix = QString("_%1_%2%3").arg(QString::number(_IDs.at(_IDidx)), QString(_attr->lang_code >> 8), QString(_attr->lang_code & 0xFF));
+ }
+
+ _muxer = new SubDemuxWriter(QString(prefix + langSuffix), 0x20 + _IDs.at(_IDidx), _width, _height, _palette, _attr->lang_code, _attr->lang_extension == 9);
+
+ QString commandLine = subMuxArgumentsFormat.arg(filename + langSuffix, lang, prefix + langSuffix + ".idx");
+ if (!demuxer.AddDemuxer(SUBSTREAM_SUB_LOW + _IDs.at(_IDidx), _muxer, commandLine))
+ 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 = ifoFile_->GetCellsList(title, menu);
+
+ if (!CellsList)
+ return;
+
+ static const QString demuxArguments (" --track-name 0:\"video\" --timecodes 0:\"%1_m2v.tmc\" \"%1.m2v\"");
+ static const QString btnMuxArgumentsFormat(" --track-name 0:\"btn-%1\" --timecodes 0:\"%2_btn.tmc\" \"%2.btn\"");
+
+ try
+ {
+ QString filename;
+ QString muxCommand;
+
+ // init filename
+ if (title == 0)
+ filename = "VMG";
+ else
+ filename = QString("VTS%1%2").arg(menu ? "M" : "").arg(title, 2, 10, QChar('0'));
+
+ const QString prefix = destinationPath_ + QDir::separator() + filename;
+
+ if (aVobParser != 0)
+ {
+ aVobParser->Reset();
+
+ const AudioTrackList & _audioTracks = ifoFile_->AudioTracks(title, menu);
+ const SubtitleTrackList & _subTracks = ifoFile_->SubsTracks(title, menu);
+
+ double _fps = 0;
+ uint16_t _width = 0, _height = 0;
+
+ ifoFile_->VideoSize(title, menu, _width, _height, _fps);
+ const uint32_t * _palette = ifoFile_->GetPalette(title, menu);
+
+ CompositeDemuxWriter &demuxer = aVobParser->GetDemuxer();
+
+ //emit progressChanged(CellsList->count());
+
+ demuxer.Reset();
+ printf("Processing %s\n", qPrintable(filename));
+
+ if ((selectionIndex < 0) || (selection_[selectionIndex].isVideoEnabled()))
+ {
+ Writer *_muxer = new VideoDemuxWriter(prefix, _fps);
+
+ QString commandLine = demuxArguments.arg(prefix);
+ if (!demuxer.AddDemuxer(VIDEO_STREAM, _muxer, commandLine))
+ delete _muxer;
+ }
+
+ size_t _stream = 0;
+
+ QString lang;
+ QString langSuffix;
+
+ // decide which audio streams to process
+ if (_audioTracks.size())
+ {
+ if (selectionIndex > -1)
+ {
+ const std::vector<size_t>& selectedAudioTracks = selection_[selectionIndex].audioTracks();
+
+ for (size_t index = 0; index < selectedAudioTracks.size(); ++index)
+ {
+ _stream = selectedAudioTracks[index];
+ demuxAudioTrack(title, menu, _audioTracks, _stream, demuxer, filename);
+ }
+ }
+ else
+ {
+ for (_stream = 0; _stream < _audioTracks.size(); ++_stream)
+ demuxAudioTrack(title, menu, _audioTracks, _stream, demuxer, filename);
+ }
+ }
+
+ // process subtitle streams
+ if (_subTracks.size())
+ {
+ if (selectionIndex > -1)
+ {
+ const std::vector<size_t>& selectedSubTracks = selection_[selectionIndex].subtitleTracks();
+
+ for (size_t index = 0; index < selectedSubTracks.size(); ++index)
+ {
+ _stream = selectedSubTracks[index];
+ demuxSubtitleTrack(title, menu, _subTracks, _stream, demuxer, filename, _palette, _width, _height);
+ }
+ }
+ else
+ {
+ for (_stream = 0; _stream < _subTracks.size(); ++_stream)
+ demuxSubtitleTrack(title, menu, _subTracks, _stream, demuxer, filename, _palette, _width, _height);
+ }
+
+ // create a possible button demuxer too
+ Writer *_muxer = new BtnDemuxWriter(prefix, _width, _height);
+
+ QString commandLine = btnMuxArgumentsFormat.arg(filename, prefix);
+ if (!demuxer.AddDemuxer(SUBSTREAM_PCI, _muxer, commandLine))
+ delete _muxer;
+ }
+
+ if (consoleMode_)
+ printf("0%%");
+ else
+ emit progressChanged(0);
+
+ const uint32_t maximum = aVobParser->GetPacketCount();
+
+ 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 += demuxer.GetString(VIDEO_STREAM);
+
+ for (_stream = 0; _stream < 256; _stream++)
+ {
+ if (_stream == VIDEO_STREAM)
+ continue;
+
+ if (demuxer.FileExists(_stream))
+ muxCommand += demuxer.GetString(_stream);
+ }
+ }
+
+ CellsListType *CellsListDone = (CellsListType *)CellsList;
+ CellsListDone->arrange();
+
+ bool addChapters = false;
+ ChapterManager chapterEditor(2 /*indent count*/);
+
+ if (menu)
+ addChapters = chapterEditor.generateMenuScript(*ifoFile_, prefix, title, editionUID);
+ else
+ addChapters = chapterEditor.generateScript(*ifoFile_, prefix, title, editionUID);
+
+ if (addChapters)
+ {
+ muxCommand += " --chapters \"" + prefix + ChapterManager::CHAPTER_SUFFIX + "\"";
+ muxCommand += " --segmentinfo \"" + prefix + ChapterManager::INFO_SUFFIX + "\"";
+ }
+
+#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");
+
+#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
===================================================================
--- 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 = false);
+ ~DMX();
+
+ static IFOFile* OpenIFOFile(const QString& path);
+ bool setExtractionParameters(const QString& sourcePath, const QString& destinationPath, const QString& toolsPath, const SelectionType& selection);
+
+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_;
+
+ bool loadIFOFile(const QString& path);
+ void processTitle(int16_t title, int index);
+
+ VobParser* buildVobParser(int16_t title, bool isMenu);
+
+ void demux(VobParser* aVobParser, int selectionIndex, const QString& editionUID, int16_t title, bool isMenu);
+ void demuxAudioTrack(int16_t title, bool isMenu, const AudioTrackList& _audioTracks, size_t _stream, CompositeDemuxWriter& demuxer, const QString& filename);
+ void demuxSubtitleTrack(int16_t title, bool isMenu, const SubtitleTrackList& _subTracks, size_t _stream, CompositeDemuxWriter& demuxer, const QString& filename, const uint32_t *_palette, uint16_t _width, uint16_t _height);
+};
+
+#endif // DMX_H
Added: trunk/DvdMenuXtractor/dmx/dmx.proj
===================================================================
--- 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
+
+ USE dvdread
+ USE vobparser
+ USE mpegparser
+
+ DEFINE __STDC_LIMIT_MACROS
+ DEFINE(QT_NO_DEBUG) QT_NO_DEBUG_STREAM
+
+ INCLUDE libdvdread
+
+ INCLUDE(COMPILER_MSVC) libdvdread/win32
+ INCLUDE(COMPILER_MSVC) "$(QTDIR)/include"
+ INCLUDE(COMPILER_MSVC) "$(QTDIR)/include/QtXml"
+ INCLUDE(COMPILER_MSVC) "$(QTDIR)/include/QtCore"
+
+ INCLUDE(COMPILER_GCC) "$(QTDIR)/include/qt4/"
+ INCLUDE(COMPILER_GCC) "$(QTDIR)/include/qt4/QtXml"
+ INCLUDE(COMPILER_GCC) "$(QTDIR)/include/qt4/QtCore"
+
+ 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
===================================================================
--- 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
===================================================================
--- trunk/DvdMenuXtractor/dmx/dmxselectionitem.cpp 2007-03-08 09:04:37 UTC (rev 1280)
+++ trunk/DvdMenuXtractor/dmx/dmxselectionitem.cpp 2007-03-10 21:14:50 UTC (rev 1281)
@@ -0,0 +1,68 @@
+#include <algorithm>
+#include "dmxselectionitem.h"
+
+DMXSelectionItem::DMXSelectionItem(int16_t title, bool isMenu, bool enableVideo = 1)
+ : title_(title), menu_(isMenu), videoEnabled_(enableVideo)
+{
+ // override for VMG
+ if (!title)
+ menu_ = 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::audioTracks() const
+{
+ return audioTracks_;
+}
+
+const DMXSelectionItem::TracksContainerType& DMXSelectionItem::subtitleTracks() const
+{
+ return subtitleTracks_;
+}
+
+void DMXSelectionItem::disableVideo()
+{
+ videoEnabled_ = false;
+}
+
+bool DMXSelectionItem::addAudioTrack(size_t trackIndex)
+{
+ // check if audio track was already selected
+ if (std::count(audioTracks_.begin(), audioTracks_.end(), trackIndex) == 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(), trackIndex) == 0)
+ {
+ subtitleTracks_.push_back(trackIndex);
+ return true;
+ }
+
+ return false;
+}
Added: trunk/DvdMenuXtractor/dmx/dmxselectionitem.h
===================================================================
--- 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
===================================================================
--- 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 = a_time % 1000000000;
+ a_time /= 1000000000;
+ sec = a_time % 60;
+ a_time /= 60;
+ min = a_time % 60;
+ hour = 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 size)
+{
+ QString result;
+
+ for (unsigned i = 0; i < size; ++i)
+ result += QString("%1 ").arg(buffer[i], 2, 16, QChar('0'));
+
+ return result.trimmed();
+}
Added: trunk/DvdMenuXtractor/dmx/utilities.h
===================================================================
--- trunk/DvdMenuXtractor/dmx/utilities.h 2007-03-08 09:04:37 UTC (rev 1280)
+++ trunk/DvdMenuXtractor/dmx/utilities.h 2007-03-10 21:14:50 UTC (rev 1281)
@@ -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 fair-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
More information about the Matroska-cvs
mailing list