1. Leyorus
  2. MkvExtract-Gtk

Commits

Leyorus  committed b176fc0

Use "Yamka" library to extract infos from the input file

As a consequence, the "mkvinfo" executable is now useless.

  • Participants
  • Parent commits fa0c5a5
  • Branches master

Comments (0)

Files changed (34)

File .project

View file
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>mkvextract-gtk</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+	</buildSpec>
+	<natures>
+	</natures>
+</projectDescription>

File CMakeLists.txt

View file
 
 find_package(PkgConfig)
 
-pkg_check_modules(GTKMM gtkmm-2.4) # look into FindPkgConfig.cmake, 
+pkg_check_modules(GTKMM gtkmm-3.0) # look into FindPkgConfig.cmake, 
 
 set(CMAKE_CXX_FLAGS "-O2 -Wall")
 

File src/CMakeLists.txt

View file
+add_subdirectory(yamka) 
+
 file(GLOB_RECURSE core_source_files	core/* )
 
-file(GLOB_RECURSE gui_source_files gui/* )
+file(GLOB_RECURSE gui_source_files gtk-gui/* )
+
+file(GLOB_RECURSE info_source_files info/* common/*)
+
+file(GLOB_RECURSE merge_source_files merge/* common/*)
 	
 link_directories(${GTKMM_LIBRARY_DIRS})
-include_directories(core ${GTKMM_INCLUDE_DIRS})
+include_directories(. yamka ${GTKMM_INCLUDE_DIRS})
 
 add_executable(
 	${PROJECT_NAME}
 target_link_libraries(
 	${PROJECT_NAME}
 	${GTKMM_LIBRARIES}
+	yamka
 	util # usefull for forkpty() function
-	)
+	)
+
+#set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++0x")

File src/core/MkvExtractor.h

View file
 
 }
 #endif
-

File src/core/MkvInfoParser.cpp

View file
 
 #include "MkvInfoParser.h"
 #include "Common.h"
-#include <stdio.h>
 
-namespace Core {
+#include "yamka/yamkaElt.h"
+#include "yamka/yamkaPayload.h"
+#include "yamka/yamkaStdInt.h"
+#include "yamka/yamkaFileStorage.h"
+#include "yamka/yamkaEBML.h"
+#include "yamka/yamkaMatroska.h"
 
-const std::string TRACK_STRING = "A track";
-const std::string CODEC_ID_STRING = "Codec ID: ";
-const std::string TRACK_TYPE_STRING = "Track type: ";
-const std::string TRACK_NUMBER_STRING = "Track number: ";
-const std::string LANGUAGE_STRING = "Language: ";
-
-std::string exec(std::string cmd) {
-    FILE* pipe = popen(cmd.c_str(), "r");
-    if (!pipe) return "ERROR";
-    char buffer[128];
-    std::string result = "";
-    while(!feof(pipe)) {
-        if(fgets(buffer, 128, pipe) != NULL)
-                result += buffer;
-    }
-    pclose(pipe);
-    return result;
-}
+using namespace Yamka;
 
-std::string MkvInfoParser::getRawMkvInfo(std::string filePath) {
-	std::string cmdline("mkvinfo \"" + filePath + "\"");
-	return exec(cmdline.c_str());
-}
 
+namespace Core {
 
-std::string substring_toend(std::string& s, int from) {
-	int end_of_line = s.find("\n", from);
-	return s.substr(from, end_of_line - from);
-}
 
-std::string parse(std::string& s, unsigned int track_number, std::string key) {
-	unsigned int current_track_number = 0;
-	size_t current_track_pos = 0;
-	size_t key_pos;
-
-	//looking for asked track position
-	do {
-		current_track_pos = s.find(TRACK_NUMBER_STRING, current_track_pos) + TRACK_NUMBER_STRING.size();
-		if (current_track_pos != std::string::npos) {
-			current_track_number = toInteger(substring_toend(s, current_track_pos));
-		} else {
-			return "unknown";
-		}
-	} while (current_track_number != track_number);
-
-	// looking for the next track (if there is any)
-	size_t next_track_pos = s.find(TRACK_NUMBER_STRING, current_track_pos);
-
-	if (next_track_pos != std::string::npos) {
-		key_pos = s.find(key, current_track_pos) + key.size();
-		if (key_pos != std::string::npos) {
-			if (key_pos < next_track_pos) {
-				return substring_toend(s, key_pos);
-			} else {
-				return "unknown";
-			}
-		} else {
-			return "keypos >= next_track_pos";
-		}
-	} else {
-		key_pos = s.find(key, current_track_pos) + key.size();
-		return substring_toend(s, key_pos);
+struct Reader : public IDelegateLoad
+{
+
+  uint64 load(FileStorage & storage,
+		      uint64 payloadBytesToRead,
+              uint64 eltId,
+              IPayload & payload)
+  {
+	  switch (eltId) {
+	  case EbmlDoc::THead::kId:
+//		  std::cout << "header" << std::endl;
+		  break;
+	  case MatroskaDoc::TSegment::kId:
+		  return 0;
+		  break;
+	  case Segment::TTracks::kId:
+//		  std::cout << "track" << std::endl;
+		  return payload.load(storage, payloadBytesToRead, NULL);
+		  break;
+	  case Segment::TAttachment::kId:
+		  break;
+	  case Segment::TChapters::kId:
+		  break;
+	  default:
+		  break;
+	  }
+
+    // skip reading (to shorten file load time):
+    storage.file_.seek(payloadBytesToRead, File::kRelativeToCurrent);
+    return payloadBytesToRead;
+  }
+};
+
+
+std::string getTrackTypeName(const unsigned long int type) {
+
+    enum MatroskaTrackType
+    {
+      kTrackTypeVideo = 1,
+      kTrackTypeAudio = 2,
+      kTrackTypeComplex = 3,
+      kTrackTypeLogo = 0x10,
+      kTrackTypeSubtitle = 0x11,
+      kTrackTypeButtons = 0x12,
+      kTrackTypeControl = 0x20
+    };
+
+	switch (type){
+	case kTrackTypeVideo:
+		return "video";
+		break;
+	case kTrackTypeAudio:
+		return "audio";
+		break;
+	case kTrackTypeComplex:
+		return "complex";
+		break;
+	case kTrackTypeLogo:
+		return "logo";
+		break;
+	case kTrackTypeSubtitle:
+		return "subtitles";
+		break;
+	case kTrackTypeButtons:
+		return "buttons";
+		break;
+	case kTrackTypeControl:
+		return "control";
+		break;
+
+	default:
+		return "";
+		break;
 	}
-	return "unknown";
+	return "";
 }
 
-int extractNumberOfTracks(std::string raw_infos) {
-	unsigned int numberOfTracks = 0;
-	size_t found = raw_infos.find(TRACK_STRING, 0) + TRACK_STRING.size();
-    while(found != std::string::npos){
-        numberOfTracks++;
-        found = raw_infos.find(TRACK_STRING, found + 1);
-    }
-    return numberOfTracks;
-}
 
 std::vector<track_info_t> MkvInfoParser::parseTracksInfos(std::string mkvFileName) {
-	std::string raw_infos = getRawMkvInfo(mkvFileName);
 	std::vector<track_info_t> tracks;
 
-    for (int track_number=1; track_number<= extractNumberOfTracks(raw_infos); track_number++) {
-        track_info_t track;
-        track.num = toString(track_number);
-        track.type = parse(raw_infos, track_number, TRACK_TYPE_STRING);
-        track.codec = parse(raw_infos, track_number, CODEC_ID_STRING);
-        track.language = parse(raw_infos, track_number, LANGUAGE_STRING);
-        tracks.push_back(track);
-    }
-    return tracks;
+	FileStorage srcFile(mkvFileName, File::kReadOnly);
+	if (!srcFile.file_.isOpen()) {
+		std::cerr << "Error : unable to open the input file " << mkvFileName << std::endl;
+	}
+
+	uint64 srcFileSize = srcFile.file_.size();
+	MatroskaDoc infos;
+
+	Reader loader;
+
+	uint64 bytesRead;
+	bytesRead = infos.loadSeekHead(srcFile, srcFileSize);
+	bytesRead += infos.loadViaSeekHead(srcFile, &loader, false);
+
+	if (!bytesRead || infos.segments_.empty()) {
+		std::cerr << "Error : the input file is not a valid matroska file : " << mkvFileName << std::endl;
+	}
+
+	MatroskaDoc::TSegment & segment = infos.segments_.back();
+	typedef std::deque<Segment::TTracks::TPayload::TTrack> TTrackList;
+	TTrackList trackList = segment.payload_.tracks_.payload_.tracks_;
+
+	for (TTrackList::iterator it = trackList.begin(); it != trackList.end(); it++) {
+		track_info_t track;
+		track.num = toString(it->payload_.trackNumber_.payload_.get() - 1); // (-1) used for compatiblity with mkvextract utility (to solve later)
+		track.type = getTrackTypeName(it->payload_.trackType_.payload_.get());
+		track.codec = it->payload_.codecID_.payload_.get();
+		track.language = it->payload_.language_.payload_.get();
+		tracks.push_back(track);
+	}
+
+	// closing file handles:
+	infos = MatroskaDoc();
+	srcFile = FileStorage();
+
+	return tracks;
 }
 
+
+
 }

File src/core/MkvInfoParser.h

View file
 
 #include <vector>
 #include <string>
-#include <map>
 #include "Common.h"
 
 namespace Core {
 class MkvInfoParser {
 public:
 	static std::vector<track_info_t> parseTracksInfos(std::string mkvFileName);
-private:
-	static std::string getRawMkvInfo(std::string filePath);
 };
 
 }

File src/gtk-gui/MainWindow.cpp

View file
+/*
+===========================================================================
+
+mkvextract-gtk
+Copyright (C) 2011 Leyorus <leyorus@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see  <http://www.gnu.org/licenses/>.
+===========================================================================
+*/
+
+#include "MainWindow.h"
+#include <core/MkvInfoParser.h>
+#include <core/MkvExtractor.h>
+
+#include <iostream>
+#include <sstream>
+#include <pthread.h>
+#include <gtkmm/filefilter.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <gtkmm/image.h>
+#include <glibmm/refptr.h>
+
+#include <sys/time.h> // for gettimeofday() function
+
+#include <pty.h>
+
+using namespace std;
+
+
+MainWindow::MainWindow() :
+			mainVBox(false, 10),
+			inputFrame("Input file"),
+			inputFileButton(Gtk::FILE_CHOOSER_ACTION_OPEN),
+			outputFrame("Output folder"),
+			outputFileButton(Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER),
+			contentFrame("Content"),
+			extractOrPauseButton("Extract"),
+			cancelButton(Gtk::Stock::CANCEL)
+{
+	this->set_title("MkvExtract-Gtk hello world vim !");
+	this->signal_delete_event().connect(sigc::mem_fun(this, &MainWindow::onCloseButton));
+	this->set_border_width(10);
+	this->set_size_request(500,350);
+	extractOrPauseButton.set_image(*Gtk::manage (new Gtk::Image (Gtk::Stock::CONVERT, Gtk::ICON_SIZE_BUTTON)));
+	inputFrame.add(inputFileButton);
+	//inputFileButton.set_border_width(5);
+	//outputFileButton.set_border_width(5);
+	mainVBox.pack_start(inputFrame, Gtk::PACK_SHRINK);
+	Glib::RefPtr<Gtk::FileFilter> mkvFileNameFilter = Gtk::FileFilter::create();
+	mkvFileNameFilter->set_name("MKV Files");
+	mkvFileNameFilter->add_mime_type("video/x-matroska");
+	inputFileButton.add_filter(mkvFileNameFilter);
+	inputFileButton.signal_file_set().connect(sigc::mem_fun(this, &MainWindow::onFileSet));
+
+	outputFrame.add(outputFileButton);
+	mainVBox.pack_start(outputFrame, Gtk::PACK_SHRINK);
+
+
+	mainVBox.pack_start(contentFrame, Gtk::PACK_EXPAND_WIDGET);
+	trackList.set_sensitive(false);
+
+//	contentFrame.add(trackList);
+	scrolledContentWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+	contentFrame.add(scrolledContentWindow);
+	scrolledContentWindow.add(trackList);
+
+	 //Create the Tree model:
+	refListStore = Gtk::ListStore::create(m_Columns);
+	trackList.set_model(refListStore);
+	//trackList.set_size_request(450,400);
+	trackList.set_border_width(20);
+	trackList.set_rules_hint(true);
+	trackList.append_column_editable("", m_Columns.m_col_selected);
+
+	((Gtk::CellRendererToggle *) trackList.get_column_cell_renderer(0))->signal_toggled().connect(
+			sigc::mem_fun(this, &MainWindow::onCheckboxClicked));
+
+	trackList.append_column("ID", m_Columns.m_col_id);
+	trackList.append_column("Type", m_Columns.m_col_type);
+	trackList.append_column("Codec", m_Columns.m_col_codec);
+	trackList.append_column("Language", m_Columns.m_col_language);
+	trackList.append_column_editable("Output filename", m_Columns.m_col_outputFileName);
+
+	extractOrPauseButton.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::onExtractOrPauseButton));
+	cancelButton.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::onCancelButton));
+
+	mainVBox.pack_start(progressBar, Gtk::PACK_SHRINK);
+	mainVBox.pack_start(hButtonBox, Gtk::PACK_SHRINK);
+
+	hButtonBox.set_layout(Gtk::BUTTONBOX_SPREAD);
+	hButtonBox.pack_start(extractOrPauseButton);
+	hButtonBox.pack_start(cancelButton);
+
+	this->add(mainVBox);
+	extractOrPauseButton.set_can_focus(false);
+	extractOrPauseButton.set_sensitive(false);
+
+	cancelButton.set_can_focus(false);
+	cancelButton.set_sensitive(false);
+
+	this->show_all();
+
+	current_state = stop_status;
+	pthread_mutex_init(&extraction_status_mutex, 0);
+}
+
+void MainWindow::printTracksInfos(std::vector<Core::track_info_t> tracks) {
+    refListStore->clear();
+    for(std::vector<Core::track_info_t>::iterator i = tracks.begin();i != tracks.end();i++){
+    	Gtk::TreeModel::Row row = *(refListStore->append());
+    	row[m_Columns.m_col_selected] = false;
+    	row[m_Columns.m_col_id] = Core::toInteger(i->num);
+    	row[m_Columns.m_col_type] = i->type;
+    	row[m_Columns.m_col_codec] = i->codec;
+    	row[m_Columns.m_col_language] = i->language;
+    	row[m_Columns.m_col_outputFileName] = Core::MkvExtractor::getDefaultFileName(*i);
+    	tracksToExtract[Core::toInteger(i->num)] = false;
+    }
+}
+
+std::string dirName(std::string source)
+{
+    source.erase(std::find(source.rbegin(), source.rend(), '/').base(), source.end());
+    return source;
+}
+
+void MainWindow::onCheckboxClicked(Glib::ustring str) {
+	Gtk::TreeModel::Path path(str.c_str());
+	Gtk::TreeModel::Row row = *(refListStore->get_iter(path));
+	tracksToExtract[row[m_Columns.m_col_id]] = !tracksToExtract[row[m_Columns.m_col_id]];
+	if (isATrackSelected()) {
+		extractOrPauseButton.set_sensitive(true);
+	} else {
+		if(!this->isExtracting()) {
+			extractOrPauseButton.set_sensitive(false);
+		}
+	}
+}
+
+bool MainWindow::isATrackSelected() {
+	for (std::map<int,bool>::iterator i = tracksToExtract.begin(); i!=tracksToExtract.end(); i++) {
+		if (i->second) { // the box is checked
+			return true;
+		}
+	}
+	return false; // no box has been checked
+}
+
+bool MainWindow::isExtracting() {
+	pthread_mutex_lock(&this->extraction_status_mutex);
+	bool ret = (current_state == extracting_status);
+	pthread_mutex_unlock(&this->extraction_status_mutex);
+	return ret;
+}
+
+void MainWindow::setExtractionStatus(extraction_status_t newState) {
+	pthread_mutex_lock(&this->extraction_status_mutex);
+	this->current_state = newState;
+	pthread_mutex_unlock(&this->extraction_status_mutex);
+}
+
+// return true if something to return
+bool getLine(int fd, std::string &str) {
+	char buffer;
+	str = "";
+
+	while (read(fd, &buffer, sizeof(buffer)) > 0) {
+		// the file is not finished
+		if (buffer == '\r') { // replace '\r' char by '\n'
+			buffer = '\n';
+		}
+		str += buffer;
+		if (buffer == '\n') {
+			break;
+		}
+	}
+	return str.size() != 0;
+}
+
+
+
+void MainWindow::extract() {
+	std::map<int, std::string> toExtract;
+	std::map<int,bool> tracksToExtract = getUserSelection();
+	for (std::map<int, bool>::iterator i = tracksToExtract.begin(); i != tracksToExtract.end(); i++) {
+		if (i->second) {
+			toExtract[i->first] = getOutputFolder() + "/" + getFileName(i->first);
+		}
+	}
+
+	int master;
+	setExtractionProcessPID(forkpty(&master, NULL, NULL, NULL));
+	if (getExtractionProcessPID() == 0) { // child process
+		close(master);
+
+		Core::MkvExtractor::extractTracks(getInputFileName(), toExtract);
+
+	} else { // parent process
+		setExtractionStatus(MainWindow::extracting_status);
+		std::cout << "Command line used : "<< std::endl;
+		std::cout << "-------------------- "<< std::endl;
+		std::cout << Core::MkvExtractor::getExtractCommandLine(getInputFileName(), toExtract) << std::endl;
+
+		std::string str;
+		while (getLine(master, str)) {
+			int progress = 0;
+			int ret = sscanf(str.c_str(), "%*[^:] : %d", &progress);
+
+			if (ret > 0) {
+				setProgressPercentage(progress);
+			}
+			std::cout << str;
+
+		}
+		setExtractionStatus(MainWindow::stop_status);
+	}
+}
+
+void* MainWindow::extractThread_fun(void* thisWindow) {
+	MainWindow *win = (MainWindow*) thisWindow;
+	win->extract();
+	return 0;
+}
+
+std::string MainWindow::getInputFileName(){
+	return this->inputFileButton.get_filename();
+}
+
+std::string MainWindow::getFileName(int id) {
+	Gtk::TreeModel::Path path(Core::toString(id));
+	Gtk::TreeModel::Row row = *(refListStore->get_iter(path));
+	std::string name;
+	row.get_value(5, name);
+	return name;
+}
+
+void MainWindow::stopExtraction()
+{
+    kill(this->extractionProcess_pid, SIGKILL);
+    setExtractionStatus(stop_status);
+    onExtractionEnd();
+}
+
+void MainWindow::pauseExtraction()
+{
+    kill(this->extractionProcess_pid, SIGSTOP);
+    setExtractionStatus(paused_status);
+    extractOrPauseButton.set_image(*Gtk::manage (new Gtk::Image (Gtk::Stock::MEDIA_PLAY, Gtk::ICON_SIZE_BUTTON)));
+	extractOrPauseButton.set_label("Continue");
+}
+
+void MainWindow::continueExtraction() {
+	extractOrPauseButton.set_label("Pause");
+	extractOrPauseButton.set_image(*Gtk::manage (new Gtk::Image (Gtk::Stock::MEDIA_PAUSE, Gtk::ICON_SIZE_BUTTON)));
+    kill(this->extractionProcess_pid, SIGCONT);
+    setExtractionStatus(extracting_status);
+    gettimeofday(&lastStartTime, 0);
+	enableTimer();
+}
+
+void MainWindow::startExtraction() {
+	extractOrPauseButton.set_label("Pause");
+	cancelButton.set_sensitive(true);
+	extractOrPauseButton.set_image(*Gtk::manage (new Gtk::Image (Gtk::Stock::MEDIA_PAUSE, Gtk::ICON_SIZE_BUTTON)));
+	pthread_create(&extraction_thread, 0, &extractThread_fun, (void*) this);
+	gettimeofday(&lastStartTime, 0);
+	time_elapsed = 0;
+	enableTimer();
+}
+
+
+
+void MainWindow::enableTimer() {
+	sigc::connection con = Glib::signal_timeout().connect(sigc::mem_fun(*this, &MainWindow::onTimeOut), 500);
+}
+
+bool MainWindow::onTimeOut() {
+	bool ret = true;
+	switch(current_state) {
+	case paused_status:
+		ret = false; // disconnect timer
+		break;
+	case extracting_status:
+		updateProgress(); // return true = continue timer
+		break;
+	case stop_status:
+		onExtractionEnd();
+		ret = false; // disconnect timer
+		break;
+	}
+	return ret;
+}
+
+
+std::string readableTime(int time_in_sec) {
+	std::string ret ="";
+	int hour, min, seconds;
+	hour = time_in_sec / 3600;
+	seconds = time_in_sec % 3600;
+	min = seconds / 60;
+	seconds = seconds % 60;
+	if (hour != 0)
+		ret += Core::toString(hour) + "h ";
+	if (min != 0)
+		ret += Core::toString(min) + "min ";
+	ret += Core::toString(seconds) + "sec";
+	return ret;
+}
+
+void MainWindow::updateProgress() {
+	progressBar.set_fraction((double)progress_percentage / 100.0);
+	timeval currentTime;
+	gettimeofday(&currentTime, 0);
+	time_elapsed += (currentTime.tv_sec - lastStartTime.tv_sec);
+	gettimeofday( &lastStartTime, 0);
+	lastStartTime = currentTime;
+
+	int remainingTime;
+	if (progress_percentage !=0) {
+		remainingTime = time_elapsed * (100-progress_percentage) / progress_percentage;
+	} else {
+		remainingTime = 0;
+	}
+
+	progressBar.set_text(Core::toString(progress_percentage)+ "%, Time elapsed = " + readableTime(time_elapsed) +", Time remaining = " + readableTime(remainingTime));
+}
+
+void MainWindow::onExtractionEnd() {
+	extractOrPauseButton.set_label("Extract");
+	cancelButton.set_sensitive(false);
+	extractOrPauseButton.set_image(*Gtk::manage (new Gtk::Image (Gtk::Stock::CONVERT, Gtk::ICON_SIZE_BUTTON)));
+	progress_percentage = 0;
+	progressBar.set_fraction((double)progress_percentage / 100.0);
+	progressBar.set_text(Core::toString(progress_percentage)+ "%");
+}
+
+
+void MainWindow::onExtractOrPauseButton() {
+	switch(current_state) {
+	case paused_status:
+		continueExtraction();
+		break;
+	case stop_status:
+		startExtraction();
+		break;
+	case extracting_status:
+		pauseExtraction();
+		break;
+	}
+}
+
+void MainWindow::onCancelButton() {
+	stopExtraction();
+}
+
+bool MainWindow::onCloseButton(GdkEventAny * ev) {
+	if (isExtracting()) {
+		this->stopExtraction();
+	}
+	return false;
+}
+
+void MainWindow::onFileSet() {
+
+	trackList.set_sensitive(true);
+
+	tracksToExtract.clear();
+	printTracksInfos(Core::MkvInfoParser::parseTracksInfos(getInputFileName()));
+
+	outputFileButton.set_current_folder(dirName(getInputFileName()));
+}
+
+void MainWindow::initTime() {
+	time_elapsed = 0;
+}
+
+std::string MainWindow::getRemainingTime() {
+	std::string ret;
+
+	return ret;
+}
+

