1 /*
2     SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 #include "k3boggvorbisdecoder.h"
7 #include "k3bplugin_i18n.h"
8 
9 #include <config-k3b.h>
10 
11 #include <QDebug>
12 #include <QFile>
13 #include <QStringList>
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <vorbis/codec.h>
18 #include <vorbis/vorbisfile.h>
19 
20 K_PLUGIN_CLASS_WITH_JSON(K3bOggVorbisDecoderFactory, "k3boggvorbisdecoder.json")
21 
22 class K3bOggVorbisDecoder::Private
23 {
24 public:
Private()25     Private()
26         : vInfo(0),
27           vComment(0),
28           isOpen(false) {
29     }
30 
31     OggVorbis_File oggVorbisFile;
32     vorbis_info* vInfo;
33     vorbis_comment* vComment;
34     bool isOpen;
35 };
36 
37 
K3bOggVorbisDecoder(QObject * parent)38 K3bOggVorbisDecoder::K3bOggVorbisDecoder( QObject* parent )
39     : K3b::AudioDecoder( parent )
40 {
41     d = new Private();
42 }
43 
44 
~K3bOggVorbisDecoder()45 K3bOggVorbisDecoder::~K3bOggVorbisDecoder()
46 {
47     delete d;
48 }
49 
50 
openOggVorbisFile()51 bool K3bOggVorbisDecoder::openOggVorbisFile()
52 {
53     if( !d->isOpen ) {
54         FILE* file = fopen( QFile::encodeName(filename()), "r" );
55         if( !file ) {
56             qDebug() << "(K3bOggVorbisDecoder) Could not open file " << filename();
57             return false;
58         }
59         else if( ov_open( file, &d->oggVorbisFile, 0, 0 ) ) {
60             qDebug() << "(K3bOggVorbisDecoder) " << filename()
61                      << " seems not to to be an ogg vorbis file." << endl;
62             fclose( file );
63             return false;
64         }
65     }
66 
67     d->isOpen = true;
68     return true;
69 }
70 
71 
analyseFileInternal(K3b::Msf & frames,int & samplerate,int & ch)72 bool K3bOggVorbisDecoder::analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch )
73 {
74     cleanup();
75 
76     if( openOggVorbisFile() ) {
77         // check length of track
78         double seconds = ov_time_total( &d->oggVorbisFile, -1 );
79         if( seconds == OV_EINVAL ) {
80             qDebug() << "(K3bOggVorbisDecoder) Could not determine length of file " << filename();
81             cleanup();
82             return false;
83         }
84         else {
85 
86             d->vInfo = ov_info( &d->oggVorbisFile, -1 /* current bitstream */ );
87             d->vComment = ov_comment( &d->oggVorbisFile, -1 );
88 
89             // add meta tags
90             for( int i = 0; i < d->vComment->comments; ++i ) {
91                 QString comment = QString::fromUtf8( d->vComment->user_comments[i] );
92                 QStringList values = comment.split( '=' );
93                 if( values.count() > 1 ) {
94                     if( values[0].toLower() == "title" )
95                         addMetaInfo( META_TITLE, values[1] );
96                     else if( values[0].toLower() == "artist" )
97                         addMetaInfo( META_ARTIST, values[1] );
98                     else if( values[0].toLower() == "description" )
99                         addMetaInfo( META_COMMENT, values[1] );
100                 }
101             }
102 
103 
104             // add technical infos
105             addTechnicalInfo( i18n("Version"), QString::number(d->vInfo->version) );
106             addTechnicalInfo( i18n("Channels"), QString::number(d->vInfo->channels) );
107             addTechnicalInfo( i18n("Sampling Rate"), i18n("%1 Hz",d->vInfo->rate) );
108             if( d->vInfo->bitrate_upper > 0 )
109                 addTechnicalInfo( i18n("Bitrate Upper"), i18n( "%1 bps" ,d->vInfo->bitrate_upper) );
110             if( d->vInfo->bitrate_nominal > 0 )
111                 addTechnicalInfo( i18n("Bitrate Nominal"), i18n( "%1 bps" ,d->vInfo->bitrate_nominal) );
112             if( d->vInfo->bitrate_lower > 0 )
113                 addTechnicalInfo( i18n("Bitrate Lower"), i18n( "%1 bps",d->vInfo->bitrate_lower) );
114 
115             frames = K3b::Msf::fromSeconds(seconds);
116             samplerate = d->vInfo->rate;
117             ch = d->vInfo->channels;
118 
119             cleanup();
120 
121             return true;
122         }
123     }
124     else
125         return false;
126 }
127 
128 
initDecoderInternal()129 bool K3bOggVorbisDecoder::initDecoderInternal()
130 {
131     cleanup();
132     return openOggVorbisFile();
133 }
134 
135 
decodeInternal(char * data,int maxLen)136 int K3bOggVorbisDecoder::decodeInternal( char* data, int maxLen )
137 {
138     int bitStream = 0;
139     long bytesRead = ov_read( &d->oggVorbisFile,
140                               data,
141                               maxLen,  // max length to be read
142                               1,                   // big endian
143                               2,                   // word size: 16-bit samples
144                               1,                   // signed
145                               &bitStream );        // current bitstream
146 
147     if( bitStream != 0 ) {
148         qDebug() << "(K3bOggVorbisDecoder) bitstream != 0. Multiple bitstreams not supported.";
149         return -1;
150     }
151 
152     else if( bytesRead == OV_HOLE ) {
153         qDebug() << "(K3bOggVorbisDecoder) OV_HOLE";
154         // recursive new try
155         return decodeInternal( data, maxLen );
156     }
157 
158     else if( bytesRead < 0 ) {
159         qDebug() << "(K3bOggVorbisDecoder) Error: " << bytesRead;
160         return -1;
161     }
162 
163     else if( bytesRead == 0 ) {
164         qDebug() << "(K3bOggVorbisDecoder) successfully finished decoding.";
165         return 0;
166     }
167 
168     else {
169         return bytesRead;
170     }
171 }
172 
173 
cleanup()174 void K3bOggVorbisDecoder::cleanup()
175 {
176     if( d->isOpen )
177         ov_clear( &d->oggVorbisFile );
178     d->isOpen = false;
179     d->vComment = 0;
180     d->vInfo = 0;
181 }
182 
183 
seekInternal(const K3b::Msf & pos)184 bool K3bOggVorbisDecoder::seekInternal( const K3b::Msf& pos )
185 {
186     return ( ov_pcm_seek( &d->oggVorbisFile, pos.pcmSamples() ) == 0 );
187 }
188 
189 
fileType() const190 QString K3bOggVorbisDecoder::fileType() const
191 {
192     return i18n("Ogg-Vorbis");
193 }
194 
195 
K3bOggVorbisDecoderFactory(QObject * parent,const QVariantList &)196 K3bOggVorbisDecoderFactory::K3bOggVorbisDecoderFactory( QObject* parent, const QVariantList& )
197     : K3b::AudioDecoderFactory( parent )
198 {
199 }
200 
201 
~K3bOggVorbisDecoderFactory()202 K3bOggVorbisDecoderFactory::~K3bOggVorbisDecoderFactory()
203 {
204 }
205 
206 
createDecoder(QObject * parent) const207 K3b::AudioDecoder* K3bOggVorbisDecoderFactory::createDecoder( QObject* parent ) const
208 {
209     return new K3bOggVorbisDecoder( parent );
210 }
211 
212 
canDecode(const QUrl & url)213 bool K3bOggVorbisDecoderFactory::canDecode( const QUrl& url )
214 {
215     FILE* file = fopen( QFile::encodeName(url.toLocalFile()), "r" );
216     if( !file ) {
217         qDebug() << "(K3bOggVorbisDecoder) Could not open file " << url.toLocalFile();
218         return false;
219     }
220 
221     OggVorbis_File of;
222 
223     if( ov_open( file, &of, 0, 0 ) ) {
224         fclose( file );
225         qDebug() << "(K3bOggVorbisDecoder) not an Ogg-Vorbis file: " << url.toLocalFile();
226         return false;
227     }
228 
229     ov_clear( &of );
230 
231     return true;
232 }
233 
234 
235 #include "k3boggvorbisdecoder.moc"
236