Skip to content

Commit

Permalink
Add capability to libFLAC to seek in chained streams
Browse files Browse the repository at this point in the history
  • Loading branch information
ktmf01 committed Aug 28, 2024
1 parent 821245f commit e864271
Show file tree
Hide file tree
Showing 9 changed files with 426 additions and 87 deletions.
26 changes: 16 additions & 10 deletions include/FLAC/stream_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -806,10 +806,8 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecod
* by using FLAC__STREAM_DECODER_READ_STATUS_END_OF_LINK
* appropriately.
*
* Note that seeking is not yet supported when this flag is set to
* true. Also, when this flag is set to true, the serial number set
* with FLAC__stream_decoder_set_ogg_serial_number is ignored when
* decoding chained ogg streams.
* Note that when this flag is set to true, the serial number set
* with FLAC__stream_decoder_set_ogg_serial_number is ignored.
*
* \note
* This function has no effect with native FLAC decoding.
Expand Down Expand Up @@ -856,7 +854,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *d
* \code decoder != NULL \endcode
* \a type is valid
* \retval FLAC__bool
* \c false if the decoder is already initialized, else \c true.
* \c false if type is invalid, else \c true.
*/
FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type);

Expand All @@ -871,7 +869,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecode
* \code decoder != NULL \endcode
* \code id != NULL \endcode
* \retval FLAC__bool
* \c false if the decoder is already initialized, else \c true.
* \c false when memory allocation fails, else \c true.
*/
FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]);

Expand All @@ -883,7 +881,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__
* \assert
* \code decoder != NULL \endcode
* \retval FLAC__bool
* \c false if the decoder is already initialized, else \c true.
* \c always \c true.
*/
FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder);

Expand All @@ -897,7 +895,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDe
* \code decoder != NULL \endcode
* \a type is valid
* \retval FLAC__bool
* \c false if the decoder is already initialized, else \c true.
* \c false if type is invalid, else \c true.
*/
FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type);

Expand All @@ -912,7 +910,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder
* \code decoder != NULL \endcode
* \code id != NULL \endcode
* \retval FLAC__bool
* \c false if the decoder is already initialized, else \c true.
* \c false if memory allocation fails, else \c true.
*/
FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]);

Expand All @@ -924,7 +922,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__S
* \assert
* \code decoder != NULL \endcode
* \retval FLAC__bool
* \c false if the decoder is already initialized, else \c true.
* \c always \c true.
*/
FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder);

Expand Down Expand Up @@ -1661,6 +1659,14 @@ FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder *
* with FLAC__stream_decoder_flush() or reset with
* FLAC__stream_decoder_reset() before decoding can continue.
*
* When seeking in a chained stream with decoding of such streams
* enabled with FLAC__stream_decoder_set_decode_chained_stream(),
* this function seeks in the whole stream, over all links. When
* a seek to another link is performed, the decoder will also
* return metadata blocks of that link. If this is not desired,
* use FLAC__stream_decoder_set_metadata_ignore_all() before
* seeking.
*
* \param decoder A decoder instance.
* \param sample The target sample number to seek to.
* \assert
Expand Down
32 changes: 23 additions & 9 deletions oss-fuzz/seek.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ int write_abort_check_counter = -1;
#define FPRINTF_DEBUG_ONLY(...)
#endif

#define CONFIG_LENGTH 2
#define CONFIG_LENGTH 3

static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
{
Expand Down Expand Up @@ -70,8 +70,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
uint8_t command_length;
FLAC__bool init_bools[16], ogg;

if(size > 2 && data[1] < 128) /* Use MSB as on/off */
alloc_check_threshold = data[1];
if(size < 1)
return 1;

if(data[0] < 128) /* Use MSB as on/off */
alloc_check_threshold = data[0];
else
alloc_check_threshold = INT32_MAX;
alloc_check_counter = 0;
Expand All @@ -83,17 +86,17 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
return 1;
}