File src/gtk-gui/MainWindow.h

View file
+/*
+===========================================================================
+
+mkvextract-gtk
+Copyright (C) 2011 Leyorus <leyorus@gmail.com> 
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see  <http://www.gnu.org/licenses/>.
+===========================================================================
+*/
+
+#ifndef DEF_MAIN_WINDOW
+#define DEF_MAIN_WINDOW
+
+#include <gtkmm/main.h>
+#include <gtkmm/window.h>
+#include <gtkmm/button.h>
+#include <gtkmm/stock.h>
+#include <gtkmm/messagedialog.h>
+#include <gtkmm/box.h>
+#include <gtkmm/frame.h>
+#include <gtkmm/filechooserbutton.h>
+#include <gtkmm/listviewtext.h>
+#include <gtkmm/treeview.h>
+#include <gtkmm/liststore.h>
+#include <gtkmm/progressbar.h>
+#include <gtkmm/buttonbox.h>
+#include <gtkmm/scrolledwindow.h>
+
+
+#include <core/Common.h>
+#include <string>
+#include <vector>
+#include <map>
+#include <pthread.h>
+
+
+class ModelColumns: public Gtk::TreeModelColumnRecord {
+public:
+
+	ModelColumns() {
+		add(m_col_selected);
+		add(m_col_id);
+		add(m_col_type);
+		add(m_col_codec);
+		add(m_col_language);
+		add(m_col_outputFileName);
+	}
+
+	Gtk::TreeModelColumn<bool> m_col_selected;
+	Gtk::TreeModelColumn<int> m_col_id;
+	Gtk::TreeModelColumn<Glib::ustring> m_col_type;
+	Gtk::TreeModelColumn<Glib::ustring> m_col_codec;
+	Gtk::TreeModelColumn<Glib::ustring> m_col_language;
+	Gtk::TreeModelColumn<Glib::ustring> m_col_outputFileName;
+};
+
+class MainWindow: public Gtk::Window {
+public:
+	MainWindow();
+private:
+	Gtk::Window window;
+	Gtk::VBox mainVBox;
+	Gtk::Frame inputFrame;
+	Gtk::FileChooserButton inputFileButton;
+	Gtk::Frame outputFrame;
+	Gtk::FileChooserButton outputFileButton;
+	Gtk::Frame contentFrame;
+	Gtk::ScrolledWindow scrolledContentWindow;
+	ModelColumns m_Columns;
+	Glib::RefPtr<Gtk::ListStore> refListStore;
+	Gtk::TreeView trackList;
+	Gtk::ProgressBar progressBar;
+	Gtk::HButtonBox hButtonBox;
+	Gtk::Button extractOrPauseButton;
+	Gtk::Button cancelButton;
+
+	std::map<int,bool> tracksToExtract;
+
+	typedef enum {extracting_status, stop_status, paused_status} extraction_status_t;
+	extraction_status_t current_state;
+
+	pthread_mutex_t extraction_status_mutex;
+	pthread_t extraction_thread;
+	pid_t extractionProcess_pid;
+
+	int progress_percentage;
+	int time_elapsed;
+	int remainingTime;
+	timeval lastStartTime;
+
+	void startExtraction();
+	void stopExtraction();
+	void pauseExtraction();
+	void continueExtraction();
+	void enableTimer();
+    void onFileSet();
+	void printTracksInfos(std::vector<Core::track_info_t> tracks);
+	void updateProgress();
+	void onExtractionEnd();
+	void onCheckboxClicked(Glib::ustring path);
+	void onExtractOrPauseButton();
+	void onCancelButton();
+	bool onCloseButton(GdkEventAny * ev);
+	bool isATrackSelected();
+	void initTime() ;
+
+	std::string getRemainingTime();
+
+	std::map<int,bool> getUserSelection() {return this->tracksToExtract;}
+	bool isExtracting();
+	void setExtractionStatus(extraction_status_t newState);
+	void setExtractionProcessPID(int PID) { extractionProcess_pid = PID;};
+	int getExtractionProcessPID() { return extractionProcess_pid;};
+	std::string getInputFileName();
+	std::string getFileName(int id);
+	std::string getOutputFolder() { return outputFileButton.get_current_folder();};
+	bool onTimeOut();
+	void setProgressPercentage(unsigned int percentage) {
+		this->progress_percentage = percentage;
+	}
+
+	static void* extractThread_fun(void* thisWindow);
+	void extract();
+};
+
+#endif

