OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/filters/ffmpeg_glue.h" | 5 #include "media/filters/ffmpeg_glue.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "base/macros.h" | 8 #include "base/macros.h" |
9 #include "base/metrics/histogram_macros.h" | 9 #include "base/metrics/histogram_macros.h" |
10 #include "base/synchronization/lock.h" | 10 #include "base/synchronization/lock.h" |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
130 format_context_->pb = avio_context_.get(); | 130 format_context_->pb = avio_context_.get(); |
131 } | 131 } |
132 | 132 |
133 bool FFmpegGlue::OpenContext() { | 133 bool FFmpegGlue::OpenContext() { |
134 DCHECK(!open_called_) << "OpenContext() shouldn't be called twice."; | 134 DCHECK(!open_called_) << "OpenContext() shouldn't be called twice."; |
135 | 135 |
136 // If avformat_open_input() is called we have to take a slightly different | 136 // If avformat_open_input() is called we have to take a slightly different |
137 // destruction path to avoid double frees. | 137 // destruction path to avoid double frees. |
138 open_called_ = true; | 138 open_called_ = true; |
139 | 139 |
140 // Attempt to recognize the container by looking at the first few bytes of the | 140 // By passing nullptr for the filename (second parameter) we are telling |
141 // stream. The stream position is left unchanged. | 141 // FFmpeg to use the AVIO context we setup from the AVFormatContext structure. |
142 std::unique_ptr<std::vector<uint8_t>> buffer(new std::vector<uint8_t>(8192)); | 142 const int ret = |
143 avformat_open_input(&format_context_, nullptr, nullptr, nullptr); | |
143 | 144 |
144 int64_t pos = AVIOSeekOperation(avio_context_.get()->opaque, 0, SEEK_CUR); | 145 // If FFmpeg can't identify the file, read the first 8k and attempt to guess |
145 AVIOSeekOperation(avio_context_.get()->opaque, 0, SEEK_SET); | 146 // at the container type ourselves. This way we can track emergent formats. |
146 int numRead = AVIOReadOperation( | 147 // Only try on AVERROR_INVALIDDATA to avoid running after I/O errors. |
147 avio_context_.get()->opaque, buffer.get()->data(), buffer.get()->size()); | 148 if (ret == AVERROR_INVALIDDATA) { |
148 AVIOSeekOperation(avio_context_.get()->opaque, pos, SEEK_SET); | 149 std::vector<uint8_t> buffer(8192); |
149 if (numRead > 0) { | 150 |
150 // < 0 means Read failed | 151 const int64_t pos = AVIOSeekOperation(avio_context_->opaque, 0, SEEK_SET); |
151 container_names::MediaContainerName container = | 152 if (pos < 0) |
152 container_names::DetermineContainer(buffer.get()->data(), numRead); | 153 return false; |
153 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedContainer", container); | 154 |
155 const int num_read = | |
156 AVIOReadOperation(avio_context_->opaque, buffer.data(), buffer.size()); | |
157 if (num_read < container_names::kMinimumContainerSize) | |
158 return false; | |
159 | |
160 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
161 "Media.DetectedContainer", | |
162 container_names::DetermineContainer(buffer.data(), num_read)); | |
163 return false; | |
164 } else if (ret < 0) { | |
165 return false; | |
154 } | 166 } |
155 | 167 |
156 // By passing nullptr for the filename (second parameter) we are telling | 168 // Rely on ffmpeg's parsing if we're able to succesfully open the file. |
157 // FFmpeg to use the AVIO context we setup from the AVFormatContext structure. | 169 container_names::MediaContainerName container = |
158 return avformat_open_input(&format_context_, nullptr, nullptr, nullptr) == 0; | 170 container_names::CONTAINER_UNKNOWN; |
171 if (strcmp(format_context_->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0) | |
jrummell
2017/04/17 18:00:15
Too bad there isn't a better way to compute this w
DaleCurtis
2017/04/17 19:08:46
Yeah, hence all the test cases to make sure we sti
| |
172 container = container_names::CONTAINER_MOV; | |
173 else if (strcmp(format_context_->iformat->name, "flac") == 0) | |
174 container = container_names::CONTAINER_FLAC; | |
175 else if (strcmp(format_context_->iformat->name, "matroska,webm") == 0) | |
176 container = container_names::CONTAINER_WEBM; | |
177 else if (strcmp(format_context_->iformat->name, "ogg") == 0) | |
178 container = container_names::CONTAINER_OGG; | |
179 else if (strcmp(format_context_->iformat->name, "wav") == 0) | |
180 container = container_names::CONTAINER_WAV; | |
181 else if (strcmp(format_context_->iformat->name, "aac") == 0) | |
182 container = container_names::CONTAINER_AAC; | |
183 else if (strcmp(format_context_->iformat->name, "mp3") == 0) | |
184 container = container_names::CONTAINER_MP3; | |
185 else if (strcmp(format_context_->iformat->name, "amr") == 0) | |
186 container = container_names::CONTAINER_AMR; | |
187 else if (strcmp(format_context_->iformat->name, "avi") == 0) | |
188 container = container_names::CONTAINER_AVI; | |
189 // TODO(jrummell): Remove GSM detection. http://crbug.com/711774 | |
190 else if (strcmp(format_context_->iformat->name, "gsm") == 0) | |
191 container = container_names::CONTAINER_GSM; | |
192 | |
193 DCHECK_NE(container, container_names::CONTAINER_UNKNOWN); | |
194 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedContainer", container); | |
195 | |
196 return true; | |
159 } | 197 } |
160 | 198 |
161 FFmpegGlue::~FFmpegGlue() { | 199 FFmpegGlue::~FFmpegGlue() { |
162 // In the event of avformat_open_input() failure, FFmpeg may sometimes free | 200 // In the event of avformat_open_input() failure, FFmpeg may sometimes free |
163 // our AVFormatContext behind the scenes, but leave the buffer alive. It will | 201 // our AVFormatContext behind the scenes, but leave the buffer alive. It will |
164 // helpfully set |format_context_| to nullptr in this case. | 202 // helpfully set |format_context_| to nullptr in this case. |
165 if (!format_context_) { | 203 if (!format_context_) { |
166 av_free(avio_context_->buffer); | 204 av_free(avio_context_->buffer); |
167 return; | 205 return; |
168 } | 206 } |
169 | 207 |
170 // If avformat_open_input() hasn't been called, we should simply free the | 208 // If avformat_open_input() hasn't been called, we should simply free the |
171 // AVFormatContext and buffer instead of using avformat_close_input(). | 209 // AVFormatContext and buffer instead of using avformat_close_input(). |
172 if (!open_called_) { | 210 if (!open_called_) { |
173 avformat_free_context(format_context_); | 211 avformat_free_context(format_context_); |
174 av_free(avio_context_->buffer); | 212 av_free(avio_context_->buffer); |
175 return; | 213 return; |
176 } | 214 } |
177 | 215 |
178 avformat_close_input(&format_context_); | 216 avformat_close_input(&format_context_); |
179 av_free(avio_context_->buffer); | 217 av_free(avio_context_->buffer); |
180 } | 218 } |
181 | 219 |
182 } // namespace media | 220 } // namespace media |
OLD | NEW |