OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/audio/sounds/wav_audio_handler.h" | 5 #include "media/audio/sounds/wav_audio_handler.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cstring> | 8 #include <cstring> |
9 | 9 |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/sys_byteorder.h" | 11 #include "base/sys_byteorder.h" |
12 #include "media/base/audio_bus.h" | 12 #include "media/base/audio_bus.h" |
13 | 13 |
14 namespace media { | |
14 namespace { | 15 namespace { |
15 | 16 |
16 const char kChunkId[] = "RIFF"; | 17 const char kChunkId[] = "RIFF"; |
17 const char kFormat[] = "WAVE"; | 18 const char kFormat[] = "WAVE"; |
18 const char kSubchunk1Id[] = "fmt "; | 19 const char kFmtSubchunkId[] = "fmt "; |
19 const char kSubchunk2Id[] = "data"; | 20 const char kDataSubchunkId[] = "data"; |
20 | 21 |
21 // The size of the header of a wav file. The header consists of 'RIFF', 4 bytes | 22 // The size of the header of a wav file. The header consists of 'RIFF', 4 bytes |
22 // of total data length, and 'WAVE'. | 23 // of total data length, and 'WAVE'. |
23 const size_t kWavFileHeaderSize = 12; | 24 const size_t kWavFileHeaderSize = 12; |
24 | 25 |
25 // The size of a chunk header in wav file format. A chunk header consists of a | 26 // The size of a chunk header in wav file format. A chunk header consists of a |
26 // tag ('fmt ' or 'data') and 4 bytes of chunk length. | 27 // tag ('fmt ' or 'data') and 4 bytes of chunk length. |
27 const size_t kChunkHeaderSize = 8; | 28 const size_t kChunkHeaderSize = 8; |
28 | 29 |
29 // The minimum size of 'fmt' chunk. | 30 // The minimum size of 'fmt' chunk. |
30 const size_t kFmtChunkMinimumSize = 16; | 31 const size_t kFmtChunkMinimumSize = 16; |
31 | 32 |
32 // The offsets of 'fmt' fields. | 33 // The offsets of 'fmt' fields. |
33 const size_t kAudioFormatOffset = 0; | 34 const size_t kAudioFormatOffset = 0; |
34 const size_t kChannelOffset = 2; | 35 const size_t kChannelOffset = 2; |
35 const size_t kSampleRateOffset = 4; | 36 const size_t kSampleRateOffset = 4; |
36 const size_t kBitsPerSampleOffset = 14; | 37 const size_t kBitsPerSampleOffset = 14; |
37 | 38 |
38 // Some constants for audio format. | 39 // Some constants for audio format. |
39 const int kAudioFormatPCM = 1; | 40 const int kAudioFormatPCM = 1; |
40 | 41 |
42 // A convenience struct for passing WAV parameters around. AudioParameters is | |
43 // too heavyweight for this. Keep this class internal to this implementation. | |
44 struct WavAudioParameters { | |
45 int audio_format; | |
46 uint16_t num_channels; | |
47 uint32_t sample_rate; | |
48 uint16_t bits_per_sample; | |
49 }; | |
50 | |
51 bool ParamsAreValid(const WavAudioParameters& params) { | |
52 return (params.audio_format == kAudioFormatPCM && params.num_channels != 0u && | |
53 params.sample_rate != 0u && params.bits_per_sample != 0u); | |
54 } | |
55 | |
41 // Reads an integer from |data| with |offset|. | 56 // Reads an integer from |data| with |offset|. |
42 template <typename T> | 57 template <typename T> |
43 T ReadInt(const base::StringPiece& data, size_t offset) { | 58 T ReadInt(const base::StringPiece& data, size_t offset) { |
44 CHECK_LE(offset + sizeof(T), data.size()); | 59 CHECK_LE(offset + sizeof(T), data.size()); |
45 T result; | 60 T result; |
46 memcpy(&result, data.data() + offset, sizeof(T)); | 61 memcpy(&result, data.data() + offset, sizeof(T)); |
47 #if !defined(ARCH_CPU_LITTLE_ENDIAN) | 62 #if !defined(ARCH_CPU_LITTLE_ENDIAN) |
48 result = base::ByteSwap(result); | 63 result = base::ByteSwap(result); |
49 #endif | 64 #endif |
50 return result; | 65 return result; |
51 } | 66 } |
52 | 67 |
68 // Parse a "fmt " chunk from wav data into its parameters. | |
69 bool ParseFmtChunk(const base::StringPiece& data, WavAudioParameters* params) { | |
70 DCHECK(params); | |
71 | |
72 // If the chunk is too small, return false. | |
73 if (data.size() < kFmtChunkMinimumSize) { | |
74 DLOG(ERROR) << "Data size " << data.size() << " is too short."; | |
wzhong
2015/11/18 22:21:35
Why DLOG?
slan
2015/11/19 00:19:44
Done.
| |
75 return false; | |
76 } | |
77 | |
78 // Read in serialized parameters. | |
79 params->audio_format = ReadInt<uint16>(data, kAudioFormatOffset); | |
80 params->num_channels = ReadInt<uint16>(data, kChannelOffset); | |
81 params->sample_rate = ReadInt<uint32>(data, kSampleRateOffset); | |
82 params->bits_per_sample = ReadInt<uint16>(data, kBitsPerSampleOffset); | |
83 return true; | |
84 } | |
85 | |
86 bool ParseWavData(const base::StringPiece& wav_data, | |
87 base::StringPiece* data_out, | |
88 WavAudioParameters* params_out) { | |
89 DCHECK(data_out); | |
90 DCHECK(params_out); | |
91 | |
92 // The data is not long enough to contain a header. | |
93 if (wav_data.size() < kWavFileHeaderSize) { | |
94 LOG(ERROR) << "wav_data is too small"; | |
95 return false; | |
96 } | |
97 | |
98 // The header should look like: |R|I|F|F|1|2|3|4|W|A|V|E| | |
99 if (!wav_data.starts_with(kChunkId) || | |
100 memcmp(wav_data.data() + 8, kFormat, 4) != 0) { | |
101 LOG(ERROR) << "incorrect wav header"; | |
102 return false; | |
103 } | |
104 uint32_t total_length = std::min(ReadInt<uint32_t>(wav_data, 4) + 8, | |
105 static_cast<uint32_t>(wav_data.size())); | |
106 | |
107 uint32_t offset = kWavFileHeaderSize; | |
108 while (offset < total_length) { | |
109 // This is just junk left at the end. Break. | |
110 if (total_length - offset < kChunkHeaderSize) | |
111 break; | |
112 | |
113 // We should be at the beginning of a subsection. The next 8 bytes should | |
114 // look like: "|f|m|t| |1|2|3|4|" or "|d|a|t|a|1|2|3|4|". | |
115 base::StringPiece chunk_header = wav_data.substr(offset, kChunkHeaderSize); | |
116 uint32_t chunk_length = ReadInt<uint32_t>(chunk_header, 4); | |
117 base::StringPiece chunk_payload = | |
118 wav_data.substr(offset + kChunkHeaderSize, chunk_length); | |
119 | |
120 if (chunk_header.starts_with(kFmtSubchunkId)) { | |
121 if (!ParseFmtChunk(chunk_payload, params_out)) | |
122 return false; | |
123 } else if (chunk_header.starts_with(kDataSubchunkId)) { | |
124 *data_out = chunk_payload; | |
125 } else { | |
126 DVLOG(1) << "Skipping unknown data chunk: " << chunk_header.substr(0, 4) | |
127 << "."; | |
128 } | |
129 | |
130 offset += kChunkHeaderSize + chunk_length; | |
131 } | |
132 | |
133 // Check that data format is valid. | |
134 if (!ParamsAreValid(*params_out)) { | |
135 LOG(ERROR) << "Format is invalid. " | |
136 << "num_channels: " << params_out->num_channels << " " | |
137 << "sample_rate: " << params_out->sample_rate << " " | |
138 << "bits_per_sample: " << params_out->bits_per_sample; | |
139 return false; | |
140 } | |
141 return true; | |
142 } | |
143 | |
53 } // namespace | 144 } // namespace |
54 | 145 |
55 namespace media { | 146 WavAudioHandler::WavAudioHandler(const base::StringPiece& audio_data, |
56 | 147 uint16_t num_channels, |
57 WavAudioHandler::WavAudioHandler(const base::StringPiece& wav_data) | 148 uint32_t sample_rate, |
58 : num_channels_(0), sample_rate_(0), bits_per_sample_(0), total_frames_(0) { | 149 uint16_t bits_per_sample) |
59 CHECK_LE(kWavFileHeaderSize, wav_data.size()) << "wav data is too small"; | 150 : data_(audio_data), |
60 CHECK(wav_data.starts_with(kChunkId) && | 151 num_channels_(num_channels), |
61 memcmp(wav_data.data() + 8, kFormat, 4) == 0) | 152 sample_rate_(sample_rate), |
62 << "incorrect wav header"; | 153 bits_per_sample_(bits_per_sample) { |
63 | 154 DCHECK_NE(num_channels_, 0u); |
64 uint32 total_length = std::min(ReadInt<uint32>(wav_data, 4), | 155 DCHECK_NE(sample_rate_, 0u); |
65 static_cast<uint32>(wav_data.size())); | 156 DCHECK_NE(bits_per_sample_, 0u); |
66 uint32 offset = kWavFileHeaderSize; | |
67 while (offset < total_length) { | |
68 const int length = ParseSubChunk(wav_data.substr(offset)); | |
69 CHECK_LE(0, length) << "can't parse wav sub-chunk"; | |
70 offset += length; | |
71 } | |
72 | |
73 total_frames_ = data_.size() * 8 / num_channels_ / bits_per_sample_; | 157 total_frames_ = data_.size() * 8 / num_channels_ / bits_per_sample_; |
74 } | 158 } |
75 | 159 |
76 WavAudioHandler::~WavAudioHandler() {} | 160 WavAudioHandler::~WavAudioHandler() {} |
77 | 161 |
162 // static | |
163 scoped_ptr<WavAudioHandler> WavAudioHandler::Create( | |
164 const base::StringPiece& wav_data) { | |
165 WavAudioParameters params; | |
166 base::StringPiece audio_data; | |
167 | |
168 // Attempt to parse the WAV data. | |
169 if (!ParseWavData(wav_data, &audio_data, ¶ms)) | |
170 return scoped_ptr<WavAudioHandler>(); | |
171 | |
172 return make_scoped_ptr(new WavAudioHandler(audio_data, | |
173 params.num_channels, | |
174 params.sample_rate, | |
175 params.bits_per_sample)); | |
176 } | |
177 | |
78 bool WavAudioHandler::AtEnd(size_t cursor) const { | 178 bool WavAudioHandler::AtEnd(size_t cursor) const { |
79 return data_.size() <= cursor; | 179 return data_.size() <= cursor; |
80 } | 180 } |
81 | 181 |
82 bool WavAudioHandler::CopyTo(AudioBus* bus, | 182 bool WavAudioHandler::CopyTo(AudioBus* bus, |
83 size_t cursor, | 183 size_t cursor, |
84 size_t* bytes_written) const { | 184 size_t* bytes_written) const { |
85 if (!bus) | 185 if (!bus) |
86 return false; | 186 return false; |
87 if (bus->channels() != num_channels_) { | 187 if (bus->channels() != num_channels_) { |
(...skipping 12 matching lines...) Expand all Loading... | |
100 *bytes_written = frames * bytes_per_frame; | 200 *bytes_written = frames * bytes_per_frame; |
101 bus->ZeroFramesPartial(frames, bus->frames() - frames); | 201 bus->ZeroFramesPartial(frames, bus->frames() - frames); |
102 return true; | 202 return true; |
103 } | 203 } |
104 | 204 |
105 base::TimeDelta WavAudioHandler::GetDuration() const { | 205 base::TimeDelta WavAudioHandler::GetDuration() const { |
106 return base::TimeDelta::FromSecondsD(total_frames_ / | 206 return base::TimeDelta::FromSecondsD(total_frames_ / |
107 static_cast<double>(sample_rate_)); | 207 static_cast<double>(sample_rate_)); |
108 } | 208 } |
109 | 209 |
110 int WavAudioHandler::ParseSubChunk(const base::StringPiece& data) { | |
111 if (data.size() < kChunkHeaderSize) | |
112 return data.size(); | |
113 uint32 chunk_length = ReadInt<uint32>(data, 4); | |
114 if (data.starts_with(kSubchunk1Id)) { | |
115 if (!ParseFmtChunk(data.substr(kChunkHeaderSize, chunk_length))) | |
116 return -1; | |
117 } else if (data.starts_with(kSubchunk2Id)) { | |
118 if (!ParseDataChunk(data.substr(kChunkHeaderSize, chunk_length))) | |
119 return -1; | |
120 } else { | |
121 DVLOG(1) << "Unknown data chunk: " << data.substr(0, 4) << "."; | |
122 } | |
123 return chunk_length + kChunkHeaderSize; | |
124 } | |
125 | |
126 bool WavAudioHandler::ParseFmtChunk(const base::StringPiece& data) { | |
127 if (data.size() < kFmtChunkMinimumSize) { | |
128 DLOG(ERROR) << "Data size " << data.size() << " is too short."; | |
129 return false; | |
130 } | |
131 DCHECK_EQ(ReadInt<uint16>(data, kAudioFormatOffset), kAudioFormatPCM); | |
132 num_channels_ = ReadInt<uint16>(data, kChannelOffset); | |
133 sample_rate_ = ReadInt<uint32>(data, kSampleRateOffset); | |
134 bits_per_sample_ = ReadInt<uint16>(data, kBitsPerSampleOffset); | |
135 return true; | |
136 } | |
137 | |
138 bool WavAudioHandler::ParseDataChunk(const base::StringPiece& data) { | |
139 data_ = data; | |
140 return true; | |
141 } | |
142 | |
143 } // namespace media | 210 } // namespace media |
OLD | NEW |