File src/gtk-gui/main.cpp

View file
+/*
+===========================================================================
+
+mkvextract-gtk
+Copyright (C) 2011 Leyorus <leyorus@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see  <http://www.gnu.org/licenses/>.
+===========================================================================
+*/
+
+#include <iostream>
+#include <gtkmm/main.h>
+#include <gtkmm/settings.h>
+#include "MainWindow.h"
+
+int main(int argc, char *argv[]) {
+    Gtk::Main app(argc, argv);
+
+    MainWindow window;
+	Glib::RefPtr<Gtk::Settings> settings = Gtk::Settings::get_default();
+	/* force using icons on stock buttons: */
+	settings->property_gtk_button_images() = true;
+
+    Gtk::Main::run(window);
+
+    return 0;
+}

File src/gui/MainWindow.cpp

-/*
-===========================================================================
-
-mkvextract-gtk
-Copyright (C) 2011 Leyorus <leyorus@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see  <http://www.gnu.org/licenses/>.
-===========================================================================
-*/
-
-#include "MainWindow.h"
-#include "MkvInfoParser.h"
-#include "MkvExtractor.h"
-
-#include <iostream>
-#include <sstream>
-#include <pthread.h>
-#include <gtkmm/filefilter.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <gtkmm/image.h>
-
-#include <sys/time.h> // for gettimeofday() function
-
-#include <pty.h>
-
-using namespace std;
-
-
-MainWindow::MainWindow() :
-			mainVBox(false, 10),
-			inputFrame("Input file"),
-			inputFileButton(Gtk::FILE_CHOOSER_ACTION_OPEN),
-			outputFrame("Output folder"),
-			outputFileButton(Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER),
-			contentFrame("Content"),
-			extractOrPauseButton("Extract"),
-			cancelButton(Gtk::Stock::CANCEL)
-{
-	this->set_title("MkvExtract-Gtk");
-	this->signal_delete_event().connect(sigc::mem_fun(this, &MainWindow::onCloseButton));
-	this->set_border_width(10);
-	extractOrPauseButton.set_image(*Gtk::manage (new Gtk::Image (Gtk::Stock::CONVERT, Gtk::ICON_SIZE_BUTTON)));
-	inputFrame.add(inputFileButton);
-	inputFileButton.set_border_width(5);
-	outputFileButton.set_border_width(5);
-	mainVBox.pack_start(inputFrame, Gtk::PACK_SHRINK);
-	Gtk::FileFilter mkvFileNameFilter;
-	mkvFileNameFilter.set_name("MKV Files");
-	mkvFileNameFilter.add_mime_type("video/x-matroska");
-	inputFileButton.add_filter(mkvFileNameFilter);
-	inputFileButton.signal_file_set().connect(sigc::mem_fun(this, &MainWindow::onFileSet));
-
-	outputFrame.add(outputFileButton);
-	mainVBox.pack_start(outputFrame, Gtk::PACK_SHRINK);
-
-
-	mainVBox.pack_start(contentFrame, Gtk::PACK_EXPAND_WIDGET);
-	trackList.set_sensitive(false);
-
-//	contentFrame.add(trackList);
-	scrolledContentWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
-	contentFrame.add(scrolledContentWindow);
-	scrolledContentWindow.add(trackList);
-
-	 //Create the Tree model:
-	refListStore = Gtk::ListStore::create(m_Columns);
-	trackList.set_model(refListStore);
-	trackList.set_size_request(450,150);
-	trackList.set_border_width(20);
-	trackList.append_column_editable("", m_Columns.m_col_selected);
-
-	((Gtk::CellRendererToggle *) trackList.get_column_cell_renderer(0))->signal_toggled().connect(
-			sigc::mem_fun(this, &MainWindow::onCheckboxClicked));
-
-	trackList.append_column("ID", m_Columns.m_col_id);
-	trackList.append_column("Type", m_Columns.m_col_type);
-	trackList.append_column("Codec", m_Columns.m_col_codec);
-	trackList.append_column("Language", m_Columns.m_col_language);
-	trackList.append_column_editable("Output filename", m_Columns.m_col_outputFileName);
-
-	extractOrPauseButton.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::onExtractOrPauseButton));
-	cancelButton.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::onCancelButton));
-
-	mainVBox.pack_start(progressBar, Gtk::PACK_SHRINK);
-	mainVBox.pack_start(hButtonBox, Gtk::PACK_SHRINK);
-
-	hButtonBox.set_layout(Gtk::BUTTONBOX_SPREAD);
-	hButtonBox.pack_start(extractOrPauseButton);
-	hButtonBox.pack_start(cancelButton);
-
-	this->add(mainVBox);
-	extractOrPauseButton.set_can_focus(false);
-	extractOrPauseButton.set_sensitive(false);
-
-	cancelButton.set_can_focus(false);
-	cancelButton.set_sensitive(false);
-
-	this->show_all();
-
-	current_state = stop_status;
-	pthread_mutex_init(&extraction_status_mutex, 0);
-}
-
-void MainWindow::printTracksInfos(std::vector<Core::track_info_t> tracks) {
-	refListStore->clear();
-    for(std::vector<Core::track_info_t>::iterator i = tracks.begin();i != tracks.end();i++){
-    	Gtk::TreeModel::Row row = *(refListStore->append());
-    	row[m_Columns.m_col_selected] = false;
-    	row[m_Columns.m_col_id] = Core::toInteger(i->num);
-    	row[m_Columns.m_col_type] = i->type;
-    	row[m_Columns.m_col_codec] = i->codec;
-    	row[m_Columns.m_col_language] = i->language;
-    	row[m_Columns.m_col_outputFileName] = Core::MkvExtractor::getDefaultFileName(*i);
-    	tracksToExtract[Core::toInteger(i->num)] = false;
-    }
-}
-
-std::string dirName(std::string source)
-{
-    source.erase(std::find(source.rbegin(), source.rend(), '/').base(), source.end());
-    return source;
-}
-
-void MainWindow::onCheckboxClicked(Glib::ustring str) {
-	Gtk::TreeModel::Path path(str.c_str());
-	Gtk::TreeModel::Row row = *(refListStore->get_iter(path));
-	tracksToExtract[row[m_Columns.m_col_id]] = !tracksToExtract[row[m_Columns.m_col_id]];
-	if (isATrackSelected()) {
-		extractOrPauseButton.set_sensitive(true);
-	} else {
-		if(!this->isExtracting()) {
-			extractOrPauseButton.set_sensitive(false);
-		}
-	}
-}
-
-bool MainWindow::isATrackSelected() {
-	for (std::map<int,bool>::iterator i = tracksToExtract.begin(); i!=tracksToExtract.end(); i++) {
-		if (i->second) { // the box is checked
-			return true;
-		}
-	}
-	return false; // no box has been checked
-}
-
-bool MainWindow::isExtracting() {
-	pthread_mutex_lock(&this->extraction_status_mutex);
-	bool ret = (current_state == extracting_status);
-	pthread_mutex_unlock(&this->extraction_status_mutex);
-	return ret;
-}
-
-void MainWindow::setExtractionStatus(extraction_status_t newState) {
-	pthread_mutex_lock(&this->extraction_status_mutex);
-	this->current_state = newState;
-	pthread_mutex_unlock(&this->extraction_status_mutex);
-}
-
-// return true if something to return
-bool getLine(int fd, std::string &str) {
-	char buffer;
-	str = "";
-
-	while (read(fd, &buffer, sizeof(buffer)) > 0) {
-		// the file is not finished
-		if (buffer == '\r') { // replace '\r' char by '\n'
-			buffer = '\n';
-		}
-		str += buffer;
-		if (buffer == '\n') {
-			break;
-		}
-	}
-	return str.size() != 0;
-}
-
-void* extractionThread_fun(void* args) {
-	MainWindow *win = (MainWindow*) args;
-	std::map<int, std::string> toExtract;
-	std::map<int,bool> tracksToExtract = win->getUserSelection();
-	for (std::map<int, bool>::iterator i = tracksToExtract.begin(); i != tracksToExtract.end(); i++) {
-		if (i->second) {
-			toExtract[i->first] = win->getOutputFolder() + "/" + win->getFileName(i->first);
-		}
-	}
-
-	int master;
-	win->setExtractionProcessPID(forkpty(&master, NULL, NULL, NULL));
-	if (win->getExtractionProcessPID() == 0) { // child process
-		close(master);
-
-		Core::MkvExtractor::extractTracks(win->getInputFileName(), toExtract);
-
-	} else { // parent process
-		win->setExtractionStatus(extracting_status);
-		std::cout << "Command line used : "<< std::endl;
-		std::cout << "-------------------- "<< std::endl;
-		std::cout << Core::MkvExtractor::getExtractCommandLine(win->getInputFileName(), toExtract) << std::endl;
-
-		std::string str;
-		while (getLine(master, str)) {
-			int progress = 0;
-			int ret = sscanf(str.c_str(), "%*[^:] : %d", &progress);
-
-			if (ret > 0) {
-				win->setProgressPercentage(progress);
-			} else {
-//				std::cout << str;
-			}
-
-		}
-		win->setExtractionStatus(stop_status);
-	}
-	return 0;
-}
-
-std::string MainWindow::getInputFileName(){
-	return this->inputFileButton.get_filename();
-}
-
-std::string MainWindow::getFileName(int id) {
-	Gtk::TreeModel::Path path(Core::toString(id-1));
-	Gtk::TreeModel::Row row = *(refListStore->get_iter(path));
-	std::string name;
-	row.get_value(5, name);
-	return name;
-}
-
-void MainWindow::stopExtraction()
-{
-    kill(this->extractionProcess_pid, SIGKILL);
-    setExtractionStatus(stop_status);
-    onExtractionEnd();
-}
-
-void MainWindow::pauseExtraction()
-{
-    kill(this->extractionProcess_pid, SIGSTOP);
-    setExtractionStatus(paused_status);
-    extractOrPauseButton.set_image(*Gtk::manage (new Gtk::Image (Gtk::Stock::MEDIA_PLAY, Gtk::ICON_SIZE_BUTTON)));
-	extractOrPauseButton.set_label("Continue");
-}
-
-void MainWindow::continueExtraction() {
-	extractOrPauseButton.set_label("Pause");
-	extractOrPauseButton.set_image(*Gtk::manage (new Gtk::Image (Gtk::Stock::MEDIA_PAUSE, Gtk::ICON_SIZE_BUTTON)));
-    kill(this->extractionProcess_pid, SIGCONT);
-    setExtractionStatus(extracting_status);
-    gettimeofday(&lastStartTime, 0);
-	enableTimer();
-}
-
-void MainWindow::startExtraction() {
-	extractOrPauseButton.set_label("Pause");
-	cancelButton.set_sensitive(true);
-	extractOrPauseButton.set_image(*Gtk::manage (new Gtk::Image (Gtk::Stock::MEDIA_PAUSE, Gtk::ICON_SIZE_BUTTON)));
-	pthread_create(&extraction_thread, 0, &extractionThread_fun, (void*) this);
-	gettimeofday(&lastStartTime, 0);
-	time_elapsed = 0;
-	enableTimer();
-}
-
-
-
-void MainWindow::enableTimer() {
-	sigc::connection con = Glib::signal_timeout().connect(sigc::mem_fun(*this, &MainWindow::onTimeOut), 500);
-}
-
-bool MainWindow::onTimeOut() {
-	bool ret = true;
-	switch(current_state) {
-	case paused_status:
-		ret = false; // disconnect timer
-		break;
-	case extracting_status:
-		updateProgress(); // return true = continue timer
-		break;
-	case stop_status:
-		onExtractionEnd();
-		ret = false; // disconnect timer
-		break;
-	}
-	return ret;
-}
-
-
-std::string readableTime(int time_in_sec) {
-	std::string ret ="";
-	int hour, min, seconds;
-	hour = time_in_sec / 3600;
-	seconds = time_in_sec % 3600;
-	min = seconds / 60;
-	seconds = seconds % 60;
-	if (hour != 0)
-		ret += Core::toString(hour) + "h ";
-	if (min != 0)
-		ret += Core::toString(min) + "min ";
-	ret += Core::toString(seconds) + "sec";
-	return ret;
-}
-
-void MainWindow::updateProgress() {
-	progressBar.set_fraction((double)progress_percentage / 100.0);
-	timeval currentTime;
-	gettimeofday(&currentTime, 0);
-	time_elapsed += (currentTime.tv_sec - lastStartTime.tv_sec);
-	gettimeofday( &lastStartTime, 0);
-	lastStartTime = currentTime;
-
-	int remainingTime;
-	if (progress_percentage !=0) {
-		remainingTime = time_elapsed * (100-progress_percentage) / progress_percentage;
-	} else {
-		remainingTime = 0;
-	}
-
-	progressBar.set_text(Core::toString(progress_percentage)+ "%, Time elapsed = " + readableTime(time_elapsed) +", Time remaining = " + readableTime(remainingTime));
-}
-
-void MainWindow::onExtractionEnd() {
-	extractOrPauseButton.set_label("Extract");
-	cancelButton.set_sensitive(false);
-	extractOrPauseButton.set_image(*Gtk::manage (new Gtk::Image (Gtk::Stock::CONVERT, Gtk::ICON_SIZE_BUTTON)));
-	progress_percentage = 0;
-	progressBar.set_fraction((double)progress_percentage / 100.0);
-	progressBar.set_text(Core::toString(progress_percentage)+ "%");
-}
-
-
-void MainWindow::onExtractOrPauseButton() {
-	switch(current_state) {
-	case paused_status:
-		continueExtraction();
-		break;
-	case stop_status:
-		startExtraction();
-		break;
-	case extracting_status:
-		pauseExtraction();
-		break;
-	}
-}
-
-void MainWindow::onCancelButton() {
-	stopExtraction();
-}
-
-bool MainWindow::onCloseButton(GdkEventAny * ev) {
-	if (isExtracting()) {
-		this->stopExtraction();
-	}
-	return false;
-}
-
-void MainWindow::onFileSet() {
-
-	trackList.set_sensitive(true);
-
-	tracksToExtract.clear();
-	printTracksInfos(Core::MkvInfoParser::parseTracksInfos(getInputFileName()));
-
-	outputFileButton.set_current_folder(dirName(getInputFileName()));
-}
-
-void MainWindow::initTime() {
-	time_elapsed = 0;
-}
-
-std::string MainWindow::getRemainingTime() {
-	std::string ret;
-
-	return ret;
-}
-

