]> granicus.if.org Git - handbrake/commitdiff
Fix Previous Bad commit for Cyanders Chapter Markers
authordynaflash <dynaflashtech@gmail.com>
Wed, 25 Apr 2007 17:24:58 +0000 (17:24 +0000)
committerdynaflash <dynaflashtech@gmail.com>
Wed, 25 Apr 2007 17:24:58 +0000 (17:24 +0000)
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@548 b64f7644-9d1e-0410-96f1-a4d463321fa5

20 files changed:
libhb/decmpeg2.c
libhb/demuxmpeg.c
libhb/dvd.c
libhb/encxvid.c
libhb/fifo.c
libhb/internal.h
libhb/muxmp4.c
libhb/sync.c
libhb/work.c
macosx/ChapterTitles.h [new file with mode: 0644]
macosx/ChapterTitles.m [new file with mode: 0644]
macosx/Controller.h
macosx/Controller.mm
macosx/English.lproj/MainMenu.nib/classes.nib
macosx/English.lproj/MainMenu.nib/info.nib
macosx/English.lproj/MainMenu.nib/keyedobjects.nib
test/Makefile
test/parsecsv.c [new file with mode: 0644]
test/parsecsv.h [new file with mode: 0644]
test/test.c

index 214444a05a57851f3bfabbc255de161cc49ebae4..4fb6ad76b4c3c933e1659150fe548024f864bf35 100644 (file)
@@ -21,6 +21,7 @@ struct hb_libmpeg2_s
     int                  height;
     int                  rate;
     int                  got_iframe;
+    int                  look_for_break;
     int64_t              last_pts;
 };
 
@@ -36,6 +37,7 @@ hb_libmpeg2_t * hb_libmpeg2_init()
     m->libmpeg2 = mpeg2_init();
     m->info     = mpeg2_info( m->libmpeg2 );
     m->last_pts = -1;
+    m->look_for_break = 0;
 
     return m;
 }
@@ -51,6 +53,7 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es,
     mpeg2_state_t   state;
     hb_buffer_t   * buf;
     uint8_t       * data;
+    int             chap_break = 0;
 
     /* Feed libmpeg2 */
     if( buf_es->start > -1 )
@@ -86,6 +89,11 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es,
                 }
             }
         }
+        else if( state == STATE_GOP && m->look_for_break == 2)
+        {
+            printf("MPEG2: Group of pictures found, searching for I-Frame\n");
+            m->look_for_break = 1;
+        }
         else if( ( state == STATE_SLICE || state == STATE_END ) &&
                  m->info->display_fbuf )
         {
@@ -93,6 +101,14 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es,
                   PIC_MASK_CODING_TYPE ) == PIC_FLAG_CODING_TYPE_I )
             {
                 m->got_iframe = 1;
+                
+                // If we are looking for a break, insert the chapter break on an I-Frame
+                if( m->look_for_break == 1 )
+                {
+                    printf("MPEG2: I-Frame Found\n");
+                    m->look_for_break = 0;
+                    chap_break = 1;
+                }
             }
 
             if( m->got_iframe )
@@ -100,6 +116,14 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es,
                 buf  = hb_buffer_init( m->width * m->height * 3 / 2 );
                 data = buf->data;
 
+                // Was a good break point found?
+                if( chap_break )
+                {
+                    printf("MPEG2: Chapter Break Inserted\n");
+                    chap_break = 0;
+                    buf->new_chap = 1;
+                }
+
                 memcpy( data, m->info->display_fbuf->buf[0],
                         m->width * m->height );
                 data += m->width * m->height;
@@ -208,6 +232,15 @@ int decmpeg2Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
     hb_work_private_t * pv = w->private_data;
     hb_buffer_t * buf, * last = NULL;
 
+    // The reader found a chapter break, consume it completely, and remove it from the
+    // stream. We need to shift it.
+    if( (*buf_in)->new_chap )
+    {
+        printf("MPEG2: Chapter Break Cell Found, searching for GOP\n");
+        pv->libmpeg2->look_for_break = 2;
+        (*buf_in)->new_chap = 0;
+    }
+
     hb_libmpeg2_decode( pv->libmpeg2, *buf_in, pv->list );
 
     *buf_out = NULL;
index 721478e802787f2e415d47aec2f07da324a3243f..5aefc37439a9345919b6ee817bbab057c42deaf8 100644 (file)
@@ -110,8 +110,10 @@ int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es )
         /* Here we hit we ES payload */
         buf_es = hb_buffer_init( pes_packet_end - pos );
 
-        buf_es->id    = id;
-        buf_es->start = pts;
+        buf_es->id       = id;
+        buf_es->start    = pts;
+        buf_es->new_chap = buf_ps->new_chap; // Consume a chapter break, and apply it to the ES.
+        buf_ps->new_chap = 0;
         memcpy( buf_es->data, d + pos, pes_packet_end - pos );
 
         hb_list_add( list_es, buf_es );
index 9b4ba3fe718733f1fe47235ba5904cc2e53cc6a3..b26c559953894de4d25f570d432b5de6caa3903d 100644 (file)
@@ -30,6 +30,7 @@ struct hb_dvd_s
     int            title_block_count;
     int            cell_cur;
     int            cell_next;
