[vlc-devel] [PATCH 5/5] Added the possibility to save an adjusted subtitle file (.srt only)
Pascal Thomet
pthomet at gmail.com
Mon Jun 9 21:00:27 CEST 2014
Principle:
* Inside the "Track synchronization window" a button was added ("Save Subtitle")
* This button is only shown for *file subtitles* (not DVB, nor DVB)
* It will save the subtitles as an srt file with (the srt file name will be proposed under
the form /subtitle/original/path/original_subtitle_file_adjusted.srt
(Also added doc for Mac OSX port inside SyncTracks.xib)
---
.../macosx/Resources/English.lproj/SyncTracks.xib | 161 ++++++++++++---------
modules/demux/subtitle.c | 117 ++++++++++++++-
modules/gui/qt4/components/extended_panels.cpp | 38 ++++-
modules/gui/qt4/components/extended_panels.hpp | 1 +
4 files changed, 245 insertions(+), 72 deletions(-)
diff --git a/extras/package/macosx/Resources/English.lproj/SyncTracks.xib b/extras/package/macosx/Resources/English.lproj/SyncTracks.xib
index 9cb68a6..8f0ff45 100644
--- a/extras/package/macosx/Resources/English.lproj/SyncTracks.xib
+++ b/extras/package/macosx/Resources/English.lproj/SyncTracks.xib
@@ -1,88 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Note :
- As this file depends on Interface Builder 3,
- I could not update it in order to add the subtitle sync feature
- which I added in the Qt "Track Synchronization window" in june 2014
+As this file depends on Interface Builder 3,
+I could not update it in order to add the subtitle sync feature
+which I added in the Qt "Track Synchronization window" in june 2014
- I prefer not to mess around it, as I am not specialist enough with
- interface builder...
+I prefer not to mess around it, as I am not specialist enough with
+interface builder...
- Pascal Thomet - 06-2014
+Pascal Thomet - 06-2014
- Below is the relevant code in QT dialect, if someone has the courage to update the
- .xib as well as its callbacks.
+Below is the relevant code in QT dialect, if someone has the courage to update the
+.xib as well as its callbacks, it would be worthwhile.
- /* Add a panel that contains 4 buttons
- (bookmark audio, bookmark subtitle, sync subtitles, reset sync)
- Make that panel visible if var_GetInteger( THEMIM->getInput(), "sub-isfilesub" ) == 1
- */
+/*Add a panel that contains 5 buttons :
+bookmark audio
+bookmark subtitle
+sync subtitles
+reset sync
+save subtitles
+Make that panel visible if
+var_GetInteger( THEMIM->getInput(), "sub-isfilesub" ) == 1
+*/
- int isfilesub = var_GetInteger( THEMIM->getInput(), "sub-isfilesub" );
+int isfilesub = var_GetInteger( THEMIM->getInput(), "sub-isfilesub" );
+if (isfilesub)
+{
+ subsVisualSyncBox = new QGroupBox( qtr( "Subtitles Visual Sync" ) );
+ QGridLayout *subsVisualSyncLayout = new QGridLayout( subsVisualSyncBox );
- /* Subs Visual Sync*/
- if (isfilesub)
- {
- subsVisualSyncBox = new QGroupBox( qtr( "Subtitles Visual Sync" ) );
- QGridLayout *subsVisualSyncLayout = new QGridLayout( subsVisualSyncBox );
-
- QPushButton *buttonBookmarkAudio = new QPushButton(_("Bookmark audio"));
- buttonBookmarkAudio->setToolTip(_("You can also use Shift-H on the video"));
- subsVisualSyncLayout->addWidget( buttonBookmarkAudio, 0, 0, 1, 1 );
- CONNECT( buttonBookmarkAudio, pressed(), this, bookmarkAudio() ) ;
+ QPushButton *buttonBookmarkAudio = new QPushButton(_("Bookmark audio"));
+ buttonBookmarkAudio->setToolTip(_("You can also use Shift-H on the video"));
+ subsVisualSyncLayout->addWidget( buttonBookmarkAudio, 0, 0, 1, 1 );
+ CONNECT( buttonBookmarkAudio, pressed(), this, bookmarkAudio() ) ;
- QPushButton *buttonBookmarkSync = new QPushButton(_("Sync subtitles"));
- QString buttonBookmarkSync_Tooltip = qtr(
- "You can also use Shift-K on the video\n"\
- "\n"\
- "\n"\
- "If the subtitle has a different fps,\n"\
- "(ie the delay varies during the movie):\n"\
- "\n"\
- "* sync the subtitles at the beginning of the video\n"\
- "* fast forward a few minutes in the movie\n"\
- "* then resync the subtitles.\n"\
- "\n"\
- "If applicable, you will be asked to change the fps\n"\
- "of the subtitle.\n"\
- );
- buttonBookmarkSync->setToolTip( buttonBookmarkSync_Tooltip );
- subsVisualSyncLayout->addWidget( buttonBookmarkSync, 0, 2, 1, 1 );
- CONNECT( buttonBookmarkSync, pressed(), this, syncBookmarks() ) ;
+ QPushButton *buttonBookmarkSync = new QPushButton(_("Sync subtitles"));
+ QString buttonBookmarkSync_Tooltip = qtr(
+ "You can also use Shift-K on the video\n"\
+ "\n"\
+ "\n"\
+ "If the subtitle has a different fps,\n"\
+ "(ie the delay varies during the movie):\n"\
+ "\n"\
+ "* sync the subtitles at the beginning of the video\n"\
+ "* fast forward a few minutes in the movie\n"\
+ "* then resync the subtitles.\n"\
+ "\n"\
+ "If applicable, you will be asked to change the fps\n"\
+ "of the subtitle.\n"\
+ );
+ buttonBookmarkSync->setToolTip( buttonBookmarkSync_Tooltip );
+ subsVisualSyncLayout->addWidget( buttonBookmarkSync, 0, 2, 1, 1 );
+ CONNECT( buttonBookmarkSync, pressed(), this, syncBookmarks() ) ;
- QPushButton *buttonBookmarkSync = new QPushButton(_("Sync subtitles"));
- buttonBookmarkSync->setToolTip(_("You can also use Shift-K on the video"));
- subsVisualSyncLayout->addWidget( buttonBookmarkSync, 0, 2, 1, 1 );
- CONNECT( buttonBookmarkSync, pressed(), this, syncBookmarks() ) ;
+ QPushButton *buttonBookmarkSync = new QPushButton(_("Sync subtitles"));
+ buttonBookmarkSync->setToolTip(_("You can also use Shift-K on the video"));
+ subsVisualSyncLayout->addWidget( buttonBookmarkSync, 0, 2, 1, 1 );
+ CONNECT( buttonBookmarkSync, pressed(), this, syncBookmarks() ) ;
- QPushButton *buttonBookmarkResetSync = new QPushButton(_("Reset Sync"));
- buttonBookmarkResetSync->setToolTip(_("You can also use Ctrl-Shift-K on the video"));
- subsVisualSyncLayout->addWidget( buttonBookmarkResetSync, 0, 3, 1, 1 );
- CONNECT( buttonBookmarkResetSync, pressed(), this, resetSync() ) ;
+ QPushButton *buttonBookmarkResetSync = new QPushButton(_("Reset Sync"));
+ buttonBookmarkResetSync->setToolTip(_("You can also use Ctrl-Shift-K on the video"));
+ subsVisualSyncLayout->addWidget( buttonBookmarkResetSync, 0, 3, 1, 1 );
+ CONNECT( buttonBookmarkResetSync, pressed(), this, resetSync() ) ;
- mainLayout->addWidget( subsVisualSyncBox, 4, 0, 1, 5 );
- }
+ mainLayout->addWidget( subsVisualSyncBox, 4, 0, 1, 5 );
+}
- //The callbacks are one liners as below :
- void SyncControls::bookmarkAudio()
- {
- var_SetInteger( THEMIM->getInput(), "sub-bookmarkaudio", 0 );
+//The callbacks are one liners as below :
+void SyncControls::bookmarkAudio()
+{
+ var_SetInteger( THEMIM->getInput(), "sub-bookmarkaudio", 0 );
+}
+void SyncControls::bookmarkSubtitle()
+{
+ var_SetInteger( THEMIM->getInput(), "sub-bookmarksubtitle", 0 );
+}
+void SyncControls::syncBookmarks()
+{
+ var_SetInteger( THEMIM->getInput(), "sub-syncbookmarks", 0 );
+ update();
+}
+void SyncControls::resetSync()
+{
+ var_SetInteger( THEMIM->getInput(), "sub-syncreset", 0 );
+ update();
+}
- }
- void SyncControls::bookmarkSubtitle()
- {
- var_SetInteger( THEMIM->getInput(), "sub-bookmarksubtitle", 0 );
- }
- void SyncControls::syncBookmarks()
+//The save subtitle callback is a little more lengthy, but nonetheless simple
+void SyncControls::saveSubtitles()
+{
+ char * sub_srt_file_path = var_GetString( THEMIM->getInput(), "sub-srt-file-path");
+ char * sub_srt_file_content = var_GetString( THEMIM->getInput(), "sub-srt-file-content");
+ // open a file save dialog box with a default name for the file
+ QString fileName_QString = QFileDialog::getSaveFileName(this, _("Save Subtitle as .srt)"),sub_srt_file_path);
+ if (fileName_QString.length() == 0)
+ return;
+ //Convert filename to plain char*
+ QByteArray fileName_ba = fileName_QString.toLocal8Bit();
+ const char *fileName_str = fileName_ba.data();
+ //Save
+ FILE *f = fopen(fileName_str, "wb"); /* binary! .srt should use CRLF we want to impose this */
+ if (f)
{
- var_SetInteger( THEMIM->getInput(), "sub-syncbookmarks", 0 );
- update();
+ fprintf(f, "%s", sub_srt_file_content);
+ fclose(f);
+ msg_Warn(THEMIM->getInput(), "Saved subtitles inside %s", fileName_str);
}
- void SyncControls::resetSync()
+ else
{
- var_SetInteger( THEMIM->getInput(), "sub-syncreset", 0 );
- update();
+ msg_Err(THEMIM->getInput(), "Could not save subtitles inside %s", fileName_str);
}
+}
+
-->
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
<data>
diff --git a/modules/demux/subtitle.c b/modules/demux/subtitle.c
index 4d72061..bee56e2 100644
--- a/modules/demux/subtitle.c
+++ b/modules/demux/subtitle.c
@@ -41,6 +41,7 @@
#include <vlc_demux.h>
#include <vlc_charset.h>
+#include <sys/param.h>
/*****************************************************************************
* Module descriptor
@@ -214,6 +215,8 @@ struct demux_sys_t
struct sub_speed_delay sub_speed_delay_current;
};
+static int StoreAdjustedSubsAsSubRip(demux_t *p_demux);
+
static int ParseMicroDvd ( demux_t *, subtitle_t *, int );
static int ParseSubRip ( demux_t *, subtitle_t *, int );
static int ParseSubViewer ( demux_t *, subtitle_t *, int );
@@ -660,13 +663,28 @@ static int Open ( vlc_object_t *p_this )
var_Create(p_demux->p_parent, "sub-bookmarksubtitle", VLC_VAR_INTEGER);
var_Create(p_demux->p_parent, "sub-syncbookmarks", VLC_VAR_INTEGER);
var_Create(p_demux->p_parent, "sub-syncreset", VLC_VAR_INTEGER);
+ var_Create(p_demux->p_parent, "sub-srt-file-content", VLC_VAR_STRING);
+ var_Create(p_demux->p_parent, "sub-srt-file-path", VLC_VAR_STRING);
var_AddCallback( p_demux->p_parent, "sub-bookmarkaudio", &subtitle_external_callback, p_this );
var_AddCallback( p_demux->p_parent, "sub-bookmarksubtitle", &subtitle_external_callback, p_this );
var_AddCallback( p_demux->p_parent, "sub-syncbookmarks", &subtitle_external_callback, p_this );
var_AddCallback( p_demux->p_parent, "sub-syncreset", &subtitle_external_callback, p_this );
var_AddCallback( p_demux->p_parent, "spu-delay", &subtitle_external_callback, p_this );
-
+ /*Set srt-file-path :
+ * File path to be suggested if the user
+ * chooses to save the subtitles with adjusted timings
+ */
+ {
+ char path[MAXPATHLEN];
+ strncpy(path, p_demux->s->psz_path, MAXPATHLEN);
+ char *extensionStart = rindex(path, '.');
+ if ( extensionStart )
+ *extensionStart = '\0';
+ strncat(path, "_adjusted.srt", MAXPATHLEN);
+ var_SetString(p_demux->p_parent, "sub-srt-file-path", path);
+ }
+
p_sys->psz_header = NULL;
p_sys->i_subtitle = 0;
p_sys->i_subtitles = 0;
@@ -986,6 +1004,8 @@ static int Open ( vlc_object_t *p_this )
p_sys->es = es_out_Add( p_demux->out, &fmt );
es_format_Clean( &fmt );
+ StoreAdjustedSubsAsSubRip(p_demux);
+
return VLC_SUCCESS;
}
@@ -1010,7 +1030,8 @@ static void Close( vlc_object_t *p_this )
var_Destroy(p_demux->p_parent, "sub-bookmarksubtitle");
var_Destroy(p_demux->p_parent, "sub-syncbookmarks");
var_Destroy(p_demux->p_parent, "sub-syncreset");
-
+ var_Destroy(p_demux->p_parent, "sub-srt-file-content");
+ var_Destroy(p_demux->p_parent, "sub-srt-file-path");
int i;
for( i = 0; i < p_sys->i_subtitles; i++ )
@@ -1045,6 +1066,8 @@ static int set_current_subtitle_by_time(demux_t *p_demux, int64_t i64_when)
if( p_sys->i_subtitle >= p_sys->i_subtitles )
return VLC_EGENERIC;
+
+ StoreAdjustedSubsAsSubRip(p_demux);
return VLC_SUCCESS;
}
@@ -1499,6 +1522,96 @@ static int ParseSubRip( demux_t *p_demux, subtitle_t *p_subtitle,
false );
}
+static void WriteSubViewerTiming(char *dst, int dstLength, int64_t time)
+{
+ float seconds_total_float = (float)time / ( 1000.f * 1000.f);
+ int seconds_total = (int)seconds_total_float;
+
+ float decimals_float = seconds_total_float - (int) seconds_total_float;
+ int decimals_int = (int) (decimals_float * 1000.f + 0.5f);
+ int seconds = seconds_total % 60;
+
+ int minutes_total = (seconds_total - seconds) / 60;
+ int minutes = minutes_total % 60;
+
+ int hours_total = (minutes_total - minutes) / 60;
+
+ snprintf( dst, dstLength, "%02i:%02i:%02i,%03i", hours_total, minutes, seconds, decimals_int);
+}
+
+/* Creates a string that contains the content of a .srt file
+ * with adjusted timings and stores it inside the var
+ * "sub-srt-file-content"
+ */
+static int StoreAdjustedSubsAsSubRip(demux_t *p_demux)
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ //we increment the buffer size by 10kB increments
+ const size_t bufferIncrement = 1024 * 10;
+ char * buffer = (char *) malloc(bufferIncrement);
+ if ( ! buffer )
+ return VLC_ENOMEM;
+ size_t bufferLength = bufferIncrement;
+ buffer[0] = 0;
+
+ //Max length for one subtitle info
+ const int oneSubtitleBufferLength = 3000;
+ char oneSubtitleBuffer[oneSubtitleBufferLength];
+
+ int i = 0;
+ char subIndexBuffer[20];
+ char time1Buffer[40];
+ char time2Buffer[40];
+ char timingBuffer[100];
+ char subtitleContent[2000];
+ for ( i = 0; i < p_sys->i_subtitles; i++)
+ {
+ const subtitle_t *p_subtitle = &p_sys->subtitle[i];
+ int64_t i_start_adjust = adjust_subtitle_time(p_demux, p_subtitle->i_start);
+ int64_t i_end_adjust = adjust_subtitle_time(p_demux, p_subtitle->i_stop);
+
+ snprintf(subIndexBuffer, 20, "%i", i + 1);
+
+ WriteSubViewerTiming(time1Buffer, 40, i_start_adjust);
+ WriteSubViewerTiming(time2Buffer, 40, i_end_adjust);
+ snprintf(timingBuffer, 100, "%s --> %s", time1Buffer, time2Buffer);
+
+ strncpy(subtitleContent, p_subtitle->psz_text, 2000);
+ {
+ /* strip the last EOL that might already terminate the subtitle*/
+ size_t len = strlen(subtitleContent);
+ if ( ( len >= 2 )
+ && ( subtitleContent[len - 2 ] == '\r' )
+ && (subtitleContent[len - 2 ] == '\n' ) )
+ {
+ subtitleContent[len - 2 ] = 0;
+ }
+ if ( ( len >= 1 ) && ( subtitleContent[len - 1 ] == '\n' ) )
+ {
+ subtitleContent[len - 1 ] = 0;
+ }
+ }
+
+ snprintf( oneSubtitleBuffer, oneSubtitleBufferLength, "%s\r\n%s\r\n%s\r\n\r\n",
+ subIndexBuffer, timingBuffer, subtitleContent);
+
+ int nbBytesToAppend = strlen(oneSubtitleBuffer);
+
+ if ( strlen(buffer) + nbBytesToAppend >= bufferLength )
+ {
+ buffer = realloc(buffer, bufferLength + bufferIncrement);
+ if ( ! buffer )
+ return VLC_ENOMEM;
+ bufferLength = bufferLength + bufferIncrement;
+ }
+ strncat(buffer, oneSubtitleBuffer, nbBytesToAppend);
+ }
+ var_SetString(p_demux->p_parent, "sub-srt-file-content", buffer);
+ free(buffer);
+ return VLC_SUCCESS;
+}
+
+
/* subtitle_ParseSubViewerTiming
* Parses SubViewer timing.
*/
diff --git a/modules/gui/qt4/components/extended_panels.cpp b/modules/gui/qt4/components/extended_panels.cpp
index 15a0656..b24f0a6 100644
--- a/modules/gui/qt4/components/extended_panels.cpp
+++ b/modules/gui/qt4/components/extended_panels.cpp
@@ -1536,12 +1536,12 @@ SyncControls::SyncControls( intf_thread_t *_p_intf, QWidget *_parent ) :
QPushButton *buttonBookmarkAudio = new QPushButton(_("Bookmark audio"));
buttonBookmarkAudio->setToolTip(_("You can also use Shift-H on the video"));
subsVisualSyncLayout->addWidget( buttonBookmarkAudio, 0, 0, 1, 1 );
- CONNECT( buttonBookmarkAudio, pressed(), this, bookmarkAudio() ) ;
+ CONNECT( buttonBookmarkAudio, pressed(), this, bookmarkAudio() );
QPushButton *buttonBookmarkSubtitle = new QPushButton(_("Bookmark subtitle"));
buttonBookmarkSubtitle->setToolTip(_("You can also use Shift-J on the video"));
subsVisualSyncLayout->addWidget( buttonBookmarkSubtitle, 0, 1, 1, 1 );
- CONNECT( buttonBookmarkSubtitle, pressed(), this, bookmarkSubtitle() ) ;
+ CONNECT( buttonBookmarkSubtitle, pressed(), this, bookmarkSubtitle() );
QPushButton *buttonBookmarkSync = new QPushButton(_("Sync subtitles"));
QString buttonBookmarkSync_Tooltip = qtr(
@@ -1562,12 +1562,17 @@ SyncControls::SyncControls( intf_thread_t *_p_intf, QWidget *_parent ) :
subsVisualSyncLayout->addWidget( buttonBookmarkSync, 0, 2, 1, 1 );
subsVisualSyncLayout->addWidget( buttonBookmarkSync, 0, 2, 1, 1 );
- CONNECT( buttonBookmarkSync, pressed(), this, syncBookmarks() ) ;
+ CONNECT( buttonBookmarkSync, pressed(), this, syncBookmarks() );
QPushButton *buttonBookmarkResetSync = new QPushButton(_("Reset Sync"));
buttonBookmarkResetSync->setToolTip(_("You can also use Ctrl-Shift-K on the video"));
subsVisualSyncLayout->addWidget( buttonBookmarkResetSync, 0, 3, 1, 1 );
- CONNECT( buttonBookmarkResetSync, pressed(), this, resetSync() ) ;
+ CONNECT( buttonBookmarkResetSync, pressed(), this, resetSync() );
+
+ QPushButton *buttonBookmarkSaveSubs = new QPushButton(_("Save Subs"));
+ buttonBookmarkSaveSubs->setToolTip(_("Save adjusted subtitles"));
+ subsVisualSyncLayout->addWidget( buttonBookmarkSaveSubs, 0, 4, 1, 1 );
+ CONNECT( buttonBookmarkSaveSubs, pressed(), this, saveSubtitles() );
mainLayout->addWidget( subsVisualSyncBox, 4, 0, 1, 5 );
}
@@ -1682,6 +1687,31 @@ void SyncControls::resetSync()
}
+void SyncControls::saveSubtitles()
+{
+ char * sub_srt_file_path = var_GetString( THEMIM->getInput(), "sub-srt-file-path");
+ char * sub_srt_file_content = var_GetString( THEMIM->getInput(), "sub-srt-file-content");
+
+ QString fileName_QString = QFileDialog::getSaveFileName(this, _("Save Subtitle as .srt)"),
+ sub_srt_file_path);
+ if (fileName_QString.length() == 0)
+ return;
+ QByteArray fileName_ba = fileName_QString.toLocal8Bit();
+ const char *fileName_str = fileName_ba.data();
+
+ FILE *f = fopen(fileName_str, "wb"); /* binary! .srt should use CRLF we want to impose this */
+ if (f)
+ {
+ fprintf(f, "%s", sub_srt_file_content);
+ fclose(f);
+ msg_Warn(THEMIM->getInput(), "Saved subtitles inside %s", fileName_str);
+ }
+ else
+ {
+ msg_Err(THEMIM->getInput(), "Could not save subtitles inside %s", fileName_str);
+ }
+}
+
void SyncControls::initSubsDuration()
{
int i_mode = var_InheritInteger( p_intf, SUBSDELAY_CFG_MODE );
diff --git a/modules/gui/qt4/components/extended_panels.hpp b/modules/gui/qt4/components/extended_panels.hpp
index 0eb3fc6..2629bff 100644
--- a/modules/gui/qt4/components/extended_panels.hpp
+++ b/modules/gui/qt4/components/extended_panels.hpp
@@ -246,6 +246,7 @@ private slots:
void bookmarkSubtitle();
void syncBookmarks();
void resetSync();
+ void saveSubtitles();
};
#endif
--
1.9.1
More information about the vlc-devel
mailing list