[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