+    int            cell_overlap;
     int            block;
     int            pack_len;
     int            next_vobu;
@@ -555,7 +556,8 @@ int hb_dvd_start( hb_dvd_t * d, int title, int chapter )
     d->block     = d->pgc->cell_playback[d->cell_cur].first_sector;
     d->next_vobu = d->block;
     d->pack_len  = 0;
-
+    d->cell_overlap = 0;
+    
     return 1;
 }
 
@@ -709,6 +711,19 @@ int hb_dvd_read( hb_dvd_t * d, hb_buffer_t * b )
             d->cell_cur  = d->cell_next;
             d->next_vobu = d->pgc->cell_playback[d->cell_cur].first_sector;
             FindNextCell( d );
+            d->cell_overlap = 1;
+            printf("DVD: End of Cell\n");
+        }
+        
+        // Revert the cell overlap, and check for a chapter break
+        if( dsi_pack.vobu_sri.prev_vobu == SRI_END_OF_CELL )
+        {
+            printf("DVD: Beginning of Cell\n");
+            if( d->cell_overlap )
+            {
+                b->new_chap = hb_dvd_is_break( d );
+                d->cell_overlap = 0;
+            }
         }
     }
     else
@@ -736,19 +751,20 @@ int hb_dvd_chapter( hb_dvd_t * d )
 {
     int     i;
     int     pgc_id, pgn;
+       int     nr_of_ptts = d->ifo->vts_ptt_srpt->title[d->ttn-1].nr_of_ptts;
     pgc_t * pgc;
 
-    for( i = 0;
-         i < d->ifo->vts_ptt_srpt->title[d->ttn-1].nr_of_ptts;
-         i++ )
+    for( i = nr_of_ptts - 1;
+         i >= 0;
+         i-- )
     {
         /* Get pgc for chapter (i+1) */
         pgc_id = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[i].pgcn;
         pgn    = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[i].pgn;
         pgc    = d->ifo->vts_pgcit->pgci_srp[pgc_id-1].pgc;
 
-        if( d->cell_cur >= pgc->program_map[pgn-1] - 1 &&
-            d->cell_cur <= pgc->nr_of_cells - 1 )
+        if( d->cell_cur - d->cell_overlap >= pgc->program_map[pgn-1] - 1 &&
+            d->cell_cur - d->cell_overlap <= pgc->nr_of_cells - 1 )
         {
             /* We are in this chapter */
             return i + 1;
@@ -759,6 +775,70 @@ int hb_dvd_chapter( hb_dvd_t * d )
     return -1;
 }
 
+/***********************************************************************
+ * hb_dvd_is_break
+ ***********************************************************************
+ * Returns 1 if the current block is a new chapter start
+ **********************************************************************/
+int hb_dvd_is_break( hb_dvd_t * d )
+{
+    int     i, j;
+    int     pgc_id, pgn;
+       int     nr_of_ptts = d->ifo->vts_ptt_srpt->title[d->ttn-1].nr_of_ptts;
+    pgc_t * pgc;
+    int     cell, chapter_length, cell_end;
+    
+    for( i = nr_of_ptts - 1;
+         i > 0;
+         i-- )
+    {
+        /* Get pgc for chapter (i+1) */
+        pgc_id = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[i].pgcn;
+        pgn    = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[i].pgn;
+        pgc    = d->ifo->vts_pgcit->pgci_srp[pgc_id-1].pgc;
+        cell   = pgc->program_map[pgn-1] - 1;
+
+        if( cell <= d->cell_start )
+            break;
+
+        // This must not match against the start cell.
+        if( pgc->cell_playback[cell].first_sector == d->block && cell != d->cell_start )
+        {
+            /* Check to see if we merged this chapter into the previous one... */
+            /* As a note, merging chapters is probably bad practice for this very reason */
+            chapter_length = 0;
+            
+            if( i == nr_of_ptts - 1 )
+            {
+                cell_end = d->pgc->nr_of_cells;
+            }
+            else
+            {
+                cell_end = pgc->program_map[pgn] - 1;
+            }
+            
+            for( j = cell; j < cell_end; j++ )
+            {
+                chapter_length += pgc->cell_playback[j].last_sector + 1 - 
+                                  pgc->cell_playback[j].first_sector;
+            }
+            
+            if( chapter_length >= 2048 )
+            {
+                printf("DVD: Chapter Break Cell Found\n");
+                /* We have a chapter break */
+                return 1;
+            }
+            else
+            {
+                printf("DVD: Cell Found (%d)\n", chapter_length);
+            }
+        }
+    }
+    
+    return 0;
+}
+
 /***********************************************************************
  * hb_dvd_close
  ***********************************************************************
index fbaa5a135755b40537706a0d4861869ce5e09d56..7430aca0fc67683a9897a0356fe64d4bdcb9d83e 100644 (file)
@@ -160,6 +160,7 @@ int encxvidWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
     buf = hb_buffer_init( 3 * job->width * job->height / 2 );
     buf->start = in->start;
     buf->stop  = in->stop;
+    //buf->chap  = in->chap;
 
     memset( &frame, 0, sizeof( frame ) );
 
index 323406538d8f99a7632a2d876cd7a28233fccdc7..fe03c47f94222f53f2ac50fb01c7d46e1f675891 100644 (file)
@@ -129,7 +129,7 @@ hb_buffer_t * hb_fifo_get( hb_fifo_t * f )
     b->next   = NULL;
     f->size  -= 1;
     hb_unlock( f->lock );
-
+    
     return b;
 }
 
index efe721e1221cc452b2352ff449e3179a36d26d76..f950205d5a67e73b9f75dbb6f550c1d1e11da08f 100644 (file)
@@ -37,6 +37,7 @@ struct hb_buffer_s
     int           id;
     int64_t       start;
     int64_t       stop;
+    int           new_chap;
     int           key;
 
     /* Holds the output PTS from x264, for use by b-frame offsets in muxmp4.c */
@@ -111,6 +112,7 @@ void         hb_dvd_stop( hb_dvd_t * );
 int          hb_dvd_seek( hb_dvd_t *, float );
 int          hb_dvd_read( hb_dvd_t *, hb_buffer_t * );
 int          hb_dvd_chapter( hb_dvd_t * );
+int          hb_dvd_is_break( hb_dvd_t * d );
 void         hb_dvd_close( hb_dvd_t ** );
 
 /***********************************************************************
index 7aae352cdc8d39c44c5a6b4eb24cddb3a144d2b0..e2de1ce860726cc5f9ac61f5bb055f5151cdf71e 100644 (file)
@@ -27,6 +27,10 @@ struct hb_mux_object_s
     /* Cumulated durations so far, in timescale units (see MP4Mux) */
     uint64_t sum_dur;
        
+    /* Chapter state information for muxing */
+    MP4TrackId chapter_track;
+    int current_chapter;
+    uint64_t chapter_duration;
 };
 
 struct hb_mux_data_s
@@ -34,6 +38,91 @@ struct hb_mux_data_s
     MP4TrackId track;
 };
 
