[Flac] C++ Set_Metadata Problem
Paul
elegant_dice at yahoo.com
Thu May 11 01:29:35 PDT 2006
Hi Nicholas,
That was my problem you are referring to. I did manage to figure it out, but
not without some hassles.
Here are the classes I'm now using (successfully).
hope this helps!
Paul
// inherit and override the callbacks, so it will write to a vector<>
// Note: Protected inheritance! We want to make a different interface.
// Note2: Keeps results as a reference! so ensure the lifespan of the
results exceeds the lifespan of instances of this class.
// Note3: Assumes that we are ALWAYS dealing with signed shorts.
class Flac_Encoder : protected FLAC::Encoder::SeekableStream, noncopyable
{
// keep cursor as an index
// it doesn't invalidate when the results are resized
ostream & results;
::FLAC__StreamMetadata app;
::FLAC__StreamMetadata* metadata_seq[1];
public:
struct Error : runtime_error
{
Error(string const& s) : runtime_error(s) {}
};
Flac_Encoder( unsigned int channels, string app_metadata, ostream & results )
: results(results)
{
unsigned int bits = sizeof(short)*8;
// do this via the C-API style, demonstrated in a test file
metadata_seq[0] = &app;
app.is_last = true;
app.type = FLAC__METADATA_TYPE_APPLICATION;
app.length = 4 + app_metadata.size();
copy(&flac_id[0],&flac_id[4],&app.data.application.id[0]);
app.data.application.data = new FLAC__byte[app_metadata.size()];
copy(app_metadata.begin(),app_metadata.end(),&app.data.application.data[0]);
set_metadata(metadata_seq,1);
set_channels(channels);
set_bits_per_sample(bits);
init();
}
// returns our results as a const-reference
template <class It>
void operator()( It begin, It end )
{
vector<FLAC__int32> samples(begin,end);
if (!process_interleaved(&samples[0],samples.size()/get_channels()))
throw Error("Flac_Encoder::process_interleaved() failed");
}
virtual ~Flac_Encoder()
{
finish();
}
// overridden functions
protected:
virtual ::FLAC__SeekableStreamEncoderSeekStatus
seek_callback(FLAC__uint64 absolute_byte_offset)
{
try {
results.seekp(absolute_byte_offset,ios::beg);
return FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK;
}
catch (ostream::failure & e)
{
return FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_ERROR;
}
}
virtual ::FLAC__SeekableStreamEncoderTellStatus
tell_callback(FLAC__uint64 *absolute_byte_offset)
{
try {
*absolute_byte_offset = results.tellp();
return FLAC__SEEKABLE_STREAM_ENCODER_TELL_STATUS_OK;
}
catch (ostream::failure & e)
{
return FLAC__SEEKABLE_STREAM_ENCODER_TELL_STATUS_ERROR;
}
}
virtual ::FLAC__StreamEncoderWriteStatus
write_callback (const FLAC__byte buffer[], unsigned bytes, unsigned
samples, unsigned current_frame)
{
try {
results.write(reinterpret_cast<const char*>(buffer),bytes);
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}
catch (ostream::failure & e)
{
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
}
}
};
class Flac_Decoder : protected FLAC::Decoder::SeekableStream, noncopyable
{
public:
string const& metadata() const { return results_metadata; }
vector<FLAC__int32> const& samples() const { return results; }
// call this to drop the first N samples out of the buffer
void clear_n_samples( size_t n )
{
assert(results.size() >= n);
results.erase( results.begin(), next(results.begin(),n));
}
Flac_Decoder( shared_ptr<istream> b ) : data(b), got_eof(false)
{
set_md5_checking(true);
set_metadata_respond_all();
init();
process_until_end_of_metadata();
}
virtual ~Flac_Decoder()
{
finish();
}
bool read_frame()
{
return process_single();
}
void print_decoder_state() const
{
switch (get_stream_decoder_state())
{
case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA:
log_file() << "SEARCH_FOR_METADATA" << endl;
break;
case FLAC__STREAM_DECODER_READ_METADATA:
log_file() << "READ_METADATA" << endl;
break;
case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
log_file() << "SEARCH_FOR_FRAME_SYNC" << endl;
break;
case FLAC__STREAM_DECODER_READ_FRAME:
log_file() << "READ_FRAME" << endl;
break;
case FLAC__STREAM_DECODER_END_OF_STREAM:
log_file() << "END_OF_STREAM" << endl;
break;
case FLAC__STREAM_DECODER_ABORTED:
log_file() << "ABORTED" << endl;
break;
case FLAC__STREAM_DECODER_UNPARSEABLE_STREAM:
log_file() << "UNPARSEABLE_STREAM" << endl;
break;
case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
log_file() << "MEMORY_ALLOCATION_ERROR" << endl;
break;
case FLAC__STREAM_DECODER_ALREADY_INITIALIZED:
log_file() << "ALREADY_INITIALIZED" << endl;
break;
case FLAC__STREAM_DECODER_INVALID_CALLBACK:
log_file() << "INVALID_CALLBACK" << endl;
break;
case FLAC__STREAM_DECODER_UNINITIALIZED:
log_file() << "UNINITIALIZED" << endl;
break;
}
}
void print_state() const
{
switch (get_state())
{
case FLAC__SEEKABLE_STREAM_DECODER_OK:
log_file() << "OK" << endl;
break;
case FLAC__SEEKABLE_STREAM_DECODER_SEEKING:
log_file() << "SEEKING" << endl;
break;
case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM:
log_file() << "END_OF_STREAM" << endl;
break;
case FLAC__SEEKABLE_STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
log_file() << "MEMORY_ALLOCATION_ERROR" << endl;
break;
case FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR:
log_file() << "SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR" <<
endl;
break;
case FLAC__SEEKABLE_STREAM_DECODER_READ_ERROR:
log_file() << "READ_ERROR" << endl;
break;
case FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR:
log_file() << "SEEK_ERROR" << endl;
break;
case FLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED:
log_file() << "ALREADY_INITIALIZED" << endl;
break;
case FLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK:
log_file() << "INVALID_CALLBACK" << endl;
break;
case FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED:
log_file() << "UNINITIALIZED" << endl;
break;
}
}
unsigned int channels() const
{
return get_channels();
}
private:
shared_ptr<istream> data;
vector<FLAC__int32> results;
string results_metadata;
bool got_eof;
protected:
virtual ::FLAC__SeekableStreamDecoderReadStatus
read_callback (FLAC__byte buffer[], unsigned *bytes)
{
try {
// check for EOF errors first
if (got_eof)
return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR;
data->read(reinterpret_cast<char*>(buffer),*bytes);
return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
}
catch (istream::failure & e)
{
// ignore EOF-generated failures
if (data->eof())
{
got_eof = true;
return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
}
return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR;
}
}
virtual ::FLAC__SeekableStreamDecoderSeekStatus
seek_callback (FLAC__uint64 absolute_byte_offset)
{
try {
data->seekg(absolute_byte_offset,ios::beg);
return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;
}
catch (istream::failure & e)
{
return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
}
}
virtual ::FLAC__SeekableStreamDecoderTellStatus
tell_callback (FLAC__uint64 *absolute_byte_offset)
{
try {
*absolute_byte_offset = data->tellg();
return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
}
catch (istream::failure & e)
{
return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR;
}
}
virtual ::FLAC__SeekableStreamDecoderLengthStatus
length_callback (FLAC__uint64 *stream_length)
{
try {
streampos old = data->tellg();
data->seekg (0, ios::end);
*stream_length = data->tellg();
data->seekg (old, ios::beg);
return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
}
catch (istream::failure & e)
{
return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR;
}
}
virtual bool eof_callback ()
{
return data->eof();
}
virtual ::FLAC__StreamDecoderWriteStatus
write_callback (const ::FLAC__Frame *frame, const FLAC__int32 *const
buffer[])
{
// read it back, interleaved!
// this is more inefficient, but at the moment we expect blocks
// to be interleaved, so we keep it that way.
for ( unsigned int b = 0; b != frame->header.blocksize; ++b )
for ( unsigned int c = 0; c != frame->header.channels; ++c )
results.push_back( buffer[c][b] );
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
virtual void metadata_callback (const ::FLAC__StreamMetadata *metadata)
{
switch (metadata->type)
{
case FLAC__METADATA_TYPE_APPLICATION:
// compare the ids
if (metadata->data.application.id[0] == flac_id[0] and
metadata->data.application.id[1] == flac_id[1] and
metadata->data.application.id[2] == flac_id[2] and
metadata->data.application.id[3] == flac_id[3])
{
results_metadata.resize( metadata->length-4 );
copy( &metadata->data.application.data[0],
&metadata->data.application.data[results_metadata.size()], &results_metadata[0] );
}
break;
default: ; // do nothing
}
}
virtual void error_callback ( ::FLAC__StreamDecoderErrorStatus status)
{
log_file() << "Error: ";
print_state();
}
};
Padfield, Nicholas wrote:
> I refer to a problem that appeared on the flac list last August that was
> either solved off-list or abandoned.
> (http://lists.xiph.org/pipermail/flac/2005-August/000468.html)
>
> The problem is with using the C++ encoder classes, particularly the
> FLAC::Encoder::File:set_metadata
> function. JC said that the developers version of how to add a simple
> metadata block looked right, but it did not work for him. I have tried
> the same and also can not get it to work, but with a different error.
>
> To recap... (I've left out is-valid checks in the listing for brevity)
>
> //////////
>
> // Create an application block
> FLAC::Metadata::Application header_flac;
> FLAC__byte header_flac_id[4] = { 1, 2, 3, 4 };
> header_flac.set_id(header_flac_id);
> header_flac.set_data((FLAC__byte*)(header_str.begin()),header_str.size()
> );
>
> // Add the block to a metadata array and pass the array to the encoder
> object
> FLAC::Metadata::Prototype *meta[] = { &header_flac };
> set_metadata(meta, sizeof(meta) / sizeof(meta[0]));
>
> // Ready to go - initialise the encoder
> if(init() != ::FLAC__FILE_ENCODER_OK)
> return die("Init failed");
>
> ///////////
>
> And this returns the following ...
>
> FAILED, Init failed, state = 2
> (FLAC__FILE_ENCODER_SEEKABLE_STREAM_ENCODER_ERROR)
> seekable stream encoder state = 1
> (FLAC__SEEKABLE_STREAM_ENCODER_STREAM_ENCODER_ERROR)
> stream encoder state = 16 (FLAC__STREAM_ENCODER_INVALID_METADATA)
>
> I have invalid metadata! I have tried the same with VorbisComment and
> Padding objects, with similar results.
>
> Do I need to set the is_last flag for the last block in my array or does
> the decoder object do that?
>
> Any ideas about what is wrong?
>
> Thanks
> Paddy
> _______________________________________________
> Flac mailing list
> Flac at xiph.org
> http://lists.xiph.org/mailman/listinfo/flac
>
More information about the Flac
mailing list