1 /***************************************************************************
2 copyright : (C) 2012 by Lukáš Lalinský
3 email : lalinsky@gmail.com
4
5 copyright : (C) 2002 - 2008 by Scott Wheeler
6 email : wheeler@kde.org
7 (original Vorbis implementation)
8 ***************************************************************************/
9
10 /***************************************************************************
11 * This library is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU Lesser General Public License version *
13 * 2.1 as published by the Free Software Foundation. *
14 * *
15 * This library is distributed in the hope that it will be useful, but *
16 * WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
18 * Lesser General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU Lesser General Public *
21 * License along with this library; if not, write to the Free Software *
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
23 * 02110-1301 USA *
24 * *
25 * Alternatively, this file is available under the Mozilla Public *
26 * License Version 1.1. You may obtain a copy of the License at *
27 * http://www.mozilla.org/MPL/ *
28 ***************************************************************************/
29
30 #include <tstring.h>
31 #include <tdebug.h>
32
33 #include <oggpageheader.h>
34
35 #include "opusproperties.h"
36 #include "opusfile.h"
37
38 using namespace TagLib;
39 using namespace TagLib::Ogg;
40
41 class Opus::Properties::PropertiesPrivate
42 {
43 public:
PropertiesPrivate()44 PropertiesPrivate() :
45 length(0),
46 bitrate(0),
47 inputSampleRate(0),
48 channels(0),
49 opusVersion(0) {}
50
51 int length;
52 int bitrate;
53 int inputSampleRate;
54 int channels;
55 int opusVersion;
56 };
57
58 ////////////////////////////////////////////////////////////////////////////////
59 // public members
60 ////////////////////////////////////////////////////////////////////////////////
61
Properties(File * file,ReadStyle style)62 Opus::Properties::Properties(File *file, ReadStyle style) :
63 AudioProperties(style),
64 d(new PropertiesPrivate())
65 {
66 read(file);
67 }
68
~Properties()69 Opus::Properties::~Properties()
70 {
71 delete d;
72 }
73
length() const74 int Opus::Properties::length() const
75 {
76 return lengthInSeconds();
77 }
78
lengthInSeconds() const79 int Ogg::Opus::Properties::lengthInSeconds() const
80 {
81 return d->length / 1000;
82 }
83
lengthInMilliseconds() const84 int Ogg::Opus::Properties::lengthInMilliseconds() const
85 {
86 return d->length;
87 }
88
bitrate() const89 int Opus::Properties::bitrate() const
90 {
91 return d->bitrate;
92 }
93
sampleRate() const94 int Opus::Properties::sampleRate() const
95 {
96 // Opus can decode any stream at a sample rate of 8, 12, 16, 24, or 48 kHz,
97 // so there is no single sample rate. Let's assume it's the highest
98 // possible.
99 return 48000;
100 }
101
channels() const102 int Opus::Properties::channels() const
103 {
104 return d->channels;
105 }
106
inputSampleRate() const107 int Opus::Properties::inputSampleRate() const
108 {
109 return d->inputSampleRate;
110 }
111
opusVersion() const112 int Opus::Properties::opusVersion() const
113 {
114 return d->opusVersion;
115 }
116
117 ////////////////////////////////////////////////////////////////////////////////
118 // private members
119 ////////////////////////////////////////////////////////////////////////////////
120
read(File * file)121 void Opus::Properties::read(File *file)
122 {
123 // Get the identification header from the Ogg implementation.
124
125 // http://tools.ietf.org/html/draft-terriberry-oggopus-01#section-5.1
126
127 const ByteVector data = file->packet(0);
128
129 // *Magic Signature*
130 unsigned int pos = 8;
131
132 // *Version* (8 bits, unsigned)
133 d->opusVersion = static_cast<unsigned char>(data.at(pos));
134 pos += 1;
135
136 // *Output Channel Count* 'C' (8 bits, unsigned)
137 d->channels = static_cast<unsigned char>(data.at(pos));
138 pos += 1;
139
140 // *Pre-skip* (16 bits, unsigned, little endian)
141 const unsigned short preSkip = data.toUShort(pos, false);
142 pos += 2;
143
144 // *Input Sample Rate* (32 bits, unsigned, little endian)
145 d->inputSampleRate = data.toUInt(pos, false);
146 pos += 4;
147
148 // *Output Gain* (16 bits, signed, little endian)
149 pos += 2;
150
151 // *Channel Mapping Family* (8 bits, unsigned)
152 pos += 1;
153
154 const Ogg::PageHeader *first = file->firstPageHeader();
155 const Ogg::PageHeader *last = file->lastPageHeader();
156
157 if(first && last) {
158 const long long start = first->absoluteGranularPosition();
159 const long long end = last->absoluteGranularPosition();
160
161 if(start >= 0 && end >= 0) {
162 const long long frameCount = (end - start - preSkip);
163
164 if(frameCount > 0) {
165 const double length = frameCount * 1000.0 / 48000.0;
166 d->length = static_cast<int>(length + 0.5);
167 d->bitrate = static_cast<int>(file->length() * 8.0 / length + 0.5);
168 }
169 }
170 else {
171 debug("Opus::Properties::read() -- The PCM values for the start or "
172 "end of this file was incorrect.");
173 }
174 }
175 else
176 debug("Opus::Properties::read() -- Could not find valid first and last Ogg pages.");
177 }
178