]>
Commit | Line | Data |
---|---|---|
f69e77a3 JK |
1 | diff -dur performous-0.7.0.orig/game/audio.cc performous-0.7.0/game/audio.cc |
2 | --- performous-0.7.0.orig/game/audio.cc 2012-11-12 23:14:08.000000000 +0100 | |
3 | +++ performous-0.7.0/game/audio.cc 2013-05-20 21:06:19.000000000 +0200 | |
4 | @@ -140,7 +140,7 @@ | |
5 | FFmpeg mpeg; | |
6 | float fadeLevel; | |
7 | float pitchFactor; | |
8 | - Track(std::string const& filename, unsigned int sr): mpeg(false, true, filename, sr), fadeLevel(1.0f), pitchFactor(0.0f) {} | |
9 | + Track(std::string const& filename, unsigned int sr): mpeg(filename, sr), fadeLevel(1.0f), pitchFactor(0.0f) {} | |
10 | }; | |
11 | typedef boost::ptr_map<std::string, Track> Tracks; | |
12 | Tracks tracks; ///< Audio decoders | |
13 | @@ -241,7 +241,7 @@ | |
14 | FFmpeg mpeg; | |
15 | bool eof; | |
16 | public: | |
17 | - Sample(std::string const& filename, unsigned sr) : srate(sr), m_pos(), mpeg(false, true, filename, sr), eof(true) { } | |
18 | + Sample(std::string const& filename, unsigned sr) : srate(sr), m_pos(), mpeg(filename, sr), eof(true) { } | |
19 | void operator()(float* begin, float* end) { | |
20 | if(eof) { | |
21 | // No more data to play in this sample | |
22 | diff -dur performous-0.7.0.orig/game/ffmpeg.cc performous-0.7.0/game/ffmpeg.cc | |
23 | --- performous-0.7.0.orig/game/ffmpeg.cc 2012-11-12 23:14:08.000000000 +0100 | |
24 | +++ performous-0.7.0/game/ffmpeg.cc 2013-05-20 21:10:23.331289370 +0200 | |
25 | @@ -3,6 +3,7 @@ | |
26 | #include "config.hh" | |
27 | #include "util.hh" | |
28 | #include "xtime.hh" | |
29 | +#include <boost/smart_ptr/shared_ptr.hpp> | |
30 | #include <iostream> | |
31 | #include <stdexcept> | |
32 | ||
33 | @@ -20,36 +21,19 @@ | |
34 | ||
35 | /*static*/ boost::mutex FFmpeg::s_avcodec_mutex; | |
36 | ||
37 | -FFmpeg::FFmpeg(bool decodeVideo, bool decodeAudio, std::string const& _filename, unsigned int rate): | |
38 | - width(), height(), m_filename(_filename), m_rate(rate), m_quit(), m_running(), m_eof(), | |
39 | - m_seekTarget(getNaN()), m_position(), m_streamId(-1), m_mediaType(), | |
40 | - m_formatContext(), m_codecContext(), m_codec(), m_resampleContext(), m_swsContext(), | |
41 | +FFmpeg::FFmpeg(std::string const& _filename, unsigned int rate): | |
42 | + width(), height(), m_filename(_filename), m_rate(rate), m_quit(), | |
43 | + m_seekTarget(getNaN()), m_position(), m_duration(), m_streamId(-1), | |
44 | + m_mediaType(rate ? AVMEDIA_TYPE_AUDIO : AVMEDIA_TYPE_VIDEO), | |
45 | + m_formatContext(), m_codecContext(), m_resampleContext(), m_swsContext(), | |
46 | m_thread(new boost::thread(boost::ref(*this))) | |
47 | -{ | |
48 | - if (decodeVideo) m_mediaType = AVMEDIA_TYPE_VIDEO; | |
49 | - else if (decodeAudio) m_mediaType = AVMEDIA_TYPE_AUDIO; | |
50 | - else throw std::logic_error("Can only decode one track"); | |
51 | -} | |
52 | +{} | |
53 | ||
54 | FFmpeg::~FFmpeg() { | |
55 | m_quit = true; | |
56 | videoQueue.reset(); | |
57 | audioQueue.quit(); | |
58 | m_thread->join(); | |
59 | - // TODO: use RAII for freeing resources (to prevent memory leaks) | |
60 | - boost::mutex::scoped_lock l(s_avcodec_mutex); // avcodec_close is not thread-safe | |
61 | - if (m_resampleContext) audio_resample_close(m_resampleContext); | |
62 | - if (m_codecContext) avcodec_close(m_codecContext); | |
63 | -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 17, 0) | |
64 | - if (m_formatContext) avformat_close_input(&m_formatContext); | |
65 | -#else | |
66 | - if (m_formatContext) av_close_input_file(m_formatContext); | |
67 | -#endif | |
68 | -} | |
69 | - | |
70 | -double FFmpeg::duration() const { | |
71 | - double d = m_running ? m_formatContext->duration / double(AV_TIME_BASE) : getNaN(); | |
72 | - return d >= 0.0 ? d : getInf(); | |
73 | } | |
74 | ||
75 | void FFmpeg::open() { | |
76 | @@ -60,11 +44,12 @@ | |
77 | if (avformat_find_stream_info(m_formatContext, NULL) < 0) throw std::runtime_error("Cannot find stream information"); | |
78 | m_formatContext->flags |= AVFMT_FLAG_GENPTS; | |
79 | // Find a track and open the codec | |
80 | - m_streamId = av_find_best_stream(m_formatContext, (AVMediaType)m_mediaType, -1, -1, &m_codec, 0); | |
81 | + AVCodec* codec = NULL; | |
82 | + m_streamId = av_find_best_stream(m_formatContext, (AVMediaType)m_mediaType, -1, -1, &codec, 0); | |
83 | if (m_streamId < 0) throw std::runtime_error("No suitable track found"); | |
84 | ||
85 | AVCodecContext* cc = m_formatContext->streams[m_streamId]->codec; | |
86 | - if (avcodec_open2(cc, m_codec, NULL) < 0) throw std::runtime_error("Cannot open audio codec"); | |
87 | + if (avcodec_open2(cc, codec, NULL) < 0) throw std::runtime_error("Cannot open codec"); | |
88 | cc->workaround_bugs = FF_BUG_AUTODETECT; | |
89 | m_codecContext = cc; | |
90 | ||
91 | @@ -90,18 +75,16 @@ | |
92 | ||
93 | void FFmpeg::operator()() { | |
94 | try { open(); } catch (std::exception const& e) { std::clog << "ffmpeg/error: Failed to open " << m_filename << ": " << e.what() << std::endl; m_quit = true; return; } | |
95 | - m_running = true; | |
96 | - audioQueue.setDuration(duration()); | |
97 | + m_duration = m_formatContext->duration / double(AV_TIME_BASE); | |
98 | + audioQueue.setDuration(m_duration); | |
99 | int errors = 0; | |
100 | while (!m_quit) { | |
101 | try { | |
102 | if (audioQueue.wantSeek()) m_seekTarget = 0.0; | |
103 | if (m_seekTarget == m_seekTarget) seek_internal(); | |
104 | decodePacket(); | |
105 | - m_eof = false; | |
106 | errors = 0; | |
107 | } catch (eof_error&) { | |
108 | - m_eof = true; | |
109 | videoQueue.push(new VideoFrame()); // EOF marker | |
110 | boost::thread::sleep(now() + 0.1); | |
111 | } catch (std::exception& e) { | |
112 | @@ -109,10 +92,17 @@ | |
113 | if (++errors > 2) { std::clog << "ffmpeg/error: FFMPEG terminating due to multiple errors" << std::endl; m_quit = true; } | |
114 | } | |
115 | } | |
116 | - m_running = false; | |
117 | - m_eof = true; | |
118 | audioQueue.setEof(); | |
119 | videoQueue.push(new VideoFrame()); // EOF marker | |
120 | + // TODO: use RAII for freeing resources (to prevent memory leaks) | |
121 | + boost::mutex::scoped_lock l(s_avcodec_mutex); // avcodec_close is not thread-safe | |
122 | + if (m_resampleContext) audio_resample_close(m_resampleContext); | |
123 | + if (m_codecContext) avcodec_close(m_codecContext); | |
124 | +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 17, 0) | |
125 | + if (m_formatContext) avformat_close_input(&m_formatContext); | |
126 | +#else | |
127 | + if (m_formatContext) av_close_input_file(m_formatContext); | |
128 | +#endif | |
129 | } | |
130 | ||
131 | void FFmpeg::seek(double time, bool wait) { | |
132 | @@ -125,7 +115,7 @@ | |
133 | videoQueue.reset(); | |
134 | audioQueue.reset(); | |
135 | int flags = 0; | |
136 | - if (m_seekTarget < position()) flags |= AVSEEK_FLAG_BACKWARD; | |
137 | + if (m_seekTarget < m_position) flags |= AVSEEK_FLAG_BACKWARD; | |
138 | av_seek_frame(m_formatContext, -1, m_seekTarget * AV_TIME_BASE, flags); | |
139 | m_seekTarget = getNaN(); // Signal that seeking is done | |
140 | } | |
141 | @@ -139,16 +129,6 @@ | |
142 | ~ReadFramePacket() { av_free_packet(this); } | |
143 | }; | |
144 | ||
145 | - struct AVFrameWrapper { | |
146 | - AVFrame* m_frame; | |
147 | - AVFrameWrapper(): m_frame(avcodec_alloc_frame()) { | |
148 | - if (!m_frame) throw std::runtime_error("Unable to allocate AVFrame"); | |
149 | - } | |
150 | - ~AVFrameWrapper() { av_free(m_frame); } | |
151 | - operator AVFrame*() { return m_frame; } | |
152 | - AVFrame* operator->() { return m_frame; } | |
153 | - }; | |
154 | - | |
155 | // Read an AVPacket and decode it into AVFrames | |
156 | ReadFramePacket packet(m_formatContext); | |
157 | int packetSize = packet.size; | |
158 | @@ -157,18 +137,19 @@ | |
159 | if (m_quit || m_seekTarget == m_seekTarget) return; | |
160 | if (packet.stream_index != m_streamId) return; | |
161 | - AVFrameWrapper frame; | |
162 | + boost::shared_ptr<AVFrame> frame(avcodec_alloc_frame(), &av_free); | |
163 | int frameFinished = 0; | |
164 | int decodeSize = (m_mediaType == AVMEDIA_TYPE_VIDEO ? | |
165 | - avcodec_decode_video2(m_codecContext, frame, &frameFinished, &packet) : | |
166 | - avcodec_decode_audio4(m_codecContext, frame, &frameFinished, &packet)); | |
167 | - if (decodeSize < 0) throw std::runtime_error("cannot decode avframe"); | |
168 | + avcodec_decode_video2(m_codecContext, frame.get(), &frameFinished, &packet) : | |
169 | + avcodec_decode_audio4(m_codecContext, frame.get(), &frameFinished, &packet)); | |
170 | + if (decodeSize < 0) return; // Packet didn't produce any output (could be waiting for B frames or something) | |
171 | packetSize -= decodeSize; // Move forward within the packet | |
172 | if (!frameFinished) continue; | |
173 | // Update current position if timecode is available | |
174 | if (frame->pkt_pts != uint64_t(AV_NOPTS_VALUE)) { | |
175 | - m_position = double(frame->pkt_pts) * av_q2d(m_formatContext->streams[m_streamId]->time_base); | |
176 | + m_position = double(frame->pkt_pts) * av_q2d(m_formatContext->streams[m_streamId]->time_base) | |
177 | + - double(m_formatContext->start_time) / AV_TIME_BASE; | |
178 | } | |
179 | - if (m_mediaType == AVMEDIA_TYPE_VIDEO) processVideo(frame); else processAudio(frame); | |
180 | + if (m_mediaType == AVMEDIA_TYPE_VIDEO) processVideo(frame.get()); else processAudio(frame.get()); | |
181 | } | |
182 | } | |
183 | ||
184 | @@ -189,9 +171,28 @@ | |
185 | } | |
186 | ||
187 | void FFmpeg::processAudio(AVFrame* frame) { | |
188 | + void* data = frame->data[0]; | |
189 | + // New FFmpeg versions use non-interleaved audio decoding and samples may be in float format. | |
190 | + // Do a conversion here, allowing us to use the old (deprecated) avcodec audio_resample(). | |
191 | + std::vector<int16_t> input; | |
192 | + unsigned inFrames = frame->nb_samples; | |
193 | + if (frame->data[1]) { | |
194 | + unsigned channels = m_codecContext->channels; | |
195 | + input.reserve(channels * inFrames); | |
196 | + for (unsigned i = 0; i < inFrames; ++i) { | |
197 | + for (unsigned ch = 0; ch < channels; ++ch) { | |
198 | + data = frame->data[ch]; | |
199 | + input.push_back(m_codecContext->sample_fmt == AV_SAMPLE_FMT_FLTP ? | |
200 | + da::conv_to_s16(reinterpret_cast<float*>(data)[i]) : | |
201 | + reinterpret_cast<int16_t*>(data)[i] | |
202 | + ); | |
203 | + } | |
204 | + } | |
205 | + data = &input[0]; | |
206 | + } | |
207 | // Resample to output sample rate, then push to audio queue and increment timecode | |
208 | std::vector<int16_t> resampled(AVCODEC_MAX_AUDIO_FRAME_SIZE); | |
209 | - int frames = audio_resample(m_resampleContext, &resampled[0], (short*)frame->data[0], frame->nb_samples); | |
210 | + int frames = audio_resample(m_resampleContext, &resampled[0], reinterpret_cast<short*>(data), inFrames); | |
211 | resampled.resize(frames * AUDIO_CHANNELS); | |
212 | audioQueue.push(resampled, m_position); // May block | |
213 | m_position += double(frames)/m_formatContext->streams[m_streamId]->codec->sample_rate; | |
214 | diff -dur performous-0.7.0.orig/game/ffmpeg.hh performous-0.7.0/game/ffmpeg.hh | |
215 | --- performous-0.7.0.orig/game/ffmpeg.hh 2012-11-12 23:14:08.000000000 +0100 | |
216 | +++ performous-0.7.0/game/ffmpeg.hh 2013-05-20 21:07:49.501722941 +0200 | |
217 | @@ -3,7 +3,7 @@ | |
218 | #include "util.hh" | |
219 | #include "libda/sample.hpp" | |
220 | #include <boost/circular_buffer.hpp> | |
221 | -#include <boost/ptr_container/ptr_set.hpp> | |
222 | +#include <boost/ptr_container/ptr_deque.hpp> | |
223 | #include <boost/scoped_ptr.hpp> | |
224 | #include <boost/thread/condition.hpp> | |
225 | #include <boost/thread/mutex.hpp> | |
226 | @@ -47,25 +47,19 @@ | |
227 | } | |
228 | }; | |
229 | ||
230 | -static bool operator<(VideoFrame const& a, VideoFrame const& b) { | |
231 | - return a.timestamp < b.timestamp; | |
232 | -} | |
233 | - | |
234 | /// video queue: first in first out | |
235 | class VideoFifo { | |
236 | public: | |
237 | - VideoFifo(): m_available(), m_timestamp(), m_eof() {} | |
238 | + VideoFifo(): m_timestamp(), m_eof() {} | |
239 | /// trys to pop a VideoFrame from queue | |
240 | bool tryPop(VideoFrame& f) { | |
241 | boost::mutex::scoped_lock l(m_mutex); | |
242 | - if (!m_queue.empty() && m_queue.begin()->data.empty()) { m_eof = true; return false; } | |
243 | - statsUpdate(); | |
244 | - if (m_available == 0) return false; // Nothing to deliver | |
245 | + if (m_queue.empty()) return false; // Nothing to deliver | |
246 | + if (m_queue.begin()->data.empty()) { m_eof = true; return false; } | |
247 | f.swap(*m_queue.begin()); | |
248 | - m_queue.erase(m_queue.begin()); | |
249 | + m_queue.pop_front(); | |
250 | m_cond.notify_all(); | |
251 | m_timestamp = f.timestamp; | |
252 | - statsUpdate(); | |
253 | return true; | |
254 | } | |
255 | /// pushes VideoFrames to queue | |
256 | @@ -73,38 +67,27 @@ | |
257 | boost::mutex::scoped_lock l(m_mutex); | |
258 | while (m_queue.size() > m_max) m_cond.wait(l); | |
259 | if (m_queue.empty()) m_timestamp = f->timestamp; | |
260 | - m_queue.insert(f); | |
261 | - statsUpdate(); | |
262 | - } | |
263 | - /// updates stats | |
264 | - void statsUpdate() { | |
265 | - m_available = std::max(0, int(m_queue.size()) - int(m_min)); | |
266 | - if (m_available == 0 && !m_queue.empty() && m_queue.rbegin()->data.empty()) m_available = m_queue.size() - 1; | |
267 | + m_queue.push_back(f); | |
268 | } | |
269 | /// resets video queue | |
270 | void reset() { | |
271 | boost::mutex::scoped_lock l(m_mutex); | |
272 | m_queue.clear(); | |
273 | m_cond.notify_all(); | |
274 | - statsUpdate(); | |
275 | m_eof = false; | |
276 | } | |
277 | /// returns current position | |
278 | double position() const { return m_timestamp; } | |
279 | - /// returns m_available / m_max | |
280 | - double percentage() const { return double(m_available) / m_max; } | |
281 | /// simple eof check | |
282 | double eof() const { return m_eof; } | |
283 | ||
284 | private: | |
285 | - boost::ptr_set<VideoFrame> m_queue; | |
286 | + boost::ptr_deque<VideoFrame> m_queue; | |
287 | mutable boost::mutex m_mutex; | |
288 | boost::condition m_cond; | |
289 | - volatile unsigned m_available; | |
290 | double m_timestamp; | |
291 | bool m_eof; | |
292 | - static const unsigned m_min = 16; // H.264 may have 16 consecutive B frames | |
293 | - static const unsigned m_max = 50; | |
294 | + static const unsigned m_max = 20; | |
295 | }; | |
296 | ||
297 | class AudioBuffer { | |
298 | @@ -195,7 +178,6 @@ | |
299 | ||
300 | // ffmpeg forward declarations | |
301 | extern "C" { | |
302 | - struct AVCodec; | |
303 | struct AVCodecContext; | |
304 | struct AVFormatContext; | |
305 | struct AVFrame; | |
306 | @@ -206,8 +188,8 @@ | |
307 | /// ffmpeg class | |
308 | class FFmpeg { | |
309 | public: | |
310 | - /// constructor | |
311 | - FFmpeg(bool decodeVideo, bool decodeAudio, std::string const& file, unsigned int rate = 48000); | |
312 | + /// Decode file; if no rate is specified, decode video, otherwise decode audio. | |
313 | + FFmpeg(std::string const& file, unsigned int rate = 0); | |
314 | ~FFmpeg(); | |
315 | void operator()(); ///< Thread runs here, don't call directly | |
316 | unsigned width, ///< width of video | |
317 | @@ -220,8 +202,6 @@ | |
318 | void seek(double time, bool wait = true); | |
319 | /// duration | |
320 | double duration() const; | |
321 | - /// return current position | |
322 | - double position() { return videoQueue.position(); /* FIXME: remove */ } | |
323 | bool terminating() const { return m_quit; } | |
324 | ||
325 | class eof_error: public std::exception {}; | |
326 | @@ -234,16 +214,14 @@ | |
327 | std::string m_filename; | |
328 | unsigned int m_rate; | |
329 | volatile bool m_quit; | |
330 | - volatile bool m_running; | |
331 | - volatile bool m_eof; | |
332 | volatile double m_seekTarget; | |
333 | double m_position; | |
334 | + double m_duration; | |
335 | // libav-specific variables | |
336 | int m_streamId; | |
337 | int m_mediaType; // enum AVMediaType | |
338 | AVFormatContext* m_formatContext; | |
339 | AVCodecContext* m_codecContext; | |
340 | - AVCodec* m_codec; | |
341 | ReSampleContext* m_resampleContext; | |
342 | SwsContext* m_swsContext; | |
343 | // Make sure the thread starts only after initializing everything else | |
344 | diff -dur performous-0.7.0.orig/game/video.cc performous-0.7.0/game/video.cc | |
345 | --- performous-0.7.0.orig/game/video.cc 2012-11-12 23:14:08.000000000 +0100 | |
346 | +++ performous-0.7.0/game/video.cc 2013-05-20 21:07:49.501722941 +0200 | |
347 | @@ -3,7 +3,7 @@ | |
348 | #include "util.hh" | |
349 | #include <cmath> | |
350 | ||
351 | -Video::Video(std::string const& _videoFile, double videoGap): m_mpeg(true, false, _videoFile), m_videoGap(videoGap), m_surfaceTime(), m_lastTime(), m_alpha(-0.5, 1.5) {} | |
352 | +Video::Video(std::string const& _videoFile, double videoGap): m_mpeg(_videoFile), m_videoGap(videoGap), m_surfaceTime(), m_lastTime(), m_alpha(-0.5, 1.5) {} | |
353 | ||
354 | void Video::prepare(double time) { | |
355 | time += m_videoGap; |