File src/gui/MainWindow.h

-/*
-===========================================================================
-
-mkvextract-gtk
-Copyright (C) 2011 Leyorus <leyorus@gmail.com> 
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see  <http://www.gnu.org/licenses/>.
-===========================================================================
-*/
-
-#ifndef DEF_MAIN_WINDOW
-#define DEF_MAIN_WINDOW
-
-#include <gtkmm/main.h>
-#include <gtkmm/window.h>
-#include <gtkmm/button.h>
-#include <gtkmm/stock.h>
-#include <gtkmm/messagedialog.h>
-#include <gtkmm/box.h>
-#include <gtkmm/frame.h>
-#include <gtkmm/filechooserbutton.h>
-#include <gtkmm/listviewtext.h>
-#include <gtkmm/treeview.h>
-#include <gtkmm/liststore.h>
-#include <gtkmm/progressbar.h>
-#include <gtkmm/buttonbox.h>
-#include <gtkmm/scrolledwindow.h>
-
-
-#include "Common.h"
-#include <string>
-#include <vector>
-#include <map>
-#include <pthread.h>
-
-typedef enum {extracting_status, stop_status, paused_status} extraction_status_t;
-
-class ModelColumns: public Gtk::TreeModelColumnRecord {
-public:
-
-	ModelColumns() {
-		add(m_col_selected);
-		add(m_col_id);
-		add(m_col_type);
-		add(m_col_codec);
-		add(m_col_language);
-		add(m_col_outputFileName);
-	}
-
-	Gtk::TreeModelColumn<bool> m_col_selected;
-	Gtk::TreeModelColumn<int> m_col_id;
-	Gtk::TreeModelColumn<Glib::ustring> m_col_type;
-	Gtk::TreeModelColumn<Glib::ustring> m_col_codec;
-	Gtk::TreeModelColumn<Glib::ustring> m_col_language;
-	Gtk::TreeModelColumn<Glib::ustring> m_col_outputFileName;
-};
-
-class MainWindow: public Gtk::Window {
-public:
-	MainWindow();
-	std::map<int,bool> getUserSelection() {return this->tracksToExtract;}
-	bool isExtracting();
-	void setExtractionStatus(extraction_status_t newState);
-	void setExtractionProcessPID(int PID) { extractionProcess_pid = PID;};
-	int getExtractionProcessPID() { return extractionProcess_pid;};
-	std::string getInputFileName();
-	std::string getFileName(int id);
-	std::string getOutputFolder() { return outputFileButton.get_current_folder();};
-	bool onTimeOut();
-	void setProgressPercentage(unsigned int percentage) {
-		this->progress_percentage = percentage;
-	}
-private:
-	Gtk::Window window;
-	Gtk::VBox mainVBox;
-	Gtk::Frame inputFrame;
-	Gtk::FileChooserButton inputFileButton;
-	Gtk::Frame outputFrame;
-	Gtk::FileChooserButton outputFileButton;
-	Gtk::Frame contentFrame;
-	Gtk::ScrolledWindow scrolledContentWindow;
-	ModelColumns m_Columns;
-	Glib::RefPtr<Gtk::ListStore> refListStore;
-	Gtk::TreeView trackList;
-	Gtk::ProgressBar progressBar;
-	Gtk::HButtonBox hButtonBox;
-	Gtk::Button extractOrPauseButton;
-	Gtk::Button cancelButton;
-
-	std::map<int,bool> tracksToExtract;
-
-	extraction_status_t current_state;
-
-	pthread_mutex_t extraction_status_mutex;
-	pthread_t extraction_thread;
-	pid_t extractionProcess_pid;
-
-	int progress_percentage;
-	int time_elapsed;
-	int remainingTime;
-	timeval lastStartTime;
-
-	void startExtraction();
-	void stopExtraction();
-	void pauseExtraction();
-	void continueExtraction();
-	void enableTimer();
-    void onFileSet();
-	void printTracksInfos(std::vector<Core::track_info_t> tracks);
-	void updateProgress();
-	void onExtractionEnd();
-	void onCheckboxClicked(Glib::ustring path);
-	void onExtractOrPauseButton();
-	void onCancelButton();
-	bool onCloseButton(GdkEventAny * ev);
-	bool isATrackSelected();
-	void initTime() ;
-
-	std::string getRemainingTime();
-};
-
-#endif

