diff options
Diffstat (limited to 'tdefile-plugins/ogg')
-rw-r--r-- | tdefile-plugins/ogg/Makefile.am | 22 | ||||
-rw-r--r-- | tdefile-plugins/ogg/configure.in.in | 1 | ||||
-rw-r--r-- | tdefile-plugins/ogg/tdefile_ogg.cpp | 357 | ||||
-rw-r--r-- | tdefile-plugins/ogg/tdefile_ogg.desktop | 68 | ||||
-rw-r--r-- | tdefile-plugins/ogg/tdefile_ogg.h | 46 | ||||
-rw-r--r-- | tdefile-plugins/ogg/vcedit.c | 331 | ||||
-rw-r--r-- | tdefile-plugins/ogg/vcedit.h | 56 |
7 files changed, 881 insertions, 0 deletions
diff --git a/tdefile-plugins/ogg/Makefile.am b/tdefile-plugins/ogg/Makefile.am new file mode 100644 index 00000000..a6590beb --- /dev/null +++ b/tdefile-plugins/ogg/Makefile.am @@ -0,0 +1,22 @@ +## Makefile.am for ogg/vorbis file meta info plugin + +# set the include path for X, qt and KDE +INCLUDES = $(all_includes) + +# these are the headers for your project +noinst_HEADERS = tdefile_ogg.h + +kde_module_LTLIBRARIES = tdefile_ogg.la + +tdefile_ogg_la_SOURCES = tdefile_ogg.cpp vcedit.c +tdefile_ogg_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_KIO) -ltdetexteditor -module $(KDE_PLUGIN) +tdefile_ogg_la_LIBADD = $(LIB_KIO) -logg -lvorbis -lvorbisfile + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) tdefile_ogg.cpp -o $(podir)/tdefile_ogg.pot + +services_DATA = tdefile_ogg.desktop +servicesdir = $(kde_servicesdir) diff --git a/tdefile-plugins/ogg/configure.in.in b/tdefile-plugins/ogg/configure.in.in new file mode 100644 index 00000000..35218d34 --- /dev/null +++ b/tdefile-plugins/ogg/configure.in.in @@ -0,0 +1 @@ +AM_CONDITIONAL(include_ogg_SUBDIR, test "x$have_oggvorbis" = xyes) diff --git a/tdefile-plugins/ogg/tdefile_ogg.cpp b/tdefile-plugins/ogg/tdefile_ogg.cpp new file mode 100644 index 00000000..b141acb1 --- /dev/null +++ b/tdefile-plugins/ogg/tdefile_ogg.cpp @@ -0,0 +1,357 @@ +/* This file is part of the KDE project + * Copyright (C) 2001, 2002 Rolf Magnus <ramagnus@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * $Id$ + */ + +#include "tdefile_ogg.h" +#include "vcedit.h" + +#include <tqcstring.h> +#include <tqfile.h> +#include <tqdatetime.h> +#include <tqdict.h> +#include <tqvalidator.h> +#include <tqfileinfo.h> + +#include <kdebug.h> +#include <kurl.h> +#include <kprocess.h> +#include <klocale.h> +#include <kgenericfactory.h> +#include <ksavefile.h> + +#include <ogg/ogg.h> +#include <vorbis/codec.h> +#include <vorbis/vorbisfile.h> + +#include <sys/stat.h> +#include <unistd.h> + +// known translations for common ogg/vorbis keys +// from http://www.ogg.org/ogg/vorbis/doc/v-comment.html +static const char* const knownTranslations[] = { + I18N_NOOP("Title"), + I18N_NOOP("Version"), + I18N_NOOP("Album"), + I18N_NOOP("Tracknumber"), + I18N_NOOP("Artist"), + I18N_NOOP("Organization"), + I18N_NOOP("Description"), + I18N_NOOP("Genre"), + I18N_NOOP("Date"), + I18N_NOOP("Location"), + I18N_NOOP("Copyright") +// I18N_NOOP("Isrc") // dunno what an Isrc number is, the link is broken +}; + +K_EXPORT_COMPONENT_FACTORY(tdefile_ogg, KGenericFactory<KOggPlugin>("tdefile_ogg")) + +KOggPlugin::KOggPlugin( TQObject *parent, const char *name, + const TQStringList &args ) + : KFilePlugin( parent, name, args ) +{ + kdDebug(7034) << "ogg plugin\n"; + + KFileMimeTypeInfo* info = addMimeTypeInfo( "audio/vorbis" ); + + KFileMimeTypeInfo::GroupInfo* group = 0; + + // comment group + group = addGroupInfo(info, "Comment", i18n("Comment")); + setAttributes(group, KFileMimeTypeInfo::Addable | + KFileMimeTypeInfo::Removable); + + KFileMimeTypeInfo::ItemInfo* item = 0; + + item = addItemInfo(group, "Artist", i18n("Artist"), TQVariant::String); + setHint(item, KFileMimeTypeInfo::Author); + setAttributes(item, KFileMimeTypeInfo::Modifiable); + + item = addItemInfo(group, "Title", i18n("Title"), TQVariant::String); + setHint(item, KFileMimeTypeInfo::Name); + setAttributes(item, KFileMimeTypeInfo::Modifiable); + + item = addItemInfo(group, "Album", i18n("Album"), TQVariant::String); + setAttributes(item, KFileMimeTypeInfo::Modifiable); + + item = addItemInfo(group, "Genre", i18n("Genre"), TQVariant::String); + setAttributes(item, KFileMimeTypeInfo::Modifiable); + + item = addItemInfo(group, "Tracknumber", i18n("Track Number"), TQVariant::String); + setAttributes(item, KFileMimeTypeInfo::Modifiable); + + item = addItemInfo(group, "Date", i18n("Date"), TQVariant::String); + setAttributes(item, KFileMimeTypeInfo::Modifiable); + + item = addItemInfo(group, "Description", i18n("Description"), TQVariant::String); + setAttributes(item, KFileMimeTypeInfo::Modifiable); + + item = addItemInfo(group, "Organization", i18n("Organization"), TQVariant::String); + setAttributes(item, KFileMimeTypeInfo::Modifiable); + + item = addItemInfo(group, "Location", i18n("Location"), TQVariant::String); + setAttributes(item, KFileMimeTypeInfo::Modifiable); + + item = addItemInfo(group, "Copyright", i18n("Copyright"), TQVariant::String); + setAttributes(item, KFileMimeTypeInfo::Modifiable); + + + addVariableInfo(group, TQVariant::String, KFileMimeTypeInfo::Addable | + KFileMimeTypeInfo::Removable | + KFileMimeTypeInfo::Modifiable); + + // technical group + + group = addGroupInfo(info, "Technical", i18n("Technical Details")); + setAttributes(group, 0); + + addItemInfo(group, "Version", i18n("Version"), TQVariant::Int); + addItemInfo(group, "Channels", i18n("Channels"), TQVariant::Int); + + item = addItemInfo(group, "Sample Rate", i18n("Sample Rate"), TQVariant::Int); + setSuffix(item, i18n(" Hz")); + + item = addItemInfo(group, "UpperBitrate", i18n("Upper Bitrate"), + TQVariant::Int); + setSuffix(item, i18n(" kbps")); + + item = addItemInfo(group, "LowerBitrate", i18n("Lower Bitrate"), + TQVariant::Int); + setSuffix(item, i18n(" kbps")); + + item = addItemInfo(group, "NominalBitrate", i18n("Nominal Bitrate"), + TQVariant::Int); + setSuffix(item, i18n(" kbps")); + + item = addItemInfo(group, "Bitrate", i18n("Average Bitrate"), + TQVariant::Int); + setAttributes(item, KFileMimeTypeInfo::Averaged); + setHint(item, KFileMimeTypeInfo::Bitrate); + setSuffix(item, i18n( " kbps")); + + item = addItemInfo(group, "Length", i18n("Length"), TQVariant::Int); + setAttributes(item, KFileMimeTypeInfo::Cummulative); + setUnit(item, KFileMimeTypeInfo::Seconds); +} + +bool KOggPlugin::readInfo( KFileMetaInfo& info, uint what ) +{ + // parts of this code taken from ogginfo.c of the vorbis-tools v1.0rc2 + FILE *fp; + OggVorbis_File vf; + int rc,i; + vorbis_comment *vc; + vorbis_info *vi; + + bool readComment = false; + bool readTech = false; + if (what & (KFileMetaInfo::Fastest | + KFileMetaInfo::DontCare | + KFileMetaInfo::ContentInfo)) readComment = true; + + if (what & (KFileMetaInfo::Fastest | + KFileMetaInfo::DontCare | + KFileMetaInfo::TechnicalInfo)) readTech = true; + + memset(&vf, 0, sizeof(OggVorbis_File)); + + if ( info.path().isEmpty() ) // remote file + return false; + + fp = fopen(TQFile::encodeName(info.path()),"rb"); + if (!fp) + { + kdDebug(7034) << "Unable to open " << TQFile::encodeName(info.path()).data() << endl; + return false; + } + + rc = ov_open(fp,&vf,NULL,0); + + if (rc < 0) + { + kdDebug(7034) << "Unable to understand " << TQFile::encodeName(info.path()).data() + << ", errorcode=" << rc << endl; + return false; + } + +// info.insert(KFileMetaInfoItem("Vendor", i18n("Vendor"), +// TQVariant(TQString(vi->vendor)))); + + // get the vorbis comments + if (readComment) + { + vc = ov_comment(&vf,-1); + + KFileMetaInfoGroup commentGroup = appendGroup(info, "Comment"); + + for (i=0; i < vc->comments; i++) + { + kdDebug(7034) << vc->user_comments[i] << endl; + TQStringList split = TQStringList::split("=", TQString::fromUtf8(vc->user_comments[i])); + split[0] = split[0].lower(); + split[0][0] = split[0][0].upper(); + + // we have to be sure that the i18n() string always has the same + // case. Oh, and is UTF8 ok here? + appendItem(commentGroup, split[0], split[1]); + } + } + + if (readTech) + { + KFileMetaInfoGroup techgroup = appendGroup(info, "Technical"); + // get other information about the file + vi = ov_info(&vf,-1); + if (vi) + { + + appendItem(techgroup, "Version", int(vi->version)); + appendItem(techgroup, "Channels", int(vi->channels)); + appendItem(techgroup, "Sample Rate", int(vi->rate)); + + if (vi->bitrate_upper > 0) + appendItem(techgroup, "UpperBitrate", + int(vi->bitrate_upper+500)/1000); + if (vi->bitrate_lower > 0) + appendItem(techgroup, "LowerBitrate", + int(vi->bitrate_lower+500)/1000); + if (vi->bitrate_nominal > 0) + appendItem(techgroup, "NominalBitrate", + int(vi->bitrate_nominal+500)/1000); + + if (ov_bitrate(&vf,-1) > 0) + appendItem(techgroup, "Bitrate", int(ov_bitrate(&vf,-1)+500)/1000); + + } + + appendItem(techgroup, "Length", int(ov_time_total(&vf,-1))); + } + + ov_clear(&vf); + + return true; +} + +bool KOggPlugin::writeInfo(const KFileMetaInfo& info) const +{ + // todo: add writing support + FILE* infile; + + infile = fopen(TQFile::encodeName(info.path()), "r"); + + if (!infile) + { + kdDebug(7034) << "couldn't open " << info.path() << endl; + return false; + } + + vcedit_state *state=vcedit_new_state(); + + if ( vcedit_open(state, infile)==-1 ) + { + kdDebug(7034) << "error in vcedit_open for " << info.path() << endl; + return false; + } + + struct vorbis_comment* oc = vcedit_comments(state); + struct vorbis_comment* vc = state->vc; + + if(vc) vorbis_comment_clear(vc); + + if (oc && oc->vendor) + { + vc->vendor = strdup(oc->vendor); + } + else + { + vc->vendor = strdup(""); + } + + KFileMetaInfoGroup group = info["Comment"]; + + TQStringList keys = group.keys(); + TQStringList::Iterator it; + for (it = keys.begin(); it!=keys.end(); ++it) + { + KFileMetaInfoItem item = group[*it]; + + if (!item.isEditable() || !(item.type()==TQVariant::String) ) + continue; + + TQCString key = item.key().upper().utf8(); + if (item.value().canCast(TQVariant::String)) + { + TQCString value = item.value().toString().utf8(); + + kdDebug(7034) << " writing tag " << key << "=" << value << endl; + + vorbis_comment_add_tag(vc, + const_cast<char*>(static_cast<const char*>(key)), + const_cast<char*>(static_cast<const char*>(value))); + } + else + kdWarning(7034) << "ignoring " << key << endl; + + } + + TQString filename; + + TQFileInfo fileinfo(info.path()); + + // follow symlinks + if (fileinfo.isSymLink()) + filename = fileinfo.readLink(); + else + filename = info.path(); + + // nothing in TQt or KDE to get the mode as an int? + struct stat s; + stat(TQFile::encodeName(filename), &s); + + KSaveFile sf(filename, s.st_mode); + FILE* outfile = sf.fstream(); + + if ( sf.status() || !outfile) + { + kdDebug(7034) << "couldn't create temp file\n"; + vcedit_clear(state); // frees comment entries and vendor + sf.abort(); + if (vc->vendor) free(vc->vendor); + vc->vendor = 0; + return false; + } + + + vcedit_write(state,outfile); // calls vcedit_clear() itself so we don't free anything + + if (vc->vendor) free(vc->vendor); + vc->vendor = 0; + + fclose(infile); + sf.close(); + + return true; +} + +TQValidator* KOggPlugin::createValidator( const TQString&, + const TQString &, const TQString &, + TQObject* parent, const char* name) const { + return new TQRegExpValidator(TQRegExp(".*"), parent, name); +} + +#include "tdefile_ogg.moc" diff --git a/tdefile-plugins/ogg/tdefile_ogg.desktop b/tdefile-plugins/ogg/tdefile_ogg.desktop new file mode 100644 index 00000000..9cc7fe6e --- /dev/null +++ b/tdefile-plugins/ogg/tdefile_ogg.desktop @@ -0,0 +1,68 @@ +[Desktop Entry] +Type=Service +Name=OGG Info +Name[af]=Ogg Inligting +Name[ar]=معلومات OGG +Name[bg]=Информация за OGG +Name[bn]=অগ তথ্য +Name[br]=Titouroù OGG +Name[bs]=OGG informacije +Name[ca]=Informació OGG +Name[cs]=OGG info +Name[cy]=Gwybodaeth OGG +Name[da]=OGG-info +Name[de]=OGG-Info +Name[el]=Πληροφορίες OGG +Name[eo]=OGG-informo +Name[es]=Info OGG +Name[et]=OGG info +Name[eu]=OGG informazioa +Name[fa]=اطلاعات OGG +Name[fi]=OGG-tiedot +Name[fr]=Informations Ogg Vorbis +Name[gl]=Información OGG +Name[he]=מידע OGG +Name[hi]=OGG जानकारी +Name[hr]=Informacije o OGG datoteci +Name[hu]=OGG-jellemzők +Name[is]=OGG upplýsingar +Name[it]=Informazioni OGG +Name[ja]=OGG 情報 +Name[kk]=OGG мәліметі +Name[km]=ព័ត៌មាន OGG +Name[ko]=OGG 정보 +Name[lt]=OGG informacija +Name[mk]=OGG информации +Name[nb]=OGG informasjon +Name[nds]=Ogg-Info +Name[ne]=अग सूचना +Name[nl]=OGG-informatie +Name[nn]=OGG-info +Name[pa]=OGG ਜਾਣਕਾਰੀ +Name[pl]=Informacja o pliku OGG +Name[pt]=Informação do OGG +Name[pt_BR]=Informação sobre OGG +Name[ro]=Informaţii OGG +Name[ru]=Сведения о OGG +Name[se]=OGG-dieđut +Name[sl]=Podatki o OGG +Name[sr]=Информације о OGG-у +Name[sr@Latn]=Informacije o OGG-u +Name[sv]=Ogg-information +Name[ta]=OGG தகவல் +Name[tg]=OGG Ахборот +Name[th]=ข้อมูล OGG +Name[tr]=OGG Bilgisi +Name[uk]=Інформація по OGG +Name[uz]=OGG haqida maʼlumot +Name[uz@cyrillic]=OGG ҳақида маълумот +Name[xh]=OGG Ulwazi +Name[zh_CN]=OGG 信息 +Name[zh_HK]=OGG 資訊 +Name[zh_TW]=OGG 資訊 +Name[zu]=Ulwazi lwe OGG +ServiceTypes=KFilePlugin +X-TDE-Library=tdefile_ogg +MimeType=audio/vorbis +PreferredGroups=Comment,Technical +PreferredItems=Title,Artist,Album,Tracknumber,Genre,Bitrate,Length,Date,Description,Organization,Channels,UpperBitrate,LowerBitrate,NominalBitrate,Location,Copyright,Version diff --git a/tdefile-plugins/ogg/tdefile_ogg.h b/tdefile-plugins/ogg/tdefile_ogg.h new file mode 100644 index 00000000..0737dff7 --- /dev/null +++ b/tdefile-plugins/ogg/tdefile_ogg.h @@ -0,0 +1,46 @@ +/* This file is part of the KDE project + * Copyright (C) 2001, 2002 Rolf Magnus <ramagnus@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * $Id$ + */ + +#ifndef __KFILE_OGG_H__ +#define __KFILE_OGG_H__ + +#include <tdefilemetainfo.h> + +class TQString; +class TQStringList; + +class KOggPlugin: public KFilePlugin +{ + Q_OBJECT + + +public: + KOggPlugin( TQObject *parent, const char *name, const TQStringList& args ); + + virtual bool readInfo( KFileMetaInfo& info, uint what); + virtual bool writeInfo( const KFileMetaInfo& info ) const; + virtual TQValidator* createValidator( const TQString& mimetype, + const TQString &group, + const TQString &key, + TQObject* parent, const char* name) const; +}; + + +#endif diff --git a/tdefile-plugins/ogg/vcedit.c b/tdefile-plugins/ogg/vcedit.c new file mode 100644 index 00000000..76e31f6c --- /dev/null +++ b/tdefile-plugins/ogg/vcedit.c @@ -0,0 +1,331 @@ +/* This program is licensed under the GNU Library General Public License, version 2 + * + * (c) 2000-2001 Michael Smith <msmith@labyrinth.net.au> + * + * Modified by Warren Spits <spits@cyberdude.com> + * - Handles vorbis files that are truncated or missing an eos flag. + * + * Comment editing backend, suitable for use by nice frontend interfaces. + * + * last modified: $Id$ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ogg/ogg.h> +#include <vorbis/codec.h> + +#include "vcedit.h" + +#define CHUNKSIZE 4096 + +vcedit_state *vcedit_new_state(void) +{ + vcedit_state *state = malloc(sizeof(vcedit_state)); + memset(state, 0, sizeof(vcedit_state)); + + return state; +} + +char *vcedit_error(vcedit_state *state) +{ + return state->lasterror; +} + +vorbis_comment *vcedit_comments(vcedit_state *state) +{ + return state->vc; +} + +static void vcedit_clear_internals(vcedit_state *state) +{ + if(state->vc) + { + vorbis_comment_clear(state->vc); + free(state->vc); + state->vc=NULL; + } + if(state->os) + { + ogg_stream_clear(state->os); + free(state->os); + state->os=NULL; + } + if(state->oy) + { + ogg_sync_clear(state->oy); + free(state->oy); + state->oy=NULL; + } +} + +void vcedit_clear(vcedit_state *state) +{ + if(state) + { + vcedit_clear_internals(state); + free(state); + } +} + +int vcedit_open(vcedit_state *state, FILE *in) +{ + return vcedit_open_callbacks(state, (void *)in, + (vcedit_read_func)fread, (vcedit_write_func)fwrite); +} + +int vcedit_open_callbacks(vcedit_state *state, void *in, + vcedit_read_func read_func, vcedit_write_func write_func) +{ + + char *buffer; + int bytes,i; + ogg_packet *header; + ogg_packet header_main; + ogg_packet header_comments; + ogg_packet header_codebooks; + ogg_page og; + vorbis_info vi; + + + state->in = in; + state->read = read_func; + state->write = write_func; + state->lasterror = 0; + + state->oy = malloc(sizeof(ogg_sync_state)); + ogg_sync_init(state->oy); + + buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); + bytes = state->read(buffer, 1, CHUNKSIZE, state->in); + + ogg_sync_wrote(state->oy, bytes); + + if(ogg_sync_pageout(state->oy, &og) != 1) + { + if(bytes<CHUNKSIZE) + state->lasterror = "Input truncated or empty."; + else + state->lasterror = "Input is not an Ogg bitstream."; + goto err; + } + + state->serial = ogg_page_serialno(&og); + + state->os = malloc(sizeof(ogg_stream_state)); + ogg_stream_init(state->os, state->serial); + + vorbis_info_init(&vi); + + state->vc = malloc(sizeof(vorbis_comment)); + vorbis_comment_init(state->vc); + + if(ogg_stream_pagein(state->os, &og) < 0) + { + state->lasterror = "Error reading first page of Ogg bitstream."; + goto err; + } + + if(ogg_stream_packetout(state->os, &header_main) != 1) + { + state->lasterror = "Error reading initial header packet."; + goto err; + } + + if(vorbis_synthesis_headerin(&vi, state->vc, &header_main) < 0) + { + state->lasterror = "Ogg bitstream does not contain vorbis data."; + goto err; + } + + state->mainlen = header_main.bytes; + state->mainbuf = malloc(state->mainlen); + memcpy(state->mainbuf, header_main.packet, header_main.bytes); + + i = 0; + header = &header_comments; + while(i<2) { + while(i<2) { + int result = ogg_sync_pageout(state->oy, &og); + if(result == 0) break; /* Too little data so far */ + else if(result == 1) + { + ogg_stream_pagein(state->os, &og); + while(i<2) + { + result = ogg_stream_packetout(state->os, header); + if(result == 0) break; + if(result == -1) + { + state->lasterror = "Corrupt secondary header."; + goto err; + } + vorbis_synthesis_headerin(&vi, state->vc, header); + if(i==1) + { + state->booklen = header->bytes; + state->bookbuf = malloc(state->booklen); + memcpy(state->bookbuf, header->packet, + header->bytes); + } + i++; + header = &header_codebooks; + } + } + } + + buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); + bytes = state->read(buffer, 1, CHUNKSIZE, state->in); + if(bytes == 0 && i < 2) + { + state->lasterror = "EOF before end of vorbis headers."; + goto err; + } + ogg_sync_wrote(state->oy, bytes); + } + + /* Headers are done! */ + vorbis_info_clear(&vi); + return 0; + +err: + vcedit_clear_internals(state); + return -1; +} + +int vcedit_write(vcedit_state *state, void *out) +{ + ogg_stream_state streamout; + ogg_packet header_main; + ogg_packet header_comments; + ogg_packet header_codebooks; + + ogg_page ogout, ogin; + ogg_packet op; + int result, outresult; + char *buffer; + int bytes, eosin=0, eosout=0; + + state->lasterror = 0; + + header_main.bytes = state->mainlen; + header_main.packet = state->mainbuf; + header_main.b_o_s = 1; + header_main.e_o_s = 0; + header_main.granulepos = 0; + + header_codebooks.bytes = state->booklen; + header_codebooks.packet = state->bookbuf; + header_codebooks.b_o_s = 0; + header_codebooks.e_o_s = 0; + header_codebooks.granulepos = 0; + + ogg_stream_init(&streamout, state->serial); + + vorbis_commentheader_out(state->vc, &header_comments); + + ogg_stream_packetin(&streamout, &header_main); + ogg_stream_packetin(&streamout, &header_comments); + ogg_stream_packetin(&streamout, &header_codebooks); + + while((result = ogg_stream_flush(&streamout, &ogout))) + { + if(state->write(ogout.header,1,ogout.header_len, out) != + (size_t) ogout.header_len) + goto cleanup; + if(state->write(ogout.body,1,ogout.body_len, out) != + (size_t) ogout.body_len) + goto cleanup; + } + + /* We copy the first logical stream + * through, rewriting the stream. */ + while (1) + { + outresult = eosin ? ogg_stream_flush(&streamout, &ogout) : + ogg_stream_pageout(&streamout, &ogout); + if (outresult > 0) + { + if (state->write(ogout.header,1,ogout.header_len, + out) != (size_t) ogout.header_len) + goto cleanup; + if (state->write(ogout.body,1,ogout.body_len, + out) != (size_t) ogout.body_len) + goto cleanup; + if (ogg_page_eos(&ogout)) eosout = 1; + } + if (outresult != 0) continue; + if (eosout || (eosin && (result == 0))) break; + + while (1) + { + result = ogg_stream_packetout(state->os, &op); + if (result < 0) continue; + if (result > 0) ogg_stream_packetin(&streamout, &op); + if (eosin || (result > 0)) break; + + while (1) + { + result = ogg_sync_pageout(state->oy, &ogin); + + if (result < 0) continue; + if (result > 0) + { + ogg_stream_pagein(state->os, &ogin); + if (ogg_page_eos(&ogin)) eosin = 1; + } + if (eosin || (result > 0)) break; + + buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); + bytes = state->read(buffer,1, CHUNKSIZE, state->in); + ogg_sync_wrote(state->oy, bytes); + if (bytes < CHUNKSIZE) eosin = 1; + } + } + } + + eosin=0; /* clear it, because not all paths to here do */ + eosout=1; /* handle input files that are truncated or without an eos flag */ + + /* We copy the rest of the stream (other logical streams) + * through, a page at a time. */ + while (1) + { + result = ogg_sync_pageout(state->oy, &ogout); + if (result > 0) + { + if (state->write(ogout.header,1,ogout.header_len, + out) != (size_t) ogout.header_len) + goto cleanup; + if (state->write(ogout.body,1,ogout.body_len, out) != + (size_t) ogout.body_len) + goto cleanup; + } + if (result != 0) continue; + if (eosin) break; + + buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); + bytes = state->read(buffer,1, CHUNKSIZE, state->in); + ogg_sync_wrote(state->oy, bytes); + eosin = (bytes < CHUNKSIZE); + } + +cleanup: + ogg_stream_clear(&streamout); + ogg_packet_clear(&header_comments); + + free(state->mainbuf); + free(state->bookbuf); + + vcedit_clear_internals(state); + if(!(eosin && eosout)) + { + state->lasterror = + "Error writing stream to output. " + "Output stream may be corrupted or truncated."; + return -1; + } + + return 0; +} diff --git a/tdefile-plugins/ogg/vcedit.h b/tdefile-plugins/ogg/vcedit.h new file mode 100644 index 00000000..6be136ba --- /dev/null +++ b/tdefile-plugins/ogg/vcedit.h @@ -0,0 +1,56 @@ +/* This program is licensed under the GNU General Public License, version 2, + * a copy of which is included with this program. + * + * (c) 2000-2001 Michael Smith <msmith@labyrinth.net.au> + * + * VCEdit header. + * + * last modified: $ID:$ + */ + +#ifndef __VCEDIT_H +#define __VCEDIT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> +#include <ogg/ogg.h> +#include <vorbis/codec.h> + +typedef size_t (*vcedit_read_func)(void *, size_t, size_t, void *); +typedef size_t (*vcedit_write_func)(const void *, size_t, size_t, void *); + +typedef struct { + ogg_sync_state *oy; + ogg_stream_state *os; + + vorbis_comment *vc; + + vcedit_read_func read; + vcedit_write_func write; + + void *in; + long serial; + unsigned char *mainbuf; + unsigned char *bookbuf; + int mainlen; + int booklen; + char *lasterror; +} vcedit_state; + +extern vcedit_state * vcedit_new_state(void); +extern void vcedit_clear(vcedit_state *state); +extern vorbis_comment * vcedit_comments(vcedit_state *state); +extern int vcedit_open(vcedit_state *state, FILE *in); +extern int vcedit_open_callbacks(vcedit_state *state, void *in, + vcedit_read_func read_func, vcedit_write_func write_func); +extern int vcedit_write(vcedit_state *state, void *out); +extern char * vcedit_error(vcedit_state *state); + +#ifdef __cplusplus +} +#endif + +#endif /* __VCEDIT_H */ |