+struct hb_text_sample_s
+{
+    uint8_t     sample[1280];
+    uint32_t    length;
+    MP4Duration duration;
+};
+
+/**********************************************************************
+ * MP4CreateTextSample
+ **********************************************************************
+ * Creates a buffer for a text track sample
+ *********************************************************************/
+static struct hb_text_sample_s *MP4CreateTextSample( char *textString, uint64_t duration )
+{
+    struct hb_text_sample_s *sample = NULL;
+    int stringLength = strlen(textString);
+    int x;
+    
+    if( stringLength < 1024 )
+    {
+        sample = malloc( sizeof( struct hb_text_sample_s ) );
+
+        //textLength = (stringLength; // Account for BOM     
+        sample->length = stringLength + 2 + 12; // Account for text length code and other marker
+        sample->duration = (MP4Duration)duration;
+        
+        // 2-byte length marker
+        sample->sample[0] = (stringLength >> 8) & 0xff;
+        sample->sample[1] = stringLength & 0xff;
+        
+        strncpy( (char *)&(sample->sample[2]), textString, stringLength );
+        
+        x = 2 + stringLength;
+
+        // Modifier Length Marker
+        sample->sample[x] = 0x00;
+        sample->sample[x+1] = 0x00;
+        sample->sample[x+2] = 0x00;
+        sample->sample[x+3] = 0x0C;
+        
+        // Modifier Type Code
+        sample->sample[x+4] = 'e';
+        sample->sample[x+5] = 'n';
+        sample->sample[x+6] = 'c';
+        sample->sample[x+7] = 'd';
+        
+        // Modifier Value
+        sample->sample[x+8] = 0x00;
+        sample->sample[x+9] = 0x00;
+        sample->sample[x+10] = (256 >> 8) & 0xff;
+        sample->sample[x+11] = 256 & 0xff;
+    }
+    
+    return sample;
+}
+/**********************************************************************
+ * MP4GenerateChapterSample
+ **********************************************************************
+ * Creates a buffer for a text track sample
+ *********************************************************************/
+static struct hb_text_sample_s *MP4GenerateChapterSample( hb_mux_object_t * m, uint64_t duration )
+{
+    int chapter = m->current_chapter;
+    hb_chapter_t *chapter_data = hb_list_item( m->job->title->list_chapter, chapter - 1 );
+    char tmp_buffer[1024];
+    char *string = tmp_buffer;
+    
+    tmp_buffer[0] = '\0';
+    
+    if( chapter_data != NULL )
+    {
+        string = chapter_data->title;
+    }
+    
+    if( strlen(string) == 0 || strlen(string) >= 1024 )
+    {
+        snprintf( tmp_buffer, 1023, "Chapter %03i", chapter );
+        string = tmp_buffer;
+    }
+    
+    return MP4CreateTextSample( string, duration );
+}
+
 /**********************************************************************
  * MP4Init
  **********************************************************************
@@ -171,36 +260,15 @@ static int MP4Init( hb_mux_object_t * m )
                
     }
 
-       if (job->chapter_markers) {
-
+       if (job->chapter_markers) 
+    {
                /* add a text track for the chapters */
                MP4TrackId textTrack;
-
                textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
