kwave  18.07.70
OggDecoder.cpp
Go to the documentation of this file.
1 /*************************************************************************
2  OggDecoder.cpp - decoder for Ogg/Vorbis data
3  -------------------
4  begin : Tue Sep 10 2002
5  copyright : (C) 2002 by Thomas Eschenbacher
6  email : Thomas.Eschenbacher@gmx.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "config.h"
19 
20 #include <math.h>
21 #include <stdlib.h>
22 
23 #include <QDate>
24 #include <QLatin1Char>
25 
26 #include <KLocalizedString>
27 
28 #include "libkwave/Compression.h"
29 #include "libkwave/MessageBox.h"
30 #include "libkwave/MultiWriter.h"
31 #include "libkwave/Sample.h"
32 #include "libkwave/SampleArray.h"
33 #include "libkwave/String.h"
34 #include "libkwave/Utils.h"
35 #include "libkwave/Writer.h"
36 
37 #include "OggCodecPlugin.h"
38 #include "OggDecoder.h"
39 #include "OggSubDecoder.h"
40 #include "OpusDecoder.h"
41 #include "VorbisDecoder.h"
42 
43 //***************************************************************************
45  :Kwave::Decoder(), m_sub_decoder(Q_NULLPTR), m_source(Q_NULLPTR)
46 {
47 #ifdef HAVE_OGG_OPUS
50 #endif /* HAVE_OGG_OPUS */
51 
52 #ifdef HAVE_OGG_VORBIS
55 #endif /* HAVE_OGG_VORBIS */
56 
57  /* Ogg audio, as per RFC5334 */
58  addMimeType("audio/ogg", i18n("Ogg audio"), "*.oga");
59  addMimeType("application/ogg", i18n("Ogg audio"), "*.ogx");
60 
61 }
62 
63 //***************************************************************************
65 {
66  if (m_source) close();
67 }
68 
69 //***************************************************************************
71 {
72  return new Kwave::OggDecoder();
73 }
74 
75 //***************************************************************************
76 int Kwave::OggDecoder::parseHeader(QWidget *widget)
77 {
78  // grab some data at the head of the stream. We want the first page
79  // (which is guaranteed to be small and only contain the Vorbis
80  // stream initial header) We need the first page to get the stream
81  // serialno.
82 
83  // submit a 4k block to libvorbis' Ogg layer
84  char *buffer = ogg_sync_buffer(&m_oy, 4096);
85  Q_ASSERT(buffer);
86  if (!buffer) return -1;
87 
88  long int bytes = static_cast<long int>(m_source->read(buffer, 4096));
89  if ((bytes <= 0) && (!m_source->pos())) {
90  Kwave::MessageBox::error(widget, i18n(
91  "Ogg bitstream has zero-length."));
92  return -1;
93  }
94  ogg_sync_wrote(&m_oy, bytes);
95 
96  // Get the first page.
97  if (ogg_sync_pageout(&m_oy, &m_og) != 1) {
98  // have we simply run out of data? If so, we're done.
99  if (bytes < 4096) return 0;
100 
101  // error case. seems not be Vorbis data
102  Kwave::MessageBox::error(widget, i18n(
103  "Input does not appear to be an Ogg bitstream."));
104  return -1;
105  }
106 
107  // Get the serial number and set up the rest of decode.
108  // serialno first; use it to set up a logical stream
109  ogg_stream_init(&m_os, ogg_page_serialno(&m_og));
110 
111  // get the first packet
112  if (ogg_stream_pagein(&m_os, &m_og) < 0) {
113  // error; stream version mismatch perhaps
114  Kwave::MessageBox::error(widget, i18n(
115  "Error reading first page of the Ogg bitstream data."));
116  return -1;
117  }
118 
119  if ((ogg_stream_packetout(&m_os, &m_op) != 1) || (m_op.bytes < 8)) {
120  // no page? must not be vorbis
121  Kwave::MessageBox::error(widget, i18n(
122  "Error reading initial header packet."));
123  return -1;
124  }
125 
126  // get rid of the previous sub decoder
127  if (m_sub_decoder) {
128  delete m_sub_decoder;
129  m_sub_decoder = Q_NULLPTR;
130  }
131 
132  Kwave::FileInfo info(metaData());
133 
134  // ---------------------------------
135  // auto-detect the sub decoder
136 #ifdef HAVE_OGG_OPUS
137  if (memcmp(m_op.packet, "OpusHead", 8) == 0) {
138  qDebug(" OggDecoder: detected Opus codec");
139  m_sub_decoder =
141  info.set(Kwave::INF_MIMETYPE, _("audio/opus"));
142  }
143 #endif /* HAVE_OGG_OPUS */
144 #ifdef HAVE_OGG_VORBIS
145  if (memcmp(m_op.packet + 1, "vorbis", 6) == 0) {
146  qDebug(" OggDecoder: detected Vorbis codec");
147  m_sub_decoder =
149  info.set(Kwave::INF_MIMETYPE, _("audio/x-vorbis+ogg"));
150  }
151 #endif /* HAVE_OGG_VORBIS */
152 
153  if (!m_sub_decoder) {
154  qDebug("--- dump of the first 8 bytes of the packet: ---");
155  for (unsigned int i = 0; i < 8; i++)
156  qDebug("%2d: 0x%02X - '%c'", i, m_op.packet[i], m_op.packet[i]);
157 
158  Kwave::MessageBox::error(widget, i18n(
159  "Error: Codec not supported"));
160  return -1;
161  }
162 
163  info.setLength(0); // use streaming
164  info.setBits(SAMPLE_BITS); // use Kwave's internal resolution
165  if (m_sub_decoder->open(widget, info) < 0)
166  return -1;
167 
169  return 1;
170 }
171 
172 //***************************************************************************
173 bool Kwave::OggDecoder::open(QWidget *widget, QIODevice &src)
174 {
175  metaData().clear();
176  Q_ASSERT(!m_source);
177  if (m_source) qWarning("OggDecoder::open(), already open !");
178 
179  // try to open the source
180  if (!src.open(QIODevice::ReadOnly)) {
181  qWarning("failed to open source !");
182  return false;
183  }
184 
185  // take over the source
186  m_source = &src;
187 
188  /********** Decode setup ************/
189  qDebug("--- OggDecoder::open() ---");
190  ogg_sync_init(&m_oy); // Now we can read pages
191 
192  // read the header the first time
193  if (parseHeader(widget) < 0)
194  return false;
195 
196  return true;
197 }
198 
199 //***************************************************************************
201 {
202  int eos = 0;
203 
204  Q_ASSERT(m_source);
205  Q_ASSERT(m_sub_decoder);
206  if (!m_source || !m_sub_decoder) return false;
207 
208  // we repeat if the bitstream is chained
209  while (!dst.isCanceled()) {
210  // The rest is just a straight decode loop until end of stream
211  while (!eos) {
212  while (!eos) {
213  int result = ogg_sync_pageout(&m_oy, &m_og);
214  if (result == 0) break; // need more data
215  if (result < 0) {
216  // missing or corrupt data at this page position
217  Kwave::MessageBox::error(widget, i18n(
218  "Corrupt or missing data in bitstream. Continuing."
219  ));
220  } else {
221  // can safely ignore errors at this point
222  ogg_stream_pagein(&m_os, &m_og);
223  while (1) {
224  result = ogg_stream_packetout(&m_os, &m_op);
225 
226  if (result == 0) break; // need more data
227  if (result < 0) {
228  // missing or corrupt data at this page position
229  // no reason to complain; already complained above
230  } else {
231  result = m_sub_decoder->decode(dst);
232  if (result < 0)
233  break;
234 
235  // signal the current position
236  emit sourceProcessed(m_source->pos());
237  }
238  }
239  if (ogg_page_eos(&m_og) || dst.isCanceled()) eos = 1;
240  }
241  }
242 
243  if (!eos) {
244  char *buffer = ogg_sync_buffer(&m_oy, 4096);
245  int bytes = Kwave::toInt(m_source->read(buffer, 4096));
246  ogg_sync_wrote(&m_oy, bytes);
247  if (!bytes) eos = 1;
248  }
249  }
250 
251  // clean up this logical bitstream; before exit we see if we're
252  // followed by another [chained]
253  ogg_stream_clear(&m_os);
254  m_sub_decoder->reset();
255 
256  // parse the next header, maybe we parse a stream or chain...
257  if (eos || (parseHeader(widget) < 1)) break;
258  }
259 
260  // OK, clean up the framer
261  ogg_sync_clear(&m_oy);
262 
263  // signal the current position
264  emit sourceProcessed(m_source->pos());
265 
266  Kwave::FileInfo info(metaData());
267  m_sub_decoder->close(info);
269 
270  // return with a valid Signal, even if the user pressed cancel !
271  return true;
272 }
273 
274 //***************************************************************************
276 {
277  m_source = Q_NULLPTR;
278  delete m_sub_decoder;
279  m_sub_decoder = Q_NULLPTR;
280 }
281 
282 //***************************************************************************
283 //***************************************************************************
Definition: App.h:33
virtual void reset()=0
virtual bool decode(QWidget *widget, Kwave::MultiWriter &dst) Q_DECL_OVERRIDE
Definition: OggDecoder.cpp:200
virtual bool open(QWidget *widget, QIODevice &source) Q_DECL_OVERRIDE
Definition: OggDecoder.cpp:173
virtual ~OggDecoder() Q_DECL_OVERRIDE
Definition: OggDecoder.cpp:64
virtual Kwave::Decoder * instance() Q_DECL_OVERRIDE
Definition: OggDecoder.cpp:70
void sourceProcessed(quint64 pos)
ogg_sync_state m_oy
Definition: OggDecoder.h:87
void set(FileProperty key, const QVariant &value)
Definition: FileInfo.cpp:363
QIODevice * m_source
Definition: OggDecoder.h:84
virtual void addMimeType(const char *name, const QString &description, const char *patterns)
Definition: CodecBase.cpp:47
#define REGISTER_OGG_VORBIS_MIME_TYPES
int parseHeader(QWidget *widget)
Definition: OggDecoder.cpp:76
static int error(QWidget *widget, QString message, QString caption=QString())
Definition: MessageBox.cpp:126
#define REGISTER_COMPRESSION_TYPE_OGG_OPUS
virtual int decode(Kwave::MultiWriter &dst)=0
int toInt(T x)
Definition: Utils.h:127
void setLength(sample_index_t length)
Definition: FileInfo.cpp:409
virtual void close(Kwave::FileInfo &info)=0
virtual void replace(const MetaDataList &list)
virtual Kwave::MetaDataList & metaData()
Definition: Decoder.h:78
Kwave::OggSubDecoder * m_sub_decoder
Definition: OggDecoder.h:81
bool isCanceled() const
Definition: MultiWriter.h:64
void setBits(unsigned int bits)
Definition: FileInfo.cpp:439
#define REGISTER_OGG_OPUS_MIME_TYPES
virtual int open(QWidget *widget, Kwave::FileInfo &info)=0
#define REGISTER_COMPRESSION_TYPE_OGG_VORBIS
#define _(m)
Definition: memcpy.c:66
ogg_stream_state m_os
Definition: OggDecoder.h:90
#define SAMPLE_BITS
Definition: Sample.h:43
ogg_packet m_op
Definition: OggDecoder.h:96
virtual void close() Q_DECL_OVERRIDE
Definition: OggDecoder.cpp:275