File src/gui/mkvextract-gtk.cpp

-/*
-===========================================================================
-
-mkvextract-gtk
-Copyright (C) 2011 Leyorus <leyorus@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see  <http://www.gnu.org/licenses/>.
-===========================================================================
-*/
-
-#include <iostream>
-#include <gtkmm/main.h>
-#include <gtkmm/settings.h>
-#include "MainWindow.h"
-
-int main(int argc, char *argv[]) {
-    Gtk::Main app(argc, argv);
-
-    MainWindow window;
-	Glib::RefPtr<Gtk::Settings> settings = Gtk::Settings::get_default();
-	/* force using icons on stock buttons: */
-	settings->property_gtk_button_images() = true;
-
-    Gtk::Main::run(window);
-
-    return 0;
-}

File src/yamka/CMakeLists.txt

View file
+project(yamka)
+
+cmake_minimum_required(VERSION 2.8.0)
+include_directories(AFTER .)
+
+find_package(Boost REQUIRED)
+if (Boost_INCLUDE_DIR)
+  include_directories(AFTER ${Boost_INCLUDE_DIR})
+endif (Boost_INCLUDE_DIR)
+
+if (WIN32)
+  add_definitions(-D_USE_MATH_DEFINES)
+  add_definitions(-DNOMINMAX)
+endif (WIN32)
+
+add_library(yamka STATIC
+  yamkaCrc32.h
+  yamkaEBML.h
+  yamkaElt.h
+  yamkaFile.h
+  yamkaFileStorage.h
+  yamka.h
+  yamkaIStorage.h
+  yamkaMatroska.h
+  yamkaPayload.h
+  yamkaStdInt.h
+  yamkaBytes.h
+  yamkaBytes.cpp
+  yamkaCrc32.cpp
+  yamkaEBML.cpp
+  yamkaElt.cpp
+  yamkaFile.cpp
+  yamkaFileStorage.cpp
+  yamkaIStorage.cpp
+  yamkaMatroska.cpp
+  yamkaPayload.cpp
+  yamkaStdInt.cpp
+  )