/* Use first byte for configuration, leave at least one byte of input */
/* Use first CONFIG_LENGTH bytes for configuration, leave at least one byte of input */
if(size < 1 + CONFIG_LENGTH){
FLAC__stream_decoder_delete(decoder);
return 0;
}

/* First 4 bits for configuration bools, next 4 for length of command section */
for(int i = 0; i < 4; i++)
init_bools[i] = data[i/8] & (1 << (i % 8));
/* bit 8 to 19 bits for configuration bools, bit 20 to 23 for length of command section */
for(int i = 0; i < 12; i++)
init_bools[i] = data[1+i/8] & (1 << (i % 8));

command_length = data[0] >> 4;
command_length = data[CONFIG_LENGTH-1] >> 4;

/* Leave at least one byte as input */
if(command_length >= size - 1 - CONFIG_LENGTH)
Expand All @@ -106,13 +109,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
fclose(file_to_decode);
}

ogg = init_bools[0];
ogg = init_bools[0];

FLAC__stream_decoder_set_md5_checking(decoder,init_bools[1]);
if(init_bools[2])
FLAC__stream_decoder_set_metadata_respond_all(decoder);
if(init_bools[3])
FLAC__stream_decoder_set_metadata_ignore_all(decoder);
if(init_bools[4])
FLAC__stream_decoder_set_decode_chained_stream(decoder, true);

/* initialize decoder */
if(decoder_valid) {
Expand Down Expand Up @@ -182,6 +187,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
/* Set abort on write callback */
write_abort_check_counter = (command[0] >> 4) + 1;
break;
case 9:
FPRINTF_DEBUG_ONLY(stderr,"end_of_link\n");
decoder_valid = FLAC__stream_decoder_process_until_end_of_link(decoder);
break;
case 10:
FPRINTF_DEBUG_ONLY(stderr,"finish_link\n");
if(FLAC__stream_decoder_get_state(decoder) == FLAC__STREAM_DECODER_END_OF_LINK)
FLAC__stream_decoder_finish_link(decoder);

}
}

Expand Down
24 changes: 24 additions & 0 deletions src/libFLAC/include/private/ogg_decoder_aspect.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@

#include "FLAC/ordinals.h"
#include "FLAC/stream_decoder.h" /* for FLAC__StreamDecoderReadStatus */
#include "share/compat.h"

typedef struct FLAC__OggDecoderAspect_LinkDetails {
long serial_number;
FLAC__off_t size;
uint64_t samples;
} FLAC__OggDecoderAspect_LinkDetails;

typedef struct FLAC__OggDecoderAspect_TargetLink {
long serial_number;
FLAC__off_t start_byte;
FLAC__off_t end_byte;
uint64_t samples_in_preceding_links;
uint64_t samples_this_link;
uint32_t linknumber;
} FLAC__OggDecoderAspect_TargetLink;

