Fix sound output when using new ffmpeg
[packages/performous.git] / performous-ffmpeg.patch
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;
This page took 0.142005 seconds and 3 git commands to generate.