File src/yamka/yamka.h

View file
+// -*- Mode: c++; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil -*-
+// NOTE: the first line of this file sets up source code indentation rules
+// for Emacs; it is also a hint to anyone modifying this file.
+
+// Created   : Fri Apr  9 22:51:18 MDT 2010
+// Copyright : Pavel Koshevoy
+// License   : MIT -- http://www.opensource.org/licenses/mit-license.php
+
+#ifndef YAMKA_H_
+#define YAMKA_H_
+
+// yamka includes:
+#include <yamkaFile.h>
+#include <yamkaIStorage.h>
+#include <yamkaFileStorage.h>
+#include <yamkaElt.h>
+#include <yamkaPayload.h>
+#include <yamkaEBML.h>
+#include <yamkaMatroska.h>
+
+
+// element: type id, payload vsize, payload
+// payload: type specific (container, vsize binary, vsize scalar, vsize string)
+// storage: abstract mechanism to save/load binary data
+
+
+#endif // YAMKA_H_

File src/yamka/yamkaBytes.cpp

View file
+// -*- Mode: c++; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil -*-
+// NOTE: the first line of this file sets up source code indentation rules
+// for Emacs; it is also a hint to anyone modifying this file.
+
+// Created   : Sun Apr 11 17:24:08 MDT 2010
+// Copyright : Pavel Koshevoy
+// License   : MIT -- http://www.opensource.org/licenses/mit-license.php
+
+// yamka includes:
+#include <yamkaBytes.h>
+
+// boost includes:
+#include <boost/shared_ptr.hpp>
+
+// system includes:
+#include <iomanip>
+#include <assert.h>
+
+
+namespace Yamka
+{
+  
+  //----------------------------------------------------------------
+  // Bytes::Bytes
+  // 
+  Bytes::Bytes(std::size_t size):
+    bytes_(new TByteVecDec(1, TByteVec(size)))
+  {}
+  
+  //----------------------------------------------------------------
+  // Bytes::Bytes
+  // 
+  Bytes::Bytes(const void * data, std::size_t size):
+    bytes_(new TByteVecDec(1, TByteVec((const unsigned char *)data,
+                                       (const unsigned char *)data + size)))
+  {}
+  
+  //----------------------------------------------------------------
+  // Bytes::Bytes
+  // 
+  Bytes::Bytes(const TByteVec & byteVec):
+    bytes_(new TByteVecDec(1, byteVec))
+  {}
+  
+  //----------------------------------------------------------------
+  // Bytes::operator TByteVec
+  // 
+  Bytes::operator TByteVec() const
+  {
+    TByteVec dstVec;
+    dstVec.reserve(size());
+    
+    const TByteVecDec & deq = *bytes_;
+    for (TByteVecDec::const_iterator i = deq.begin(); i != deq.end(); ++i)
+    {
+      const TByteVec & vec = *i;
+      dstVec.insert(dstVec.end(), vec.begin(), vec.end());
+    }
+    
+    return dstVec;
+  }
+  
+  //----------------------------------------------------------------
+  // Bytes::deepCopy
+  // 
+  Bytes &
+  Bytes::deepCopy(const Bytes & bytes)
+  {
+    TByteVecDec & deq = *bytes_;
+    deq.resize(1);
+    
+    TByteVec & vec = deq.back();
+    vec = TByteVec(bytes);
+    
+    return *this;
+  }
+  
+  //----------------------------------------------------------------
+  // Bytes::deepCopy
+  // 
+  Bytes &
+  Bytes::deepCopy(const TByte * bytes, std::size_t numBytes)
+  {
+    Bytes tmp(bytes, numBytes);
+    return *this = tmp;
+  }
+  
+  //----------------------------------------------------------------
+  // Bytes::operator +=
+  // 
+  // NOTE: this is a deep copy of data (not shared with source):
+  Bytes &
+  Bytes::operator += (const Bytes & from)
+  {
+    const TByteVecDec & src = *(from.bytes_);
+    TByteVecDec & dst = *bytes_;
+    
+    dst.insert(dst.end(),
+               src.begin(),
+               src.end());
+    
+    return *this;
+  }
+  
+  //----------------------------------------------------------------
+  // Bytes::operator <<
+  // 
+  Bytes &
+  Bytes::operator << (const TByteVec & bytes)
+  {
+    TByteVecDec & deq = *bytes_;
+    deq.push_back(bytes);
+    
+    return *this;
+  }
+  
+  //----------------------------------------------------------------
+  // Bytes::operator <<
+  // 
+  Bytes &
+  Bytes::operator << (const TByte & byte)
+  {
+    TByteVecDec & deq = *bytes_;
+    deq.back().push_back(byte);
+    
+    return *this;
+  }
+  
+  //----------------------------------------------------------------
+  // Bytes::operator <<
+  // 
+  Bytes &
+  Bytes::operator << (const std::string & str)
+  {
+    TByteVec vec(str.data(), str.data() + str.size());
+    return (*this) << vec;
+  }
+  
+  //----------------------------------------------------------------
+  // Bytes::empty
+  // 
+  bool
+  Bytes::empty() const
+  {
+    const TByteVecDec & deq = *bytes_;
+    for (TByteVecDec::const_iterator i = deq.begin(); i != deq.end(); ++i)
+    {
+      const TByteVec & byteVec = *i;
+      if (!byteVec.empty())
+      {
+        return false;
+      }
+    }
+    
+    return true;
+  }
+  
+  //----------------------------------------------------------------
+  // Bytes::size
+  // 
+  std::size_t
+  Bytes::size() const
+  {
+    std::size_t total = 0;
+    
+    const TByteVecDec & deq = *bytes_;
+    for (TByteVecDec::const_iterator i = deq.begin(); i != deq.end(); ++i)
+    {
+      const TByteVec & byteVec = *i;
+      total += byteVec.size();
+    }
+    
+    return total;
+  }
+  
+  //----------------------------------------------------------------
+  // Bytes::getByte
+  // 
+  const TByte &
+  Bytes::getByte(std::size_t i) const
+  {
+    const TByteVecDec & deq = *bytes_;
+    for (TByteVecDec::const_iterator j = deq.begin(); j != deq.end(); ++j)
+    {
+      const TByteVec & byteVec = *j;
+      const std::size_t byteVecSize = byteVec.size();
+      
+      if (i < byteVecSize)
+      {
+        return byteVec[i];
+      }
+      
+      i -= byteVecSize;
+    }
+    
+    assert(false);
+    return *(TByte *)NULL;
+  }
+  
+  //----------------------------------------------------------------
+  // operator +
+  // 
+  Bytes
+  operator + (const Bytes & a, const Bytes & b)
+  {
+    Bytes ab(a);
+    ab += b;
+    return ab;
+  }
+  
+  //----------------------------------------------------------------
+  // operator <<
+  // 
+  std::ostream &
+  operator << (std::ostream & os, const TByteVec & vec)
+  {
+    os << std::hex
+       << std::uppercase;
+    
+    for (TByteVec::const_iterator j = vec.begin(); j != vec.end(); ++j)
+    {
+      const TByte & byte = *j;
+      os << std::setw(2)
+         << std::setfill('0')
+         << int(byte);
+    }
+    
+    os << std::dec;
+    return os;
+  }
+  
+  //----------------------------------------------------------------
+  // operator <<
+  // 
+  std::ostream &
+  operator << (std::ostream & os, const Bytes & bytes)
+  {
+    const TByteVecDec & deq = *(bytes.bytes_);
+    for (TByteVecDec::const_iterator i = deq.begin(); i != deq.end(); ++i)
+    {
+      const TByteVec & vec = *i;
+      os << vec;
+    }
+    return os;
+  }
+  
+}