-
-               /* write the chapter markers for each selected chapter */
-               char markerBuf[13];
-               hb_chapter_t  * chapter;
-               MP4Duration chapterDuration;
-               float fOrigDuration, fTimescale;
-               float fTSDuration;
-
-               for( i = job->chapter_start - 1; i <= job->chapter_end - 1; i++ )
-               {
-                       chapter = hb_list_item( title->list_chapter, i );
-
-                       fOrigDuration = chapter->duration;
-                       fTimescale = job->arate;
-                       fTSDuration = (fOrigDuration / 90000) * fTimescale;
-                       chapterDuration = (MP4Duration)fTSDuration;
-                       
-                       sprintf(markerBuf, "  Chapter %03i", i + 1);
-                       markerBuf[0] = 0;
-                       markerBuf[1] = 11; // "Chapter xxx"
-                       MP4WriteSample(m->file, textTrack, (u_int8_t*)markerBuf, 13, chapterDuration, 0, true);
-
-               }
-               
+        
+        m->chapter_track = textTrack;
+        m->chapter_duration = 0;
+        m->current_chapter = job->chapter_start;
        }
        
     return 0;
@@ -214,7 +282,21 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
     uint64_t duration;
 
     if( mux_data == job->mux_data )
-    {
+    {    
+        /* Add the sample before the new frame.
+           It is important that this be calculated prior to the duration
+           of the new video sample, as we want to sync to right after it.
+           (This is because of how durations for text tracks work in QT) */
+        if( job->chapter_markers && buf->new_chap )
+        {
+            struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
+        
+            MP4WriteSample(m->file, m->chapter_track, sample->sample, sample->length, sample->duration, 0, true);
+            free(sample);
+            m->current_chapter++;
+            m->chapter_duration = m->sum_dur;
+        }
+    
         /* Video */
         /* Because we use the audio samplerate as the timescale,
            we have to use potentially variable durations so the video
@@ -253,6 +335,15 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
 
 static int MP4End( hb_mux_object_t * m )
 {
+    /* Write our final chapter marker */
+    if( m->job->chapter_markers )
+    {
+        struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
+    
+        MP4WriteSample(m->file, m->chapter_track, sample->sample, sample->length, sample->duration, 0, true);
+        free(sample);
+    }
+    
 #if 0
     hb_job_t * job = m->job;
     char filename[1024]; memset( filename, 0, 1024 );
index fb5d704acd8f472ff196f3f0419eac1f9fd70297..6ebbf6e0d6425430196081a12bd5984a138f417c 100644 (file)
@@ -244,6 +244,7 @@ static int SyncVideo( hb_work_object_t * w )
     hb_buffer_t * cur, * next, * sub = NULL;
     hb_job_t * job = pv->job;
     int64_t pts_expected;
+    int chap_break;
 
     if( pv->done )
     {
@@ -310,8 +311,11 @@ static int SyncVideo( hb_work_object_t * w )
             }
 
             /* Trash current picture */
+            /* Also, make sure we don't trash a chapter break */
+            chap_break = cur->new_chap;
             hb_buffer_close( &cur );
             pv->cur = cur = hb_fifo_get( job->fifo_raw );
+            cur->new_chap |= chap_break; // Don't stomp existing chapter breaks
 
             /* Calculate new offset */
             pv->pts_offset_old = pv->pts_offset;
@@ -358,8 +362,12 @@ static int SyncVideo( hb_work_object_t * w )
         {
             /* The current frame is too old but the next one matches,
                let's trash */
+            /* Also, make sure we don't trash a chapter break */
+            chap_break = cur->new_chap;
             hb_buffer_close( &cur );
             pv->cur = cur = hb_fifo_get( job->fifo_raw );
+            cur->new_chap |= chap_break; // Make sure we don't stomp the existing one.
+            
             continue;
         }
 
@@ -377,7 +385,7 @@ static int SyncVideo( hb_work_object_t * w )
             buf_tmp = cur;
             pv->cur = cur = hb_fifo_get( job->fifo_raw );
         }
-
+        
         /* Replace those MPEG-2 dates with our dates */
         buf_tmp->start = (uint64_t) pv->count_frames *
             pv->job->vrate_base / 300;
index a4dcb45147a2d668df5196b1b9ef7d809f56a48a..3e5ec4a78aa450ccc152b64388d2aaa2a07cfea5 100644 (file)
@@ -535,6 +535,14 @@ static void work_loop( void * _w )
 //             w->thread_sleep_interval = MAX(1, (w->thread_sleep_interval - 1));
 
         w->work( w, &buf_in, &buf_out );
+
+        // Propogate any chapter breaks for the worker
+        if( buf_in && buf_out && buf_in->new_chap )
+        {
+            printf("WORK: Copying Chapter Break\n");
+            buf_out->new_chap = 1;
+        }
+        
         if( buf_in )
         {
             hb_buffer_close( &buf_in );
diff --git a/macosx/ChapterTitles.h b/macosx/ChapterTitles.h
new file mode 100644 (file)
index 0000000..7f905e0
--- /dev/null
@@ -0,0 +1,28 @@
+/*  ChapterTitles.h $
+
+   This file is part of the HandBrake source code.
+   Homepage: <http://handbrake.m0k.org/>.
+   It may be used under the terms of the GNU General Public License. */
+
+#include <Cocoa/Cocoa.h>
+#include "hb.h"
+
+@interface ChapterTitles : NSObject {
+    hb_title_t *fTitle;
+}
+
+// Trigger a refresh of data
+- (void)resetWithTitle:(hb_title_t *)title;
+
+// Table View Delegates
+- (int)numberOfRowsInTableView:(NSTableView *)aTableView;
+
+- (id)tableView:(NSTableView *)aTableView
+      objectValueForTableColumn:(NSTableColumn *)aTableColumn
+      row:(int)rowIndex;
+      
+- (void)tableView:(NSTableView *)aTableView
+        setObjectValue:(id)anObject
+        forTableColumn:(NSTableColumn *)aTableColumn
+        row:(int)rowIndex;
+@end
diff --git a/macosx/ChapterTitles.m b/macosx/ChapterTitles.m
new file mode 100644 (file)
index 0000000..26f7191
--- /dev/null
@@ -0,0 +1,99 @@
+/*  ChapterTitles.m $
+
+   This file is part of the HandBrake source code.
+   Homepage: <http://handbrake.m0k.org/>.
+   It may be used under the terms of the GNU General Public License. */
+   
+#include "ChapterTitles.h"
+#include "hb.h"
+
+@implementation ChapterTitles
+- (id)init 
+{
+    self = [super init];
+    if( self != nil )
+    {
+        fTitle = NULL;
+    }
+    
+    return self;
+}
+
+- (void)resetWithTitle:(hb_title_t *)title
+{
+    int i;
+    NSString *chapterString;
+    int count = hb_list_count( title->list_chapter );
+
+    for( i = 0; i < count; i++ )
+    {
+        hb_chapter_t *chapter = hb_list_item( title->list_chapter, i );
+        
+        if( chapter != NULL && chapter->title[0] == '\0' )
+        {
+            chapterString = [NSString stringWithFormat:@"Chapter %2d",(i+1)];
+    
+            strncpy( chapter->title, [chapterString UTF8String], 1023);
+            chapter->title[1023] = '\0';
+        }
+    }
+    
+    fTitle = title;
+}
+
+- (int)numberOfRowsInTableView:(NSTableView *)aTableView
+{
+    if( fTitle == NULL )
+    {
+        return 0;
+    }
+    else
+    {
+        return hb_list_count( fTitle->list_chapter );
+    }
+}
+
+- (void)tableView:(NSTableView *)aTableView
+        setObjectValue:(id)anObject
+        forTableColumn:(NSTableColumn *)aTableColumn
+        row:(int)rowIndex
+{
+    if(aTableColumn != nil && [[aTableColumn identifier] intValue] == 2)
+    {
+        hb_chapter_t *chapter = hb_list_item( fTitle->list_chapter, rowIndex );
+        
+        if( chapter != NULL )
+        {
+            strncpy( chapter->title, [anObject UTF8String], 1023);
+            chapter->title[1023] = '\0';
+        }
+    }
+}
+
+- (id)tableView:(NSTableView *)aTableView
+      objectValueForTableColumn:(NSTableColumn *)aTableColumn
+      row:(int)rowIndex
+{
+    NSString *cellEntry;
+
+    if([[aTableColumn identifier] intValue] == 1)
+    {
+        cellEntry = [NSString stringWithFormat:@"%d",rowIndex+1];
+    }
+    else
+    {
+        hb_chapter_t *chapter = hb_list_item( fTitle->list_chapter, rowIndex );
+        
+        if( chapter != NULL )
+        {
+            cellEntry = [NSString stringWithUTF8String:chapter->title];
+        }
+        else
+        {
+            cellEntry = @"__DATA ERROR__";
+        }
+    }
+    
+    return cellEntry;
+}
+@end
index a866d58c797fdad047a9e7e21e3d77d77e320f16..ce97509472fb3a652402a2caf6076195f70ac926 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "hb.h"
 
+#include "ChapterTitles.h"
 #include "ScanController.h"
 #include "PictureController.h"
 #include "QueueController.h"
@@ -52,7 +53,6 @@
     IBOutlet NSTextField         * fDstFile1Field;
     IBOutlet NSTextField         * fDstFile2Field;
     IBOutlet NSButton            * fDstBrowseButton;
-       IBOutlet NSButton            * fCreateChapterMarkers;
 
     /* Video box */
     IBOutlet NSTextField         * fVidRateField;
     IBOutlet NSPopUpButton       * fAudRatePopUp;
     IBOutlet NSTextField         * fAudBitrateField;
     IBOutlet NSPopUpButton       * fAudBitratePopUp;
+    
+    /* Chapters box */
+    IBOutlet NSButton            * fCreateChapterMarkers;
+    IBOutlet NSTableView         * fChapterTable;
+             ChapterTitles       * fChapterTitlesDelegate;
 
     /* Bottom */
     IBOutlet NSButton            * fPictureButton;
index 9e614fa6d92c9349bfeab43339d08ea782951e09..9290cb67a8825411875801d5f66dc7e75a420df6 100644 (file)
@@ -49,6 +49,8 @@ static int FormatSettings[3][4] =
     [fPictureController SetHandle: fHandle];
     [fQueueController   SetHandle: fHandle];
        
+    fChapterTitlesDelegate = [[ChapterTitles alloc] init];
+    [fChapterTable setDataSource:fChapterTitlesDelegate];
 
      /* Call UpdateUI every 2/10 sec */
     [[NSRunLoop currentRunLoop] addTimer: [NSTimer
@@ -738,13 +740,12 @@ static int FormatSettings[3][4] =
     hb_title_t * title = (hb_title_t *) hb_list_item( list,
             [fSrcTitlePopUp indexOfSelectedItem] );
     hb_job_t * job = title->job;
+    int i;
 
     /* Chapter selection */
     job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
     job->chapter_end   = [fSrcChapterEndPopUp   indexOfSelectedItem] + 1;
        
-
-
     /* Format and codecs */
     int format = [fDstFormatPopUp indexOfSelectedItem];
     int codecs = [fDstCodecsPopUp indexOfSelectedItem];
@@ -755,7 +756,7 @@ static int FormatSettings[3][4] =
        mpeg4 and the checkbox being checked */
        if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [fCreateChapterMarkers state] == NSOnState)
        {
-       job->chapter_markers = 1;
+        job->chapter_markers = 1;
        }
        else
        {
@@ -1136,7 +1137,6 @@ static int FormatSettings[3][4] =
        //[self EncoderPopUpChanged: NULL];
        /* END Get and set the initial pic size for display */ 
 
-
     /* Update subtitle popups */
     hb_subtitle_t * subtitle;
     [fSubPopUp removeAllItems];
@@ -1151,6 +1151,10 @@ static int FormatSettings[3][4] =
             subtitle->lang] action: NULL keyEquivalent: @""];
     }
     [fSubPopUp selectItemAtIndex: 0];
+    
+    /* Update chapter table */
+    [fChapterTitlesDelegate resetWithTitle:title];
+    [fChapterTable reloadData];
 
     /* Update audio popups */
     [self AddAllAudioTracksToPopUp: fAudLang1PopUp];
index 64874fea77b6ca373454b92903d8fb61c2b938e2..f10f110dcad8cf8258fa93edc476766ab141433b 100644 (file)
@@ -52,6 +52,7 @@
                 fAudTrack1MixPopUp = NSPopUpButton; 
                 fAudTrack2MixLabel = NSTextField; 
                 fAudTrack2MixPopUp = NSPopUpButton; 
+                fChapterTable = NSTableView; 
                 fCreateChapterMarkers = NSButton; 
                 fDisplayX264Options = NSTextField; 
                 fDstBrowseButton = NSButton; 
index 54ae4c1036c82adcace8e4a1facb20fc0990c7a3..b845f1d90b4815d7ee55fcfd0b4bc41d46d7078c 100644 (file)
@@ -3,7 +3,7 @@
 <plist version="1.0">
 <dict>
        <key>IBDocumentLocation</key>
-       <string>39 277 630 601 0 0 1440 878 </string>
+       <string>1484 151 630 601 1440 0 1280 1024 </string>
        <key>IBEditorPositions</key>
        <dict>
                <key>1843</key>
        <array>
                <integer>1477</integer>
        </array>
-       <key>IBOpenObjects</key>
-       <array>
-               <integer>21</integer>
-               <integer>1438</integer>
-       </array>
        <key>IBSystem Version</key>
        <string>8P2137</string>
        <key>IBUserGuides</key>
index c52465c1bc55fca0de7fb7ecc2cc3346a7ba613c..560c265965574fb3ff47e5179ace4d2b42db69d4 100644 (file)
Binary files a/macosx/English.lproj/MainMenu.nib/keyedobjects.nib and b/macosx/English.lproj/MainMenu.nib/keyedobjects.nib differ
index 0f021cc1def0f6b9c54b7fa2dcb6a460a19d0948..6068484eadc43ee4b50ae83c200de114616d74ce 100644 (file)
@@ -13,11 +13,13 @@ CXXFLAGS += -I../libhb
 LIBS2 = ../libhb/libhb.a $(LIBS:%=../contrib/lib/lib%.a)
 LDFLAGS += $(LIBS2)
 
-../HandBrakeCLI: test.c $(LIBS2)
+../HandBrakeCLI: test.c parsecsv.c $(LIBS2)
        @CMD="$(CC) $(CFLAGS) -o test.o -c test.c"; $$CMD || \
        ( echo "Compile line for $@ was:"; echo $$CMD; false )
+        @CMD="$(CC) $(CFLAGS) -o parsecsv.o -c parsecsv.c"; $$CMD || \
+        ( echo "Compile line for $@ was:"; echo $$CMD; false )
        @echo "Link HandBrakeCLI"
-       @CMD="g++ $(CXXFLAGS) -o ../HandBrakeCLI test.o $(LDFLAGS) -lz -lpthread"; $$CMD || \
+       @CMD="g++ $(CXXFLAGS) -o ../HandBrakeCLI test.o parsecsv.o $(LDFLAGS) -lz -lpthread"; $$CMD || \
        ( echo "Compile line for $@ was:"; echo $$CMD; false )
         @CMD="rm -rf ../plugins ; mkdir ../plugins ; cp ../contrib/lib/libquicktime/* ../plugins"; $$CMD
 
diff --git a/test/parsecsv.c b/test/parsecsv.c
new file mode 100644 (file)
index 0000000..f0a8a1a
--- /dev/null
@@ -0,0 +1,214 @@
+/* $Id: parsecsv.c $
+
+   This file is part of the HandBrake source code.
+   Homepage: <http://handbrake.m0k.org/>.
+   It may be used under the terms of the GNU General Public License. */
+
+#include <fcntl.h>
+#include "hb.h"
+#include "parsecsv.h"
+
+/* Internal declarations */
+#define is_newline(_x)      ( (_x) == 13 || \
+                              (_x) == 11 || \
+                              (_x) == 10 )
+                              
+#define is_white(_x)        ( (_x) == '\t' || \
+                              (_x) == ' '  || \
+                              is_newline(_x) ) 
+                              
+#define is_sep(_x)          ( (_x) == ',' )
+
+#define is_esc(_x)          ( (_x) == '\\' )
+
+#define CSV_CHAR_ERROR      0x8000
+#define CSV_CHAR_EOF        0x4000
+#define CSV_CHAR_ROWSEP     0x2000
+#define CSV_CHAR_COLSEP     0x1000
+
+#define CSV_PARSE_NORMAL    0x0000
+#define CSV_PARSE_SEEK      0x0001
+#define CSV_PARSE_ESC       0x0002
+
+static uint16_t hb_parse_character( hb_csv_file_t * file );
+static void hb_trim_end( char *text );
+
+/* Open a CSV File */
+hb_csv_file_t *hb_open_csv_file( const char *filepath )
+{
+    hb_csv_file_t *file = NULL;
+    FILE * fileref;
+    
+    if( filepath == NULL )
+    {
+        return file;
+    }
+    
+    fileref = fopen( filepath, "r" );
+    if( fileref == NULL )
+    {
+        return file;
+    }
+    
+    file = malloc( sizeof( hb_csv_file_t ) );
+    file->fileref       = fileref;
+    file->eof           = 0;
+    file->parse_state   = CSV_PARSE_SEEK;
+    file->curr_col      = 0;
+    file->curr_row      = 0;
+    return file;
+}
+
+void hb_close_csv_file( hb_csv_file_t *file ) 
+{
+    if( file == NULL )
+    {
+        return;
+    }
+    
+    fclose( file->fileref );
+    free( file );
+}
+
+/* Parse CSV Cells */
+hb_csv_cell_t *hb_read_next_cell( hb_csv_file_t *file )
+{
+    hb_csv_cell_t *cell = NULL;
+    uint16_t c;
+    int index;
+
+    if( file == NULL  )
+    {
+        return cell;
+    }
+    
+    if( file->eof )
+    {
+        return cell;
+    }
+    
+    cell = malloc( sizeof( hb_csv_cell_t ) );
+    cell->cell_row = file->curr_row;
+    cell->cell_col = file->curr_col;
+    index = 0;
+    while( CSV_CHAR_EOF != (c = hb_parse_character( file ) ) )
+    {
+        if( c == CSV_CHAR_ROWSEP )
+        {
+            file->curr_row++;
+            file->curr_col = 0;
+            break;
+        }
+        else if( c == CSV_CHAR_COLSEP )
+        {
+            file->curr_col++;
+            break;
+        }
+        else
+        {
+            if( index < 1023 )
+            {
+                cell->cell_text[index] = (char)c;
+                index++;
+            }
+        }
+    }
+    
+    if( c == CSV_CHAR_EOF )
+    {
+        file->eof = 1;
+    }
+    
+    /* Terminate the cell text */
+    cell->cell_text[index] = '\0';
+    hb_trim_end( cell->cell_text );
+    return cell;
+}
+
+void hb_dispose_cell( hb_csv_cell_t *cell )
+{
+    if( cell == NULL )
+    {
+        return;
+    }
+    
+    free( cell );
+}
+
+/* Raw parsing */
+static uint16_t hb_parse_character( hb_csv_file_t * file )
+{
+    int byte;
+    uint16_t c;
+    int read_result;
+    int need_char = 1;
+    
+    if( file == NULL )
+    {
+        return CSV_CHAR_ERROR;
+    }
+    
+    while( need_char )
+    {
+        byte = fgetc( file->fileref );
+        if( feof( file->fileref ) )
+        {
+            return CSV_CHAR_EOF;
+        }
+        if( ferror( file->fileref ) )
+        {
+            return CSV_CHAR_ERROR;
+        }
+        
+        if( file->parse_state == CSV_PARSE_SEEK && is_white(byte) )
+        {
+            continue;
+        }
+        else if( file->parse_state != CSV_PARSE_ESC && is_esc(byte) )
+        {
+            file->parse_state = CSV_PARSE_ESC;
+            continue;
+        }
+        else if( file->parse_state != CSV_PARSE_ESC && is_sep(byte) )
+        {
+            file->parse_state = CSV_PARSE_SEEK;
+            need_char = 0;
+            c = CSV_CHAR_COLSEP;
+        }
+        else if( file->parse_state == CSV_PARSE_ESC )
+        {
+            file->parse_state = CSV_PARSE_NORMAL;
+            need_char = 0;
+            c = (uint16_t)byte;
+        }
+        else if( is_newline(byte) )
+        {
+            file->parse_state = CSV_PARSE_SEEK;
+            need_char = 0;
+            c = CSV_CHAR_ROWSEP;
+        }
+        else
+        {
+            file->parse_state = CSV_PARSE_NORMAL;
+            need_char = 0;
+            c = (uint16_t)byte;
+        }
+    }
+    
+    return c;
+}
+
+static void hb_trim_end( char *text )
+{
+    if( text == NULL )
+    {
+        return;
+    }
+
+    int i = strlen(text) - 1;
+    
+    for( i = strlen(text) - 1; i >= 0 && is_white(text[i]) ; i-- )
+    {
+        text[i] = '\0';
+    }
+}
\ No newline at end of file
diff --git a/test/parsecsv.h b/test/parsecsv.h
new file mode 100644 (file)
index 0000000..6247ddb
--- /dev/null
@@ -0,0 +1,36 @@
+/* $Id: parsecsv.h $
+
+   This file is part of the HandBrake source code.
+   Homepage: <http://handbrake.m0k.org/>.
+   It may be used under the terms of the GNU General Public License. */
+
+/*
+    A very simple CSV file parser.
+ */
+
+typedef struct hb_csv_file_s hb_csv_file_t;
+typedef struct hb_csv_cell_s hb_csv_cell_t;
+
+struct hb_csv_file_s
+{
+    FILE  * fileref;
+    int     eof;
+    int     parse_state;
+    int     curr_row;
+    int     curr_col;
+};
+
+struct hb_csv_cell_s
+{
+    char    cell_text[1024];
+    int     cell_row;
+    int     cell_col;
+};
+
+/* Open a CSV File */
+hb_csv_file_t *hb_open_csv_file( const char *filepath );
+void hb_close_csv_file( hb_csv_file_t *file );
+
+/* Parse CSV Cells */
+hb_csv_cell_t *hb_read_next_cell( hb_csv_file_t *file );
+void hb_dispose_cell( hb_csv_cell_t *cell );
\ No newline at end of file
index 903b330b6a1b8e1ed46aad09b8e61980300f59c0..967cd9c11ce5cb91ec63a62230ec9c4bf1b8d5b8 100644 (file)
@@ -11,6 +11,7 @@
 #include <unistd.h>
 
 #include "hb.h"
