From 5a5de738a8ceb2cedb0ffe9acf82566754152e2b Mon Sep 17 00:00:00 2001 From: Josh Coalson Date: Thu, 1 Aug 2002 06:37:11 +0000 Subject: [PATCH] new functions for seektable templates, point sorting --- include/FLAC/format.h | 3 + src/libFLAC/format.c | 48 +++++++++++++++- src/libFLAC/metadata_object.c | 110 ++++++++++++++++++++++++++++++++----- src/test_libFLAC/metadata_object.c | 103 +++++++++++++++++++++++++++++++++- 4 files changed, 247 insertions(+), 17 deletions(-) diff --git a/include/FLAC/format.h b/include/FLAC/format.h index e30a47a..441ba0b 100644 --- a/include/FLAC/format.h +++ b/include/FLAC/format.h @@ -626,6 +626,9 @@ FLAC__bool FLAC__format_sample_rate_is_valid(unsigned sample_rate); */ FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *object); +/* @@@@ document */ +unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table); + /* \} */ #ifdef __cplusplus diff --git a/src/libFLAC/format.c b/src/libFLAC/format.c index d3c94c3..9c3a83b 100644 --- a/src/libFLAC/format.c +++ b/src/libFLAC/format.c @@ -18,6 +18,7 @@ */ #include +#include /* for qsort() */ #include "FLAC/assert.h" #include "FLAC/format.h" @@ -138,7 +139,10 @@ FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable for(i = 0; i < seek_table->num_points; i++) { if(got_prev) { - if(seek_table->points[i].sample_number <= prev_sample_number) + if( + seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && + seek_table->points[i].sample_number <= prev_sample_number + ) return false; } prev_sample_number = seek_table->points[i].sample_number; @@ -147,3 +151,45 @@ FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable return true; } + +/* used as the sort predicate for qsort() */ +static int seekpoint_compare_(const FLAC__StreamMetadata_SeekPoint *l, const FLAC__StreamMetadata_SeekPoint *r) +{ + /* we don't just 'return l->sample_number - r->sample_number' since the result (FLAC__int64) might overflow an 'int' */ + if(l->sample_number == r->sample_number) + return 0; + else if(l->sample_number < r->sample_number) + return -1; + else + return 1; +} + +unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table) +{ + unsigned i, j; + FLAC__bool first; + + /* sort the seekpoints */ + qsort(seek_table->points, seek_table->num_points, sizeof(FLAC__StreamMetadata_SeekPoint), (int (*)(const void *, const void *))seekpoint_compare_); + + /* uniquify the seekpoints */ + first = true; + for(i = j = 0; i < seek_table->num_points; i++) { + if(seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) { + if(!first) { + if(seek_table->points[i].sample_number == seek_table->points[j-1].sample_number) + continue; + } + } + first = false; + seek_table->points[j++] = seek_table->points[i]; + } + + for(; j < seek_table->num_points; j++) { + seek_table->points[j].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + seek_table->points[j].stream_offset = 0; + seek_table->points[j].frame_samples = 0; + } + + return j; +} diff --git a/src/libFLAC/metadata_object.c b/src/libFLAC/metadata_object.c index 72fa914..0f822b0 100644 --- a/src/libFLAC/metadata_object.c +++ b/src/libFLAC/metadata_object.c @@ -202,10 +202,6 @@ static FLAC__bool vorbiscomment_set_entry_(FLAC__StreamMetadata *object, FLAC__S * ***************************************************************************/ -/*@@@move -will return pointer to new empty object of type 'type', or 0 if malloc failed -type is valid type -*/ FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type) { FLAC__StreamMetadata *object = malloc(sizeof(FLAC__StreamMetadata)); @@ -238,10 +234,6 @@ FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type) return object; } -/*@@@move -return a pointer to a copy of 'object', or 0 if any malloc failed. does a deep copy. user gets ownership of object. - FLAC__ASSERT(0 != object); -*/ FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object) { FLAC__StreamMetadata *to; @@ -331,9 +323,6 @@ void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object) } } -/*@@@move -frees 'object'. does a deep delete. -*/ void FLAC__metadata_object_delete(FLAC__StreamMetadata *object) { FLAC__metadata_object_delete_data(object); @@ -437,13 +426,13 @@ FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, co if(block1->type != block2->type) { return false; - } + } if(block1->is_last != block2->is_last) { return false; - } + } if(block1->length != block2->length) { return false; - } + } switch(block1->type) { case FLAC__METADATA_TYPE_STREAMINFO: return compare_block_data_streaminfo_(&block1->data.stream_info, &block2->data.stream_info); @@ -581,6 +570,99 @@ FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata * return FLAC__format_seektable_is_legal(&object->data.seek_table); } +FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + if(num > 0) + /* WATCHOUT: we rely on the fact that growing the array adds PLACEHOLDERS at the end */ + return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points + num); + else + return true; +} + +FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number) +{ + FLAC__StreamMetadata_SeekTable *seek_table; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + seek_table = &object->data.seek_table; + + if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + 1)) + return false; + + seek_table->points[seek_table->num_points - 1].sample_number = sample_number; + seek_table->points[seek_table->num_points - 1].stream_offset = 0; + seek_table->points[seek_table->num_points - 1].frame_samples = 0; + + return true; +} + +FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(0 != sample_numbers || num == 0); + + if(num > 0) { + FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table; + unsigned i, j; + + i = seek_table->num_points; + + if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num)) + return false; + + for(j = 0; j < num; i++, j++) { + seek_table->points[i].sample_number = sample_numbers[j]; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + } + + return true; +} + +FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(total_samples > 0); + + if(num > 0) { + FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table; + unsigned i, j; + + i = seek_table->num_points; + + if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num)) + return false; + + for(j = 0; j < num; i++, j++) { + seek_table->points[i].sample_number = total_samples * (FLAC__uint64)j / (FLAC__uint64)num; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + } + + return true; +} + +FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact) +{ + unsigned unique; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + unique = FLAC__format_seektable_sort(&object->data.seek_table); + + return !compact || FLAC__metadata_object_seektable_resize_points(object, unique); +} + FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) { return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.vendor_string, &entry, copy); diff --git a/src/test_libFLAC/metadata_object.c b/src/test_libFLAC/metadata_object.c index 7559f59..36468f9 100644 --- a/src/test_libFLAC/metadata_object.c +++ b/src/test_libFLAC/metadata_object.c @@ -186,7 +186,7 @@ static void vc_delete_(FLAC__StreamMetadata *block, unsigned pos) FLAC__bool test_metadata_object() { FLAC__StreamMetadata *block, *blockcopy, *vorbiscomment; - FLAC__StreamMetadata_SeekPoint seekpoint_array[4]; + FLAC__StreamMetadata_SeekPoint seekpoint_array[8]; FLAC__StreamMetadata_VorbisComment_Entry entry; unsigned i, expected_length, seekpoints; static FLAC__byte dummydata[4] = { 'a', 'b', 'c', 'd' }; @@ -349,7 +349,7 @@ FLAC__bool test_metadata_object() printf("testing SEEKTABLE\n"); - for(i = 0; i < 4; i++) { + for(i = 0; i < sizeof(seekpoint_array) / sizeof(FLAC__StreamMetadata_SeekPoint); i++) { seekpoint_array[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; seekpoint_array[i].stream_offset = 0; seekpoint_array[i].frame_samples = 0; @@ -499,6 +499,105 @@ FLAC__bool test_metadata_object() if(!check_seektable_(block, seekpoints, seekpoint_array)) return false; + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(block); + printf("OK\n"); + + /* seektable template functions */ + + for(i = 0; i < sizeof(seekpoint_array) / sizeof(FLAC__StreamMetadata_SeekPoint); i++) { + seekpoint_array[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + seekpoint_array[i].stream_offset = 0; + seekpoint_array[i].frame_samples = 0; + } + + seekpoints = 0; + printf("testing FLAC__metadata_object_new()... "); + block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_SEEKTABLE); + if(0 == block) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!check_seektable_(block, seekpoints, 0)) + return false; + + seekpoints += 2; + printf("testing FLAC__metadata_object_seekpoint_template_append_placeholders()... "); + if(!FLAC__metadata_object_seektable_template_append_placeholders(block, 2)) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + seekpoint_array[seekpoints++].sample_number = 7; + printf("testing FLAC__metadata_object_seekpoint_template_append_point()... "); + if(!FLAC__metadata_object_seektable_template_append_point(block, 7)) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + { + FLAC__uint64 nums[2] = { 3, 7 }; + seekpoint_array[seekpoints++].sample_number = nums[0]; + seekpoint_array[seekpoints++].sample_number = nums[1]; + printf("testing FLAC__metadata_object_seekpoint_template_append_points()... "); + if(!FLAC__metadata_object_seektable_template_append_points(block, nums, sizeof(nums)/sizeof(FLAC__uint64))) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + } + + seekpoint_array[seekpoints++].sample_number = 0; + seekpoint_array[seekpoints++].sample_number = 10; + seekpoint_array[seekpoints++].sample_number = 20; + printf("testing FLAC__metadata_object_seekpoint_template_append_spaced_points()... "); + if(!FLAC__metadata_object_seektable_template_append_spaced_points(block, 3, 30)) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + seekpoints--; + seekpoint_array[0].sample_number = 0; + seekpoint_array[1].sample_number = 3; + seekpoint_array[2].sample_number = 7; + seekpoint_array[3].sample_number = 10; + seekpoint_array[4].sample_number = 20; + seekpoint_array[5].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + seekpoint_array[6].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + printf("testing FLAC__metadata_object_seekpoint_template_sort(compact=true)... "); + if(!FLAC__metadata_object_seektable_template_sort(block, /*compact=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!FLAC__metadata_object_seektable_is_legal(block)) { + printf("FAILED, seek table is illegal\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + printf("testing FLAC__metadata_object_seekpoint_template_sort(compact=false)... "); + if(!FLAC__metadata_object_seektable_template_sort(block, /*compact=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!FLAC__metadata_object_seektable_is_legal(block)) { + printf("FAILED, seek table is illegal\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(block); + printf("OK\n"); printf("testing VORBIS_COMMENT\n"); -- 2.7.4