typedef struct FLAC__OggDecoderAspect {
/* these are storage for values that can be set through the API */
Expand All @@ -55,6 +71,12 @@ typedef struct FLAC__OggDecoderAspect {
ogg_page working_page;
FLAC__bool have_working_packet; /* only if true will the following vars be valid */
ogg_packet working_packet; /* as we work through the packet we will move working_packet.packet forward and working_packet.bytes down */
FLAC__OggDecoderAspect_LinkDetails *linkdetails;
FLAC__OggDecoderAspect_TargetLink target_link; /* to pass data to the seek routine */
uint32_t number_of_links_detected;
uint32_t number_of_links_indexed;
uint32_t current_linknumber;
FLAC__bool is_seeking;
} FLAC__OggDecoderAspect;

void FLAC__ogg_decoder_aspect_set_serial_number(FLAC__OggDecoderAspect *aspect, long value);
Expand All @@ -64,8 +86,10 @@ void FLAC__ogg_decoder_aspect_finish(FLAC__OggDecoderAspect *aspect);
void FLAC__ogg_decoder_aspect_flush(FLAC__OggDecoderAspect *aspect);
void FLAC__ogg_decoder_aspect_reset(FLAC__OggDecoderAspect* aspect);
void FLAC__ogg_decoder_aspect_next_link(FLAC__OggDecoderAspect* aspect);
FLAC__OggDecoderAspect_TargetLink * FLAC__ogg_decoder_aspect_get_target_link(FLAC__OggDecoderAspect* aspect, FLAC__uint64 target_sample);
void FLAC__ogg_decoder_aspect_set_decode_chained_stream(FLAC__OggDecoderAspect* aspect, FLAC__bool value);
FLAC__bool FLAC__ogg_decoder_aspect_get_decode_chained_stream(FLAC__OggDecoderAspect* aspect);
void FLAC__ogg_decoder_aspect_set_seek_parameters(FLAC__OggDecoderAspect *aspect, FLAC__OggDecoderAspect_TargetLink *target_link);

typedef enum {
FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK = 0,
Expand Down
84 changes: 82 additions & 2 deletions src/libFLAC/ogg_decoder_aspect.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@

#include <string.h> /* for memcpy() */
#include "FLAC/assert.h"
#include "share/alloc.h" /* for free() */
#include "private/ogg_decoder_aspect.h"
#include "private/ogg_mapping.h"
#include "private/macros.h"


/***********************************************************************
*
* Public class methods
Expand All @@ -65,13 +65,22 @@ FLAC__bool FLAC__ogg_decoder_aspect_init(FLAC__OggDecoderAspect *aspect)
aspect->have_working_page = false;
aspect->end_of_link = false;

aspect->current_linknumber = 0;
aspect->number_of_links_indexed = 0;
aspect->number_of_links_detected = 0;

if(NULL == (aspect->linkdetails = safe_realloc_mul_2op_(NULL,4,sizeof(FLAC__OggDecoderAspect_LinkDetails))))
return false;
memset(aspect->linkdetails, 0, 4 * sizeof(FLAC__OggDecoderAspect_LinkDetails));

return true;
}

void FLAC__ogg_decoder_aspect_finish(FLAC__OggDecoderAspect *aspect)
{
(void)ogg_sync_clear(&aspect->sync_state);
(void)ogg_stream_clear(&aspect->stream_state);
free(aspect->linkdetails);
}

void FLAC__ogg_decoder_aspect_set_serial_number(FLAC__OggDecoderAspect *aspect, long value)
Expand All @@ -98,6 +107,7 @@ void FLAC__ogg_decoder_aspect_flush(FLAC__OggDecoderAspect *aspect)
void FLAC__ogg_decoder_aspect_reset(FLAC__OggDecoderAspect *aspect)
{
FLAC__ogg_decoder_aspect_flush(aspect);
aspect->current_linknumber = 0;

if(aspect->use_first_serial_number || aspect->decode_chained_stream)
aspect->need_serial_number = true;
Expand All @@ -106,6 +116,7 @@ void FLAC__ogg_decoder_aspect_reset(FLAC__OggDecoderAspect *aspect)
void FLAC__ogg_decoder_aspect_next_link(FLAC__OggDecoderAspect* aspect)
{
aspect->end_of_link = false;
aspect->current_linknumber++;
}

void FLAC__ogg_decoder_aspect_set_decode_chained_stream(FLAC__OggDecoderAspect* aspect, FLAC__bool value)
Expand All @@ -118,6 +129,48 @@ FLAC__bool FLAC__ogg_decoder_aspect_get_decode_chained_stream(FLAC__OggDecoderAs
return aspect->decode_chained_stream;
}

FLAC__OggDecoderAspect_TargetLink * FLAC__ogg_decoder_aspect_get_target_link(FLAC__OggDecoderAspect* aspect, FLAC__uint64 target_sample)
{
/* This returns the link containing the seek target if known. In
* effect, this function always returns NULL if no links have been
* indexed */

uint32_t current_link = 0;
uint32_t total_samples = 0;
uint32_t position = 0;

while(current_link < aspect->number_of_links_indexed) {
total_samples += aspect->linkdetails[current_link].samples;
if(target_sample < total_samples) {
aspect->target_link.serial_number = aspect->linkdetails[current_link].serial_number;
aspect->target_link.start_byte = position;
aspect->target_link.samples_in_preceding_links = total_samples - aspect->linkdetails[current_link].samples;
aspect->target_link.end_byte = position + aspect->linkdetails[current_link].size;
aspect->target_link.samples_this_link = aspect->linkdetails[current_link].samples;
aspect->target_link.linknumber = current_link;
return &aspect->target_link;
}
position += aspect->linkdetails[current_link].size;
current_link++;
}
return NULL;
}

void FLAC__ogg_decoder_aspect_set_seek_parameters(FLAC__OggDecoderAspect *aspect, FLAC__OggDecoderAspect_TargetLink *target_link)
{
if(target_link == 0) {
aspect->is_seeking = false;
}
else {
aspect->need_serial_number = false;
aspect->current_linknumber = target_link->linknumber;
aspect->serial_number = target_link->serial_number;
ogg_stream_reset_serialno(&aspect->stream_state, aspect->serial_number);
aspect->is_seeking = true;
}
}


FLAC__OggDecoderAspectReadStatus FLAC__ogg_decoder_aspect_read_callback_wrapper(FLAC__OggDecoderAspect *aspect, FLAC__byte buffer[], size_t *bytes, FLAC__OggDecoderAspectReadCallbackProxy read_callback, const FLAC__StreamDecoder *decoder, void *client_data)
{
static const size_t OGG_BYTES_CHUNK = 8192;
Expand Down Expand Up @@ -173,7 +226,13 @@ FLAC__OggDecoderAspectReadStatus FLAC__ogg_decoder_aspect_read_callback_wrapper(
aspect->end_of_stream = true;
else {
aspect->end_of_link = true;
aspect->need_serial_number = true;
if(aspect->current_linknumber >= aspect->number_of_links_indexed) {
aspect->linkdetails[aspect->current_linknumber].samples = aspect->working_packet.granulepos;
aspect->linkdetails[aspect->current_linknumber].serial_number = aspect->serial_number;
aspect->number_of_links_indexed++;
}
if(!aspect->is_seeking)
aspect->need_serial_number = true;
aspect->have_working_page = false; /* e-o-s packet ends page */
}
}
Expand Down Expand Up @@ -234,10 +293,31 @@ FLAC__OggDecoderAspectReadStatus FLAC__ogg_decoder_aspect_read_callback_wrapper(
aspect->serial_number = ogg_page_serialno(&aspect->working_page);
ogg_stream_reset_serialno(&aspect->stream_state, aspect->serial_number);
aspect->need_serial_number = false;

/* current_linknumber lags a bit: it is only increased after processing
* of the whole links is done, while this code does advance processing */
if(aspect->current_linknumber >= aspect->number_of_links_detected) {
FLAC__OggDecoderAspect_LinkDetails * tmpptr = NULL;
aspect->number_of_links_detected = aspect->current_linknumber + 1;

/* reallocate in chunks of 4 */
if((aspect->current_linknumber + 1) % 4 == 0) {
if(NULL == (tmpptr = safe_realloc_nofree_mul_2op_(aspect->linkdetails,5+aspect->current_linknumber,sizeof(FLAC__OggDecoderAspect_LinkDetails)))) {
return FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR;
}
aspect->linkdetails = tmpptr;
}
memset(&aspect->linkdetails[aspect->current_linknumber], 0, sizeof(FLAC__OggDecoderAspect_LinkDetails));
}


}
if(ogg_stream_pagein(&aspect->stream_state, &aspect->working_page) == 0) {
aspect->have_working_page = true;
aspect->have_working_packet = false;
if(aspect->current_linknumber >= aspect->number_of_links_indexed) {
aspect->linkdetails[aspect->current_linknumber].size += aspect->working_page.header_len + aspect->working_page.body_len;
}
}
/* else do nothing, could be a page from another stream */
}
Expand Down
Loading

0 comments on commit e864271

Please sign in to comment.