FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object) { FLAC__ASSERT(0 != object); FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); return FLAC__format_seektable_is_legal(&object->data.seek_table); }
OggFLAC_API OggFLAC__SeekableStreamEncoderState OggFLAC__seekable_stream_encoder_init(OggFLAC__SeekableStreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); if(encoder->protected_->state != OggFLAC__SEEKABLE_STREAM_ENCODER_UNINITIALIZED) return encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_ALREADY_INITIALIZED; if(0 == encoder->private_->seek_callback || 0 == encoder->private_->tell_callback || 0 == encoder->private_->write_callback) return encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_INVALID_CALLBACK; if(!OggFLAC__ogg_encoder_aspect_init(&encoder->protected_->ogg_encoder_aspect)) return encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR; if(0 != encoder->private_->seek_table && !FLAC__format_seektable_is_legal(encoder->private_->seek_table)) return encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_INVALID_SEEKTABLE; /* * These must be done before we init the stream encoder because that * calls the write_callback, which uses these values. */ encoder->private_->first_seekpoint_to_check = 0; encoder->private_->samples_written = 0; encoder->protected_->streaminfo_offset = 0; encoder->protected_->seektable_offset = 0; encoder->protected_->audio_offset = 0; FLAC__stream_encoder_set_write_callback(encoder->private_->FLAC_stream_encoder, write_callback_); FLAC__stream_encoder_set_metadata_callback(encoder->private_->FLAC_stream_encoder, metadata_callback_); FLAC__stream_encoder_set_client_data(encoder->private_->FLAC_stream_encoder, encoder); if(FLAC__stream_encoder_init(encoder->private_->FLAC_stream_encoder) != FLAC__STREAM_ENCODER_OK) return encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_FLAC_STREAM_ENCODER_ERROR; /* * Initializing the stream encoder writes all the metadata, so we * save the stream offset now. */ if(encoder->private_->tell_callback(encoder, &encoder->protected_->audio_offset, encoder->private_->client_data) != FLAC__SEEKABLE_STREAM_ENCODER_TELL_STATUS_OK) return encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_TELL_ERROR; return encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OK; }
void metadata_callback_(const FLAC__StreamEncoder *unused, const FLAC__StreamMetadata *metadata, void *client_data) { OggFLAC__SeekableStreamEncoder *encoder = (OggFLAC__SeekableStreamEncoder*)client_data; FLAC__byte b[max(6, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)]; const FLAC__uint64 samples = metadata->data.stream_info.total_samples; const unsigned min_framesize = metadata->data.stream_info.min_framesize; const unsigned max_framesize = metadata->data.stream_info.max_framesize; ogg_page page; FLAC__ASSERT(metadata->type == FLAC__METADATA_TYPE_STREAMINFO); /* We get called by the stream encoder when the encoding process * has finished so that we can update the STREAMINFO and SEEKTABLE * blocks. */ (void)unused; /* silence compiler warning about unused parameter */ FLAC__ASSERT(encoder->private_->FLAC_stream_encoder == unused); /*@@@ reopen callback here? The docs currently require user to open files in update mode from the start */ /* All this is based on intimate knowledge of the stream header * layout, but a change to the header format that would break this * would also break all streams encoded in the previous format. */ /* * Write STREAMINFO stats */ simple_ogg_page__init(&page); if(!simple_ogg_page__get_at(encoder, encoder->protected_->streaminfo_offset, &page, encoder->private_->seek_callback, encoder->private_->read_callback, encoder->private_->client_data)) { simple_ogg_page__clear(&page); return; /* state already set */ } /* * MD5 signature */ { const unsigned md5_offset = FLAC__STREAM_METADATA_HEADER_LENGTH + ( FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN + FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN + FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN + FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN + FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN + FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN + FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN ) / 8; if(md5_offset + 16 > (unsigned)page.body_len) { encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR; simple_ogg_page__clear(&page); return; } memcpy(page.body + md5_offset, metadata->data.stream_info.md5sum, 16); } /* * total samples */ { const unsigned total_samples_byte_offset = FLAC__STREAM_METADATA_HEADER_LENGTH + ( FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN + FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN + FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN + FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN + FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN + FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN - 4 ) / 8; if(total_samples_byte_offset + 5 > (unsigned)page.body_len) { encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR; simple_ogg_page__clear(&page); return; } b[0] = (FLAC__byte)page.body[total_samples_byte_offset] & 0xF0; b[0] |= (FLAC__byte)((samples >> 32) & 0x0F); b[1] = (FLAC__byte)((samples >> 24) & 0xFF); b[2] = (FLAC__byte)((samples >> 16) & 0xFF); b[3] = (FLAC__byte)((samples >> 8) & 0xFF); b[4] = (FLAC__byte)(samples & 0xFF); memcpy(page.body + total_samples_byte_offset, b, 5); } /* * min/max framesize */ { const unsigned min_framesize_offset = FLAC__STREAM_METADATA_HEADER_LENGTH + ( FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN ) / 8; if(min_framesize_offset + 6 > (unsigned)page.body_len) { encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR; simple_ogg_page__clear(&page); return; } b[0] = (FLAC__byte)((min_framesize >> 16) & 0xFF); b[1] = (FLAC__byte)((min_framesize >> 8) & 0xFF); b[2] = (FLAC__byte)(min_framesize & 0xFF); b[3] = (FLAC__byte)((max_framesize >> 16) & 0xFF); b[4] = (FLAC__byte)((max_framesize >> 8) & 0xFF); b[5] = (FLAC__byte)(max_framesize & 0xFF); memcpy(page.body + min_framesize_offset, b, 6); } if(!simple_ogg_page__set_at(encoder, encoder->protected_->streaminfo_offset, &page, encoder->private_->seek_callback, encoder->private_->write_callback, encoder->private_->client_data)) { simple_ogg_page__clear(&page); return; /* state already set */ } simple_ogg_page__clear(&page); /* * Write seektable */ if(0 != encoder->private_->seek_table && encoder->private_->seek_table->num_points > 0 && encoder->protected_->seektable_offset > 0) { unsigned i; FLAC__byte *p; FLAC__format_seektable_sort(encoder->private_->seek_table); FLAC__ASSERT(FLAC__format_seektable_is_legal(encoder->private_->seek_table)); simple_ogg_page__init(&page); if(!simple_ogg_page__get_at(encoder, encoder->protected_->seektable_offset, &page, encoder->private_->seek_callback, encoder->private_->read_callback, encoder->private_->client_data)) { simple_ogg_page__clear(&page); return; /* state already set */ } if(FLAC__STREAM_METADATA_HEADER_LENGTH + (18*encoder->private_->seek_table->num_points) > (unsigned)page.body_len) { encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR; simple_ogg_page__clear(&page); return; } for(i = 0, p = page.body + FLAC__STREAM_METADATA_HEADER_LENGTH; i < encoder->private_->seek_table->num_points; i++, p += 18) { FLAC__uint64 xx; unsigned x; xx = encoder->private_->seek_table->points[i].sample_number; b[7] = (FLAC__byte)xx; xx >>= 8; b[6] = (FLAC__byte)xx; xx >>= 8; b[5] = (FLAC__byte)xx; xx >>= 8; b[4] = (FLAC__byte)xx; xx >>= 8; b[3] = (FLAC__byte)xx; xx >>= 8; b[2] = (FLAC__byte)xx; xx >>= 8; b[1] = (FLAC__byte)xx; xx >>= 8; b[0] = (FLAC__byte)xx; xx >>= 8; xx = encoder->private_->seek_table->points[i].stream_offset; b[15] = (FLAC__byte)xx; xx >>= 8; b[14] = (FLAC__byte)xx; xx >>= 8; b[13] = (FLAC__byte)xx; xx >>= 8; b[12] = (FLAC__byte)xx; xx >>= 8; b[11] = (FLAC__byte)xx; xx >>= 8; b[10] = (FLAC__byte)xx; xx >>= 8; b[9] = (FLAC__byte)xx; xx >>= 8; b[8] = (FLAC__byte)xx; xx >>= 8; x = encoder->private_->seek_table->points[i].frame_samples; b[17] = (FLAC__byte)x; x >>= 8; b[16] = (FLAC__byte)x; x >>= 8; if(encoder->private_->write_callback(encoder, b, 18, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_WRITE_ERROR; simple_ogg_page__clear(&page); return; } memcpy(p, b, 18); } if(!simple_ogg_page__set_at(encoder, encoder->protected_->seektable_offset, &page, encoder->private_->seek_callback, encoder->private_->write_callback, encoder->private_->client_data)) { simple_ogg_page__clear(&page); return; /* state already set */ } simple_ogg_page__clear(&page); }