summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libhb/decmpeg2.c33
-rw-r--r--libhb/demuxmpeg.c6
-rw-r--r--libhb/dvd.c92
-rw-r--r--libhb/encxvid.c1
-rw-r--r--libhb/fifo.c2
-rw-r--r--libhb/internal.h2
-rw-r--r--libhb/muxmp4.c147
-rw-r--r--libhb/sync.c10
-rw-r--r--libhb/work.c8
-rw-r--r--macosx/ChapterTitles.h28
-rw-r--r--macosx/ChapterTitles.m99
-rw-r--r--macosx/Controller.h7
-rw-r--r--macosx/Controller.mm12
-rw-r--r--macosx/English.lproj/MainMenu.nib/classes.nib1
-rw-r--r--macosx/English.lproj/MainMenu.nib/info.nib7
-rw-r--r--macosx/English.lproj/MainMenu.nib/keyedobjects.nibbin99793 -> 102083 bytes
-rw-r--r--test/Makefile6
-rw-r--r--test/parsecsv.c214
-rw-r--r--test/parsecsv.h36
-rw-r--r--test/test.c55
20 files changed, 714 insertions, 52 deletions
diff --git a/libhb/decmpeg2.c b/libhb/decmpeg2.c
index 214444a05..4fb6ad76b 100644
--- a/libhb/decmpeg2.c
+++ b/libhb/decmpeg2.c
@@ -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;
diff --git a/libhb/demuxmpeg.c b/libhb/demuxmpeg.c
index 721478e80..5aefc3743 100644
--- a/libhb/demuxmpeg.c
+++ b/libhb/demuxmpeg.c
@@ -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 );
diff --git a/libhb/dvd.c b/libhb/dvd.c
index 9b4ba3fe7..b26c55995 100644
--- a/libhb/dvd.c
+++ b/libhb/dvd.c
@@ -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;
@@ -760,6 +776,70 @@ int hb_dvd_chapter( hb_dvd_t * d )
}
/***********************************************************************
+ * 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
***********************************************************************
* Closes and frees everything
diff --git a/libhb/encxvid.c b/libhb/encxvid.c
index fbaa5a135..7430aca0f 100644
--- a/libhb/encxvid.c
+++ b/libhb/encxvid.c
@@ -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 ) );
diff --git a/libhb/fifo.c b/libhb/fifo.c
index 323406538..fe03c47f9 100644
--- a/libhb/fifo.c
+++ b/libhb/fifo.c
@@ -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;
}
diff --git a/libhb/internal.h b/libhb/internal.h
index efe721e12..f950205d5 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -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 ** );
/***********************************************************************
diff --git a/libhb/muxmp4.c b/libhb/muxmp4.c
index 7aae352cd..e2de1ce86 100644
--- a/libhb/muxmp4.c
+++ b/libhb/muxmp4.c
@@ -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 );
diff --git a/libhb/sync.c b/libhb/sync.c
index fb5d704ac..6ebbf6e0d 100644
--- a/libhb/sync.c
+++ b/libhb/sync.c
@@ -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;
diff --git a/libhb/work.c b/libhb/work.c
index a4dcb4514..3e5ec4a78 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -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
index 000000000..7f905e0d5
--- /dev/null
+++ b/macosx/ChapterTitles.h
@@ -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
index 000000000..26f71913d
--- /dev/null
+++ b/macosx/ChapterTitles.m
@@ -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
diff --git a/macosx/Controller.h b/macosx/Controller.h
index a866d58c7..ce9750947 100644
--- a/macosx/Controller.h
+++ b/macosx/Controller.h
@@ -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;
@@ -118,6 +118,11 @@
IBOutlet NSPopUpButton * fAudRatePopUp;
IBOutlet NSTextField * fAudBitrateField;
IBOutlet NSPopUpButton * fAudBitratePopUp;
+
+ /* Chapters box */
+ IBOutlet NSButton * fCreateChapterMarkers;
+ IBOutlet NSTableView * fChapterTable;
+ ChapterTitles * fChapterTitlesDelegate;
/* Bottom */
IBOutlet NSButton * fPictureButton;
diff --git a/macosx/Controller.mm b/macosx/Controller.mm
index 9e614fa6d..9290cb67a 100644
--- a/macosx/Controller.mm
+++ b/macosx/Controller.mm
@@ -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];
diff --git a/macosx/English.lproj/MainMenu.nib/classes.nib b/macosx/English.lproj/MainMenu.nib/classes.nib
index 64874fea7..f10f110dc 100644
--- a/macosx/English.lproj/MainMenu.nib/classes.nib
+++ b/macosx/English.lproj/MainMenu.nib/classes.nib
@@ -52,6 +52,7 @@
fAudTrack1MixPopUp = NSPopUpButton;
fAudTrack2MixLabel = NSTextField;
fAudTrack2MixPopUp = NSPopUpButton;
+ fChapterTable = NSTableView;
fCreateChapterMarkers = NSButton;
fDisplayX264Options = NSTextField;
fDstBrowseButton = NSButton;
diff --git a/macosx/English.lproj/MainMenu.nib/info.nib b/macosx/English.lproj/MainMenu.nib/info.nib
index 54ae4c103..b845f1d90 100644
--- a/macosx/English.lproj/MainMenu.nib/info.nib
+++ b/macosx/English.lproj/MainMenu.nib/info.nib
@@ -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>
@@ -17,11 +17,6 @@
<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>
diff --git a/macosx/English.lproj/MainMenu.nib/keyedobjects.nib b/macosx/English.lproj/MainMenu.nib/keyedobjects.nib
index c52465c1b..560c26596 100644
--- a/macosx/English.lproj/MainMenu.nib/keyedobjects.nib
+++ b/macosx/English.lproj/MainMenu.nib/keyedobjects.nib
Binary files differ
diff --git a/test/Makefile b/test/Makefile
index 0f021cc1d..6068484ea 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -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
index 000000000..f0a8a1a18
--- /dev/null
+++ b/test/parsecsv.c
@@ -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
index 000000000..6247ddb44
--- /dev/null
+++ b/test/parsecsv.h
@@ -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
diff --git a/test/test.c b/test/test.c
index 903b330b6..967cd9c11 100644
--- a/test/test.c
+++ b/test/test.c
@@ -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':