+#include "parsecsv.h"
 
 /* Options */
 static int    debug       = HB_DEBUG_NONE;
@@ -44,6 +45,7 @@ static int    pixelratio  = 0;
 static int    chapter_start = 0;
 static int    chapter_end   = 0;
 static int    chapter_markers = 0;
+static char * marker_file   = NULL;
 static int       crf                   = 0;
 static char      *x264opts             = NULL;
 static char      *x264opts2    = NULL;
@@ -314,6 +316,53 @@ static int HandleEvents( hb_handle_t * h )
                        if ( chapter_markers )
                        {
                                job->chapter_markers = chapter_markers;
+
+                if( marker_file != NULL )
+                {
+                    hb_csv_file_t * file = hb_open_csv_file( marker_file );
+                    hb_csv_cell_t * cell;
+                    int row = 0;
+                    int chapter = 0;
+                    
+                    fprintf( stderr, "Reading chapter markers from file %s\n", marker_file );
+                    
+                    if( file == NULL )
+                    {
+                         fprintf( stderr, "Cannot open chapter marker file, using defaults\n" );
+                    }
+                    else
+                    {
+                        /* Parse the cells */
+                        while( NULL != ( cell = hb_read_next_cell( file ) ) )
+                        {                            
+                            /* We have a chapter number */
+                            if( cell->cell_col == 0 )
+                            {
+                                row = cell->cell_row;
+                                chapter = atoi( cell->cell_text );
+                            }
+                             
+                            /* We have a chapter name */
+                            if( cell->cell_col == 1 && row == cell->cell_row )
+                            {
+                                /* If we have a valid chapter, copy the string an terminate it */
+                                if( chapter >= job->chapter_start && chapter <= job->chapter_end )
+                                {
+                                    hb_chapter_t * chapter_s;
+                                    
+                                    chapter_s = hb_list_item( job->title->list_chapter, chapter - 1);
+                                    strncpy(chapter_s->title, cell->cell_text, 1023);
+                                    chapter_s->title[1023] = '\0';
+                                }
+                            }                               
+                        
+                                                           
+                            hb_dispose_cell( cell );
+                        }
+                        
+                        hb_close_csv_file( file );
+                    }
+                }
                        }
 
             if( crop[0] >= 0 && crop[1] >= 0 &&
@@ -650,7 +699,7 @@ static int ParseOptions( int argc, char ** argv )
 
             { "title",       required_argument, NULL,    't' },
             { "chapters",    required_argument, NULL,    'c' },
-            { "markers",     no_argument,       NULL,    'm' },
+            { "markers",     optional_argument, NULL,    'm' },
             { "audio",       required_argument, NULL,    'a' },
             { "mixdown",     required_argument, NULL,    '6' },
             { "subtitle",    required_argument, NULL,    's' },
@@ -740,6 +789,10 @@ static int ParseOptions( int argc, char ** argv )
                 break;
             }
             case 'm':
+                if( optarg != NULL )
+                {
+                    marker_file = strdup( optarg );
+                }
                 chapter_markers = 1;
                 break;
             case 'a':