1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/filters/ffmpeg_glue.h"
7 #include "base/check_op.h"
8 #include "base/metrics/histogram_functions.h"
9 #include "base/notreached.h"
10 #include "media/base/container_names.h"
11 #include "media/ffmpeg/ffmpeg_common.h"
15 // Internal buffer size used by AVIO for reading.
16 // TODO(dalecurtis): Experiment with this buffer size and measure impact on
17 // performance. Currently we want to use 32kb to preserve existing behavior
18 // with the previous URLProtocol based approach.
19 enum { kBufferSize = 32 * 1024 };
21 static int AVIOReadOperation(void* opaque, uint8_t* buf, int buf_size) {
22 return reinterpret_cast<FFmpegURLProtocol*>(opaque)->Read(buf_size, buf);
25 static int64_t AVIOSeekOperation(void* opaque, int64_t offset, int whence) {
26 FFmpegURLProtocol* protocol = reinterpret_cast<FFmpegURLProtocol*>(opaque);
27 int64_t new_offset = AVERROR(EIO);
30 if (protocol->SetPosition(offset))
31 protocol->GetPosition(&new_offset);
36 if (!protocol->GetPosition(&pos))
38 if (protocol->SetPosition(pos + offset))
39 protocol->GetPosition(&new_offset);
44 if (!protocol->GetSize(&size))
46 if (protocol->SetPosition(size + offset))
47 protocol->GetPosition(&new_offset);
51 protocol->GetSize(&new_offset);
60 static void LogContainer(bool is_local_file,
61 container_names::MediaContainerName container) {
62 base::UmaHistogramSparse("Media.DetectedContainer", container);
64 base::UmaHistogramSparse("Media.DetectedContainer.Local", container);
67 FFmpegGlue::FFmpegGlue(FFmpegURLProtocol* protocol) {
68 // Initialize an AVIOContext using our custom read and seek operations. Don't
69 // keep pointers to the buffer since FFmpeg may reallocate it on the fly. It
71 format_context_ = avformat_alloc_context();
72 avio_context_.reset(avio_alloc_context(
73 static_cast<unsigned char*>(av_malloc(kBufferSize)), kBufferSize, 0,
74 protocol, &AVIOReadOperation, nullptr, &AVIOSeekOperation));
76 // Ensure FFmpeg only tries to seek on resources we know to be seekable.
77 avio_context_->seekable =
78 protocol->IsStreaming() ? 0 : AVIO_SEEKABLE_NORMAL;
80 // Ensure writing is disabled.
81 avio_context_->write_flag = 0;
83 // Tell the format context about our custom IO context. avformat_open_input()
84 // will set the AVFMT_FLAG_CUSTOM_IO flag for us, but do so here to ensure an
85 // early error state doesn't cause FFmpeg to free our resources in error.
86 format_context_->flags |= AVFMT_FLAG_CUSTOM_IO;
88 // Enable fast, but inaccurate seeks for MP3.
89 format_context_->flags |= AVFMT_FLAG_FAST_SEEK;
91 // Ensures format parsing errors will bail out. From an audit on 11/2017, all
92 // instances were real failures. Solves bugs like http://crbug.com/710791.
93 format_context_->error_recognition |= AV_EF_EXPLODE;
95 format_context_->pb = avio_context_.get();
98 bool FFmpegGlue::OpenContext(bool is_local_file) {
99 DCHECK(!open_called_) << "OpenContext() shouldn't be called twice.";
101 // If avformat_open_input() is called we have to take a slightly different
102 // destruction path to avoid double frees.
105 // By passing nullptr for the filename (second parameter) we are telling
106 // FFmpeg to use the AVIO context we setup from the AVFormatContext structure.
108 avformat_open_input(&format_context_, nullptr, nullptr, nullptr);
110 // If FFmpeg can't identify the file, read the first 8k and attempt to guess
111 // at the container type ourselves. This way we can track emergent formats.
112 // Only try on AVERROR_INVALIDDATA to avoid running after I/O errors.
113 if (ret == AVERROR_INVALIDDATA) {
114 std::vector<uint8_t> buffer(8192);
116 const int64_t pos = AVIOSeekOperation(avio_context_->opaque, 0, SEEK_SET);
121 AVIOReadOperation(avio_context_->opaque, buffer.data(), buffer.size());
122 if (num_read < container_names::kMinimumContainerSize)
125 container_ = container_names::DetermineContainer(buffer.data(), num_read);
126 LogContainer(is_local_file, container_);
129 container_ == container_names::MediaContainerName::CONTAINER_HLS;
131 } else if (ret < 0) {
135 // Rely on ffmpeg's parsing if we're able to succesfully open the file.
136 if (strcmp(format_context_->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0)
137 container_ = container_names::CONTAINER_MOV;
138 else if (strcmp(format_context_->iformat->name, "flac") == 0)
139 container_ = container_names::CONTAINER_FLAC;
140 else if (strcmp(format_context_->iformat->name, "matroska,webm") == 0)
141 container_ = container_names::CONTAINER_WEBM;
142 else if (strcmp(format_context_->iformat->name, "ogg") == 0)
143 container_ = container_names::CONTAINER_OGG;
144 else if (strcmp(format_context_->iformat->name, "wav") == 0)
145 container_ = container_names::CONTAINER_WAV;
146 else if (strcmp(format_context_->iformat->name, "aac") == 0)
147 container_ = container_names::CONTAINER_AAC;
148 else if (strcmp(format_context_->iformat->name, "mp3") == 0)
149 container_ = container_names::CONTAINER_MP3;
150 else if (strcmp(format_context_->iformat->name, "amr") == 0)
151 container_ = container_names::CONTAINER_AMR;
152 else if (strcmp(format_context_->iformat->name, "avi") == 0)
153 container_ = container_names::CONTAINER_AVI;
155 DCHECK_NE(container_, container_names::CONTAINER_UNKNOWN);
156 LogContainer(is_local_file, container_);
161 FFmpegGlue::~FFmpegGlue() {
162 // 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
164 // helpfully set |format_context_| to nullptr in this case.
165 if (!format_context_) {
166 av_free(avio_context_->buffer);
170 // If avformat_open_input() hasn't been called, we should simply free the
171 // AVFormatContext and buffer instead of using avformat_close_input().
173 avformat_free_context(format_context_);
174 av_free(avio_context_->buffer);
178 avformat_close_input(&format_context_);
179 av_free(avio_context_->buffer);