File src/yamka/yamkaBytes.h

View file
+// -*- Mode: c++; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil -*-
+// NOTE: the first line of this file sets up source code indentation rules
+// for Emacs; it is also a hint to anyone modifying this file.
+
+// Created   : Sun Apr 11 17:10:52 MDT 2010
+// Copyright : Pavel Koshevoy
+// License   : MIT -- http://www.opensource.org/licenses/mit-license.php
+
+#ifndef YAMKA_BYTES_H_
+#define YAMKA_BYTES_H_
+
+// boost includes:
+#include <boost/shared_ptr.hpp>
+
+// system includes:
+#include <deque>
+#include <vector>
+#include <iostream>
+
+
+namespace Yamka
+{
+  
+  //----------------------------------------------------------------
+  // TByte
+  // 
+  typedef unsigned char TByte;
+  
+  //----------------------------------------------------------------
+  // TByteVec
+  // 
+  typedef std::vector<TByte> TByteVec;
+  
+  //----------------------------------------------------------------
+  // TByteVecDec
+  // 
+  typedef std::deque<TByteVec> TByteVecDec;
+  
+  //----------------------------------------------------------------
+  // TByteVecDecPtr
+  // 
+  typedef boost::shared_ptr<TByteVecDec> TByteVecDecPtr;
+  
+  //----------------------------------------------------------------
+  // Bytes
+  // 
+  struct Bytes
+  {
+    Bytes(std::size_t size = 0);
+    Bytes(const void * data, std::size_t size);
+    explicit Bytes(const TByteVec & byteVec);
+    
+    // convert to a byte vector (deep copy of data):
+    operator TByteVec() const;
+    
+    // reset the size and copy all given data:
+    Bytes & deepCopy(const Bytes & bytes);
+    Bytes & deepCopy(const TByte * bytes, std::size_t numBytes);
+    
+    // append via shallow copy of given bytes:
+    Bytes & operator += (const Bytes & bytes);
+    
+    // append via deep copy of given byte(s):
+    Bytes & operator << (const TByteVec & bytes);
+    Bytes & operator << (const TByte & byte);
+    Bytes & operator << (const std::string & str);
+    
+    // size accessors:
+    bool empty() const;
+    std::size_t size() const;
+    
+    // byte accessors:
+    const TByte & getByte(std::size_t i) const;
+    
+    inline const TByte & operator [] (std::size_t i) const
+    { return getByte(i); }
+    
+    inline TByte & operator [] (std::size_t i)
+    { return const_cast<TByte &>(this->getByte(i)); }
+    
+    // using a shared pointer to avoid expensive deep copies:
+    TByteVecDecPtr bytes_;
+  };
+  
+  //----------------------------------------------------------------
+  // operator +
+  // 
+  extern Bytes operator + (const Bytes & a, const Bytes & b);
+  
+  //----------------------------------------------------------------
+  // operator <<
+  // 
+  extern std::ostream &
+  operator << (std::ostream & os, const TByteVec & byteVec);
+  
+  //----------------------------------------------------------------
+  // operator <<
+  // 
+  extern std::ostream &
+  operator << (std::ostream & os, const Bytes & bytes);
+  
+}
+
+
+#endif // YAMKA_BYTES_H_

