Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(197)

Side by Side Diff: media/filters/ffmpeg_glue.cc

Issue 10912080: Switch to AVIO instead of a custom FFmpeg URLProtocol handler. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Comments. Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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/stringprintf.h" 8 #include "base/synchronization/lock.h"
9 #include "media/ffmpeg/ffmpeg_common.h" 9 #include "media/ffmpeg/ffmpeg_common.h"
10 10
11 namespace media { 11 namespace media {
12 12
13 static FFmpegURLProtocol* ToProtocol(void* data) { 13 // Internal buffer size used by AVIO for reading.
14 return reinterpret_cast<FFmpegURLProtocol*>(data); 14 // TODO(dalecurtis): Experiment with this buffer size and measure impact on
15 } 15 // performance. Currently we want to use 32kb to preserve existing behavior
16 // with the previous URLProtocol based approach.
17 enum { kBufferSize = 32 * 1024 };
16 18
17 // FFmpeg protocol interface. 19 static int AVIOReadOperation(void* opaque, uint8_t* buf, int buf_size) {
18 static int OpenContext(URLContext* h, const char* filename, int flags) { 20 FFmpegURLProtocol* protocol = reinterpret_cast<FFmpegURLProtocol*>(opaque);
19 FFmpegURLProtocol* protocol; 21 int result = protocol->Read(buf_size, buf);
20 FFmpegGlue::GetInstance()->GetProtocol(filename, &protocol);
21 if (!protocol)
22 return AVERROR(EIO);
23
24 h->priv_data = protocol;
25 h->flags = AVIO_FLAG_READ;
26 h->is_streamed = protocol->IsStreaming();
27 return 0;
28 }
29
30 static int ReadContext(URLContext* h, unsigned char* buf, int size) {
31 FFmpegURLProtocol* protocol = ToProtocol(h->priv_data);
32 int result = protocol->Read(size, buf);
33 if (result < 0) 22 if (result < 0)
34 result = AVERROR(EIO); 23 result = AVERROR(EIO);
35 return result; 24 return result;
36 } 25 }
37 26
38 #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52, 68, 0) 27 static int64 AVIOSeekOperation(void* opaque, int64 offset, int whence) {
39 static int WriteContext(URLContext* h, const unsigned char* buf, int size) { 28 FFmpegURLProtocol* protocol = reinterpret_cast<FFmpegURLProtocol*>(opaque);
40 #else
41 static int WriteContext(URLContext* h, unsigned char* buf, int size) {
42 #endif
43 // We don't support writing.
44 return AVERROR(EIO);
45 }
46
47 static int64 SeekContext(URLContext* h, int64 offset, int whence) {
48 FFmpegURLProtocol* protocol = ToProtocol(h->priv_data);
49 int64 new_offset = AVERROR(EIO); 29 int64 new_offset = AVERROR(EIO);
50 switch (whence) { 30 switch (whence) {
51 case SEEK_SET: 31 case SEEK_SET:
52 if (protocol->SetPosition(offset)) 32 if (protocol->SetPosition(offset))
53 protocol->GetPosition(&new_offset); 33 protocol->GetPosition(&new_offset);
54 break; 34 break;
55 35
56 case SEEK_CUR: 36 case SEEK_CUR:
57 int64 pos; 37 int64 pos;
58 if (!protocol->GetPosition(&pos)) 38 if (!protocol->GetPosition(&pos))
(...skipping 15 matching lines...) Expand all
74 break; 54 break;
75 55
76 default: 56 default:
77 NOTREACHED(); 57 NOTREACHED();
78 } 58 }
79 if (new_offset < 0) 59 if (new_offset < 0)
80 new_offset = AVERROR(EIO); 60 new_offset = AVERROR(EIO);
81 return new_offset; 61 return new_offset;
82 } 62 }
83 63
84 static int CloseContext(URLContext* h) {
85 h->priv_data = NULL;
86 return 0;
87 }
88
89 static int LockManagerOperation(void** lock, enum AVLockOp op) { 64 static int LockManagerOperation(void** lock, enum AVLockOp op) {
90 switch (op) { 65 switch (op) {
91 case AV_LOCK_CREATE: 66 case AV_LOCK_CREATE:
92 *lock = new base::Lock(); 67 *lock = new base::Lock();
93 if (!*lock) 68 if (!*lock)
94 return 1; 69 return 1;
95 return 0; 70 return 0;
96 71
97 case AV_LOCK_OBTAIN: 72 case AV_LOCK_OBTAIN:
98 static_cast<base::Lock*>(*lock)->Acquire(); 73 static_cast<base::Lock*>(*lock)->Acquire();
99 return 0; 74 return 0;
100 75
101 case AV_LOCK_RELEASE: 76 case AV_LOCK_RELEASE:
102 static_cast<base::Lock*>(*lock)->Release(); 77 static_cast<base::Lock*>(*lock)->Release();
103 return 0; 78 return 0;
104 79
105 case AV_LOCK_DESTROY: 80 case AV_LOCK_DESTROY:
106 delete static_cast<base::Lock*>(*lock); 81 delete static_cast<base::Lock*>(*lock);
107 *lock = NULL; 82 *lock = NULL;
108 return 0; 83 return 0;
109 } 84 }
110 return 1; 85 return 1;
111 } 86 }
112 87
113 // Use the HTTP protocol to avoid any file path separator issues. 88 static bool InitializeFFmpegInternal() {
114 static const char kProtocol[] = "http";
115
116 // Fill out our FFmpeg protocol definition.
117 static URLProtocol kFFmpegURLProtocol = {
118 kProtocol,
119 &OpenContext,
120 NULL, // url_open2
121 &ReadContext,
122 &WriteContext,
123 &SeekContext,
124 &CloseContext,
125 };
126
127 FFmpegGlue::FFmpegGlue() {
128 // Before doing anything disable logging as it interferes with layout tests. 89 // Before doing anything disable logging as it interferes with layout tests.
129 av_log_set_level(AV_LOG_QUIET); 90 av_log_set_level(AV_LOG_QUIET);
130 91
131 // Register our protocol glue code with FFmpeg. 92 // Register our protocol glue code with FFmpeg.
132 av_register_protocol2(&kFFmpegURLProtocol, sizeof(kFFmpegURLProtocol)); 93 if (av_lockmgr_register(&LockManagerOperation) != 0)
133 av_lockmgr_register(&LockManagerOperation); 94 return false;
134 95
135 // Now register the rest of FFmpeg. 96 // Now register the rest of FFmpeg.
136 av_register_all(); 97 av_register_all();
98 return true;
99 }
100
101 void FFmpegGlue::InitializeFFmpeg() {
102 // FFmpeg only needs to be initialized once.
103 static const bool kStatus = InitializeFFmpegInternal();
104 CHECK(kStatus);
105 }
106
107 FFmpegGlue::FFmpegGlue(FFmpegURLProtocol* protocol)
108 : open_called_(false) {
109 InitializeFFmpeg();
110
111 // Initialize an AVIOContext using our custom read and seek operations. Don't
112 // keep pointers to the buffer since FFmpeg may reallocate it on the fly. It
113 // will be cleaned up
114 format_context_ = avformat_alloc_context();
115 avio_context_.reset(avio_alloc_context(
116 static_cast<unsigned char*>(av_malloc(kBufferSize)), kBufferSize, 0,
117 protocol, &AVIOReadOperation, NULL, &AVIOSeekOperation));
118
119 // Ensure FFmpeg only tries to seek on resources we know to be seekable.
120 avio_context_->seekable =
121 protocol->IsStreaming() ? 0 : AVIO_SEEKABLE_NORMAL;
122
123 // Ensure writing is disabled.
124 avio_context_->write_flag = 0;
125
126 // Tell the format context about our custom IO context. avformat_open_input()
127 // will set the AVFMT_FLAG_CUSTOM_IO flag for us, but do so here to ensure an
128 // early error state doesn't cause FFmpeg to free our resources in error.
129 format_context_->flags |= AVFMT_FLAG_CUSTOM_IO;
130 format_context_->pb = avio_context_.get();
131 }
132
133 bool FFmpegGlue::OpenContext() {
134 DCHECK(!open_called_) << "OpenContext() should't be called twice.";
135
136 // If avformat_open_input() is called we have to take a slightly different
137 // destruction path to avoid double frees.
138 open_called_ = true;
139
140 // By passing NULL for the filename (second parameter) we are telling FFmpeg
141 // to use the AVIO context we setup from the AVFormatContext structure.
142 return avformat_open_input(&format_context_, NULL, NULL, NULL) == 0;
137 } 143 }
138 144
139 FFmpegGlue::~FFmpegGlue() { 145 FFmpegGlue::~FFmpegGlue() {
140 av_lockmgr_register(NULL); 146 // In the event of avformat_open_input() failure, FFmpeg may sometimes free
141 } 147 // our AVFormatContext behind the scenes, but leave the buffer alive. It will
142 148 // helpfully set |format_context_| to NULL in this case.
143 // static 149 if (!format_context_) {
144 FFmpegGlue* FFmpegGlue::GetInstance() { 150 av_free(avio_context_->buffer);
145 return Singleton<FFmpegGlue>::get();
146 }
147
148 // static
149 URLProtocol* FFmpegGlue::url_protocol() {
150 return &kFFmpegURLProtocol;
151 }
152
153 std::string FFmpegGlue::AddProtocol(FFmpegURLProtocol* protocol) {
154 base::AutoLock auto_lock(lock_);
155 std::string key = GetProtocolKey(protocol);
156 if (protocols_.find(key) == protocols_.end()) {
157 protocols_[key] = protocol;
158 }
159 return key;
160 }
161
162 void FFmpegGlue::RemoveProtocol(FFmpegURLProtocol* protocol) {
163 base::AutoLock auto_lock(lock_);
164 for (ProtocolMap::iterator cur, iter = protocols_.begin();
165 iter != protocols_.end();) {
166 cur = iter;
167 iter++;
168
169 if (cur->second == protocol)
170 protocols_.erase(cur);
171 }
172 }
173
174 void FFmpegGlue::GetProtocol(const std::string& key,
175 FFmpegURLProtocol** protocol) {
176 base::AutoLock auto_lock(lock_);
177 ProtocolMap::iterator iter = protocols_.find(key);
178 if (iter == protocols_.end()) {
179 *protocol = NULL;
180 return; 151 return;
181 } 152 }
182 *protocol = iter->second;
183 }
184 153
185 std::string FFmpegGlue::GetProtocolKey(FFmpegURLProtocol* protocol) { 154 // If avformat_open_input() hasn't been called, we should simply free the
186 // Use the FFmpegURLProtocol's memory address to generate the unique string. 155 // AVFormatContext and buffer instead of using avformat_close_input().
187 // This also has the nice property that adding the same FFmpegURLProtocol 156 if (!open_called_) {
188 // reference will not generate duplicate entries. 157 avformat_free_context(format_context_);
189 return base::StringPrintf("%s://%p", kProtocol, static_cast<void*>(protocol)); 158 av_free(avio_context_->buffer);
159 return;
160 }
161
162 // If avformat_open_input() has been called with this context, we need to
163 // close out any codecs/streams before closing the context.
164 if (format_context_->streams) {
165 for (int i = format_context_->nb_streams - 1; i >= 0; --i) {
166 AVStream* stream = format_context_->streams[i];
167
168 // The conditions for calling avcodec_close():
169 // 1. AVStream is alive.
170 // 2. AVCodecContext in AVStream is alive.
171 // 3. AVCodec in AVCodecContext is alive.
172 //
173 // Closing a codec context without prior avcodec_open2() will result in
174 // a crash in FFmpeg.
175 if (stream && stream->codec && stream->codec->codec) {
176 stream->discard = AVDISCARD_ALL;
177 avcodec_close(stream->codec);
178 }
179 }
180 }
181
182 avformat_close_input(&format_context_);
183 av_free(avio_context_->buffer);
190 } 184 }
191 185
192 } // namespace media 186 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698