Chromium Code Reviews| 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 |