summaryrefslogtreecommitdiffstats
path: root/tdefile-plugins/ogg
diff options
context:
space:
mode:
Diffstat (limited to 'tdefile-plugins/ogg')
-rw-r--r--tdefile-plugins/ogg/Makefile.am22
-rw-r--r--tdefile-plugins/ogg/configure.in.in1
-rw-r--r--tdefile-plugins/ogg/tdefile_ogg.cpp357
-rw-r--r--tdefile-plugins/ogg/tdefile_ogg.desktop68
-rw-r--r--tdefile-plugins/ogg/tdefile_ogg.h46
-rw-r--r--tdefile-plugins/ogg/vcedit.c331
-rw-r--r--tdefile-plugins/ogg/vcedit.h56
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 */