diff --git a/src/actions.h b/src/actions.h index 8836df9..b8fdec5 100644 --- a/src/actions.h +++ b/src/actions.h @@ -37,7 +37,8 @@ namespace opustags { // header is read. // // Use: - // ogg::Decoder dec(std::ifstream("in.ogg")); + // std::ifstream in("in.ogg"); + // ogg::Decoder dec(&in); // TagsLister lister(options); // list_tags(dec, lister); // @@ -47,8 +48,10 @@ namespace opustags { // with the handler's edit method. // // Use: - // ogg::Decoder dec(std::ifstream("in.ogg")); - // ogg::Encoder enc(std::ofstream("out.ogg")); + // std::ifstream in("in.ogg"); + // ogg::Decoder dec(&in); + // std::ofstream out("out.ogg"); + // std::Encoder enc(&out); // TagsEditor editor(options); // edit_tags(dec, enc, editor); // diff --git a/src/ogg.cc b/src/ogg.cc index 9ffff5d..83ef006 100644 --- a/src/ogg.cc +++ b/src/ogg.cc @@ -1,9 +1,13 @@ #include "ogg.h" #include +#include using namespace opustags; +//////////////////////////////////////////////////////////////////////////////// +// ogg::Stream + ogg::Stream::Stream(int streamno) { state = ogg::BEGIN_OF_STREAM; @@ -61,11 +65,14 @@ void ogg::Stream::handle_packet(const ogg_packet &op) void ogg::Stream::parse_header(const ogg_packet &op) { // TODO + // set type + // set state } void ogg::Stream::parse_tags(const ogg_packet &op) { // TODO + state = TAGS_READY; } void ogg::Stream::downgrade() @@ -74,3 +81,65 @@ void ogg::Stream::downgrade() if (state != ogg::BEGIN_OF_STREAM && state != ogg::END_OF_STREAM) state = RAW_READY; } + +//////////////////////////////////////////////////////////////////////////////// +// ogg::Decoder + +ogg::Decoder::Decoder(std::istream *in) + : input(in) +{ + input->exceptions(std::ifstream::badbit); + ogg_sync_init(&sync); +} + +ogg::Decoder::~Decoder() +{ + ogg_sync_clear(&sync); +} + +ogg::Stream* ogg::Decoder::read_page() +{ + while (page_out()) { + int streamno = ogg_page_serialno(¤t_page); + auto i = streams.find(streamno); + if (i == streams.end()) { + // we could check the page number to detect new streams (pageno = 0) + i = streams.emplace(streamno, Stream(streamno)).first; + } + if (i->second.page_in(current_page)) + return &(i->second); + } + return NULL; // end of stream +} + +// Read the next page and return true on success, false on end of stream. +bool ogg::Decoder::page_out() +{ + int rc; + for (;;) { + rc = ogg_sync_pageout(&sync, ¤t_page); + if (rc < 0) { + throw std::runtime_error("ogg_sync_pageout failed"); + } else if (rc == 1) { + break; // page complete + } else if (!buff()) { + // more data required but end of file reached + // TODO check sync.unsynced flag in case we've got an incomplete page + return false; + } + } + return true; +} + +// Read data from the stream into the sync's buffer. +bool ogg::Decoder::buff() +{ + if (input->eof()) + return false; + char *buf = ogg_sync_buffer(&sync, 65536); + if (buf == NULL) + throw std::runtime_error("ogg_sync_buffer failed"); + input->read(buf, 65536); + ogg_sync_wrote(&sync, input->gcount()); + return true; +} diff --git a/src/ogg.h b/src/ogg.h index b301c5c..d3125d8 100644 --- a/src/ogg.h +++ b/src/ogg.h @@ -70,7 +70,7 @@ namespace ogg struct Decoder { - Decoder(std::istream&&); + Decoder(std::istream*); ~Decoder(); // Read a page, dispatch it, and return the stream it belongs to. @@ -79,16 +79,20 @@ namespace ogg // After the end of the file is reached, it returns NULL. Stream* read_page(); - std::istream input; + std::istream *input; ogg_sync_state sync; ogg_page current_page; std::map streams; + + private: + bool page_out(); + bool buff(); }; struct Encoder { - Encoder(std::ostream&&); + Encoder(std::ostream*); ~Encoder(); void write_page(const ogg_page&); @@ -100,7 +104,7 @@ namespace ogg void write_tags(int streamno, const Tags&); - std::ostream output; + std::ostream *output; // We're gonna need some ogg_stream_state for adjusting the page // numbers and splitting large packets as it's gotta be done.