File src/yamka/yamkaCrc32.cpp

View file
+// -*- Mode: c++; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil -*-
+// NOTE: the first line of this file sets up source code indentation rules
+// for Emacs; it is also a hint to anyone modifying this file.
+
+// Created   : Sat Apr 10 15:13:21 MDT 2010
+// Copyright : Pavel Koshevoy
+// License   : MIT -- http://www.opensource.org/licenses/mit-license.php
+
+// yamka includes:
+#include <yamkaCrc32.h>
+
+// boost includes:
+#include <boost/crc.hpp>
+
+
+namespace Yamka
+{
+
+  //----------------------------------------------------------------
+  // Crc32::Private
+  // 
+  class Crc32::Private
+  {
+  public:
+    boost::crc_32_type checksum_;
+  };
+  
+  //----------------------------------------------------------------
+  // Crc32::Crc32
+  // 
+  Crc32::Crc32():
+    private_(new Crc32::Private)
+  {}
+  
+  //----------------------------------------------------------------
+  // Crc32::~Crc32
+  // 
+  Crc32::~Crc32()
+  {
+    delete private_;
+  }
+
+  //----------------------------------------------------------------
+  // Crc32::compute
+  // 
+  void
+  Crc32::compute(const Bytes & bytes)
+  {
+    const TByteVecDec & deq = *(bytes.bytes_);
+    for (TByteVecDec::const_iterator i = deq.begin(); i != deq.end(); ++i)
+    {
+      const TByteVec & vec = *i;
+      if (!vec.empty())
+      {
+        compute(&(vec[0]), vec.size());
+      }
+    }
+  }
+  
+  //----------------------------------------------------------------
+  // Crc32::compute
+  // 
+  void
+  Crc32::compute(const void * bytes, std::size_t numBytes)
+  {
+    private_->checksum_.process_bytes(bytes, numBytes);
+  }
+  
+  //----------------------------------------------------------------
+  // Crc32::checksum
+  // 
+  unsigned int
+  Crc32::checksum() const
+  {
+    unsigned int crc32 = private_->checksum_.checksum();
+    return crc32;
+  }
+}

File src/yamka/yamkaCrc32.h

View file
+// -*- Mode: c++; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil -*-
+// NOTE: the first line of this file sets up source code indentation rules
+// for Emacs; it is also a hint to anyone modifying this file.
+
+// Created   : Sat Apr 10 15:06:08 MDT 2010
+// Copyright : Pavel Koshevoy
+// License   : MIT -- http://www.opensource.org/licenses/mit-license.php
+
+#ifndef YAMKA_CRC32_H_
+#define YAMKA_CRC32_H_
+
+// yamka includes:
+#include <yamkaBytes.h>
+
+
+namespace Yamka
+{
+
+  //----------------------------------------------------------------
+  // Crc32
+  // 
+  struct Crc32
+  {
+    Crc32();
+    ~Crc32();
+    
+    // process data to compute the checksum:
+    void compute(const void * bytes, std::size_t numBytes);
+    
+    // process data to compute the checksum:
+    void compute(const Bytes & bytes);
+    
+    // accessor to the current checksum:
+    unsigned int checksum() const;
+    
+  private:
+    // intentionally disabled:
+    Crc32(const Crc32 &);
+    Crc32 & operator = (const Crc32);
+    
+    // private implementation details:
+    class Private;
+    Private * private_;
+  };
+}
+
+
+#endif // YAMKA_CRC32_H_

File src/yamka/yamkaEBML.cpp

View file
+// -*- Mode: c++; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil -*-
+// NOTE: the first line of this file sets up source code indentation rules
+// for Emacs; it is also a hint to anyone modifying this file.
+
+// Created   : Sun Apr 11 23:44:58 MDT 2010
+// Copyright : Pavel Koshevoy
+// License   : MIT -- http://www.opensource.org/licenses/mit-license.php
+
+// yamka includes:
+#include <yamkaEBML.h>
+
+
+namespace Yamka
+{
+  
+  //----------------------------------------------------------------
+  // EbmlMaster::isComposite
+  // 
+  bool
+  EbmlMaster::isComposite() const
+  {
+    return true;
+  }
+  
+  
+  //----------------------------------------------------------------
+  // EbmlHead::EbmlHead
+  // 
+  EbmlHead::EbmlHead()
+  {
+    version_.alwaysSave().payload_.setDefault(1);
+    readVersion_.alwaysSave().payload_.setDefault(1);
+    maxIdLength_.alwaysSave().payload_.setDefault(4);
+    maxSizeLength_.alwaysSave().payload_.setDefault(8);
+    docType_.alwaysSave();
+    docTypeVersion_.alwaysSave();
+    docTypeReadVersion_.alwaysSave();
+  }
+
+  //----------------------------------------------------------------
+  // EbmlHead::eval
+  // 
+  bool
+  EbmlHead::eval(IElementCrawler & crawler)
+  {
+    return
+      crawler.eval(version_) ||
+      crawler.eval(readVersion_) ||
+      crawler.eval(maxIdLength_) ||
+      crawler.eval(maxSizeLength_) ||
+      crawler.eval(docType_) ||
+      crawler.eval(docTypeVersion_) ||
+      crawler.eval(docTypeReadVersion_);
+  }
+  
+  //----------------------------------------------------------------
+  // EbmlHead::isDefault
+  // 
+  bool
+  EbmlHead::isDefault() const
+  {
+    return false;
+  }
+  
+  //----------------------------------------------------------------
+  // EbmlHead::calcSize
+  // 
+  uint64
+  EbmlHead::calcSize() const
+  {
+    uint64 size =
+      version_.calcSize() +
+      readVersion_.calcSize() +
+      maxIdLength_.calcSize() +
+      maxSizeLength_.calcSize() +
+      docType_.calcSize() +
+      docTypeVersion_.calcSize() +
+      docTypeReadVersion_.calcSize();
+    
+    return size;
+  }
+
+  //----------------------------------------------------------------
+  // EbmlHead::save
+  // 
+  IStorage::IReceiptPtr
+  EbmlHead::save(IStorage & storage) const
+  {
+    IStorage::IReceiptPtr receipt = storage.receipt();
+    
+    *receipt += version_.save(storage);
+    *receipt += readVersion_.save(storage);
+    *receipt += maxIdLength_.save(storage);
+    *receipt += maxSizeLength_.save(storage);
+    *receipt += docType_.save(storage);
+    *receipt += docTypeVersion_.save(storage);
+    *receipt += docTypeReadVersion_.save(storage);
+    
+    return receipt;
+  }
+  
+  //----------------------------------------------------------------
+  // EbmlHead::load
+  // 
+  uint64
+  EbmlHead::load(FileStorage & storage,
+                 uint64 bytesToRead,
+                 IDelegateLoad * loader)
+  {
+    uint64 prevBytesToRead = bytesToRead;
+    
+    bytesToRead -= version_.load(storage, bytesToRead, loader);
+    bytesToRead -= readVersion_.load(storage, bytesToRead, loader);
+    bytesToRead -= maxIdLength_.load(storage, bytesToRead, loader);