[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