From: Jacek Konieczny Date: Tue, 21 May 2013 17:48:02 +0000 (+0200) Subject: Fix sound output when using new ffmpeg X-Git-Tag: auto/th/performous-0.7.0-4 X-Git-Url: http://git.pld-linux.org/?p=packages%2Fperformous.git;a=commitdiff_plain;h=f69e77a Fix sound output when using new ffmpeg Patch from: https://github.com/performous/performous/commit/13ba270d0cfedd2fc15eff0ff7e071b3cce05635 Release: 4 --- diff --git a/performous-ffmpeg.patch b/performous-ffmpeg.patch new file mode 100644 index 0000000..f4955ff --- /dev/null +++ b/performous-ffmpeg.patch @@ -0,0 +1,355 @@ +diff -dur performous-0.7.0.orig/game/audio.cc performous-0.7.0/game/audio.cc +--- performous-0.7.0.orig/game/audio.cc 2012-11-12 23:14:08.000000000 +0100 ++++ performous-0.7.0/game/audio.cc 2013-05-20 21:06:19.000000000 +0200 +@@ -140,7 +140,7 @@ + FFmpeg mpeg; + float fadeLevel; + float pitchFactor; +- Track(std::string const& filename, unsigned int sr): mpeg(false, true, filename, sr), fadeLevel(1.0f), pitchFactor(0.0f) {} ++ Track(std::string const& filename, unsigned int sr): mpeg(filename, sr), fadeLevel(1.0f), pitchFactor(0.0f) {} + }; + typedef boost::ptr_map Tracks; + Tracks tracks; ///< Audio decoders +@@ -241,7 +241,7 @@ + FFmpeg mpeg; + bool eof; + public: +- Sample(std::string const& filename, unsigned sr) : srate(sr), m_pos(), mpeg(false, true, filename, sr), eof(true) { } ++ Sample(std::string const& filename, unsigned sr) : srate(sr), m_pos(), mpeg(filename, sr), eof(true) { } + void operator()(float* begin, float* end) { + if(eof) { + // No more data to play in this sample +diff -dur performous-0.7.0.orig/game/ffmpeg.cc performous-0.7.0/game/ffmpeg.cc +--- performous-0.7.0.orig/game/ffmpeg.cc 2012-11-12 23:14:08.000000000 +0100 ++++ performous-0.7.0/game/ffmpeg.cc 2013-05-20 21:10:23.331289370 +0200 +@@ -3,6 +3,7 @@ + #include "config.hh" + #include "util.hh" + #include "xtime.hh" ++#include + #include + #include + +@@ -20,36 +21,19 @@ + + /*static*/ boost::mutex FFmpeg::s_avcodec_mutex; + +-FFmpeg::FFmpeg(bool decodeVideo, bool decodeAudio, std::string const& _filename, unsigned int rate): +- width(), height(), m_filename(_filename), m_rate(rate), m_quit(), m_running(), m_eof(), +- m_seekTarget(getNaN()), m_position(), m_streamId(-1), m_mediaType(), +- m_formatContext(), m_codecContext(), m_codec(), m_resampleContext(), m_swsContext(), ++FFmpeg::FFmpeg(std::string const& _filename, unsigned int rate): ++ width(), height(), m_filename(_filename), m_rate(rate), m_quit(), ++ m_seekTarget(getNaN()), m_position(), m_duration(), m_streamId(-1), ++ m_mediaType(rate ? AVMEDIA_TYPE_AUDIO : AVMEDIA_TYPE_VIDEO), ++ m_formatContext(), m_codecContext(), m_resampleContext(), m_swsContext(), + m_thread(new boost::thread(boost::ref(*this))) +-{ +- if (decodeVideo) m_mediaType = AVMEDIA_TYPE_VIDEO; +- else if (decodeAudio) m_mediaType = AVMEDIA_TYPE_AUDIO; +- else throw std::logic_error("Can only decode one track"); +-} ++{} + + FFmpeg::~FFmpeg() { + m_quit = true; + videoQueue.reset(); + audioQueue.quit(); + m_thread->join(); +- // TODO: use RAII for freeing resources (to prevent memory leaks) +- boost::mutex::scoped_lock l(s_avcodec_mutex); // avcodec_close is not thread-safe +- if (m_resampleContext) audio_resample_close(m_resampleContext); +- if (m_codecContext) avcodec_close(m_codecContext); +-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 17, 0) +- if (m_formatContext) avformat_close_input(&m_formatContext); +-#else +- if (m_formatContext) av_close_input_file(m_formatContext); +-#endif +-} +- +-double FFmpeg::duration() const { +- double d = m_running ? m_formatContext->duration / double(AV_TIME_BASE) : getNaN(); +- return d >= 0.0 ? d : getInf(); + } + + void FFmpeg::open() { +@@ -60,11 +44,12 @@ + if (avformat_find_stream_info(m_formatContext, NULL) < 0) throw std::runtime_error("Cannot find stream information"); + m_formatContext->flags |= AVFMT_FLAG_GENPTS; + // Find a track and open the codec +- m_streamId = av_find_best_stream(m_formatContext, (AVMediaType)m_mediaType, -1, -1, &m_codec, 0); ++ AVCodec* codec = NULL; ++ m_streamId = av_find_best_stream(m_formatContext, (AVMediaType)m_mediaType, -1, -1, &codec, 0); + if (m_streamId < 0) throw std::runtime_error("No suitable track found"); + + AVCodecContext* cc = m_formatContext->streams[m_streamId]->codec; +- if (avcodec_open2(cc, m_codec, NULL) < 0) throw std::runtime_error("Cannot open audio codec"); ++ if (avcodec_open2(cc, codec, NULL) < 0) throw std::runtime_error("Cannot open codec"); + cc->workaround_bugs = FF_BUG_AUTODETECT; + m_codecContext = cc; + +@@ -90,18 +75,16 @@ + + void FFmpeg::operator()() { + try { open(); } catch (std::exception const& e) { std::clog << "ffmpeg/error: Failed to open " << m_filename << ": " << e.what() << std::endl; m_quit = true; return; } +- m_running = true; +- audioQueue.setDuration(duration()); ++ m_duration = m_formatContext->duration / double(AV_TIME_BASE); ++ audioQueue.setDuration(m_duration); + int errors = 0; + while (!m_quit) { + try { + if (audioQueue.wantSeek()) m_seekTarget = 0.0; + if (m_seekTarget == m_seekTarget) seek_internal(); + decodePacket(); +- m_eof = false; + errors = 0; + } catch (eof_error&) { +- m_eof = true; + videoQueue.push(new VideoFrame()); // EOF marker + boost::thread::sleep(now() + 0.1); + } catch (std::exception& e) { +@@ -109,10 +92,17 @@ + if (++errors > 2) { std::clog << "ffmpeg/error: FFMPEG terminating due to multiple errors" << std::endl; m_quit = true; } + } + } +- m_running = false; +- m_eof = true; + audioQueue.setEof(); + videoQueue.push(new VideoFrame()); // EOF marker ++ // TODO: use RAII for freeing resources (to prevent memory leaks) ++ boost::mutex::scoped_lock l(s_avcodec_mutex); // avcodec_close is not thread-safe ++ if (m_resampleContext) audio_resample_close(m_resampleContext); ++ if (m_codecContext) avcodec_close(m_codecContext); ++#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 17, 0) ++ if (m_formatContext) avformat_close_input(&m_formatContext); ++#else ++ if (m_formatContext) av_close_input_file(m_formatContext); ++#endif + } + + void FFmpeg::seek(double time, bool wait) { +@@ -125,7 +115,7 @@ + videoQueue.reset(); + audioQueue.reset(); + int flags = 0; +- if (m_seekTarget < position()) flags |= AVSEEK_FLAG_BACKWARD; ++ if (m_seekTarget < m_position) flags |= AVSEEK_FLAG_BACKWARD; + av_seek_frame(m_formatContext, -1, m_seekTarget * AV_TIME_BASE, flags); + m_seekTarget = getNaN(); // Signal that seeking is done + } +@@ -139,16 +129,6 @@ + ~ReadFramePacket() { av_free_packet(this); } + }; + +- struct AVFrameWrapper { +- AVFrame* m_frame; +- AVFrameWrapper(): m_frame(avcodec_alloc_frame()) { +- if (!m_frame) throw std::runtime_error("Unable to allocate AVFrame"); +- } +- ~AVFrameWrapper() { av_free(m_frame); } +- operator AVFrame*() { return m_frame; } +- AVFrame* operator->() { return m_frame; } +- }; +- + // Read an AVPacket and decode it into AVFrames + ReadFramePacket packet(m_formatContext); + int packetSize = packet.size; +@@ -157,18 +137,19 @@ + if (m_quit || m_seekTarget == m_seekTarget) return; + if (packet.stream_index != m_streamId) return; +- AVFrameWrapper frame; ++ boost::shared_ptr frame(avcodec_alloc_frame(), &av_free); + int frameFinished = 0; + int decodeSize = (m_mediaType == AVMEDIA_TYPE_VIDEO ? +- avcodec_decode_video2(m_codecContext, frame, &frameFinished, &packet) : +- avcodec_decode_audio4(m_codecContext, frame, &frameFinished, &packet)); +- if (decodeSize < 0) throw std::runtime_error("cannot decode avframe"); ++ avcodec_decode_video2(m_codecContext, frame.get(), &frameFinished, &packet) : ++ avcodec_decode_audio4(m_codecContext, frame.get(), &frameFinished, &packet)); ++ if (decodeSize < 0) return; // Packet didn't produce any output (could be waiting for B frames or something) + packetSize -= decodeSize; // Move forward within the packet + if (!frameFinished) continue; + // Update current position if timecode is available + if (frame->pkt_pts != uint64_t(AV_NOPTS_VALUE)) { +- m_position = double(frame->pkt_pts) * av_q2d(m_formatContext->streams[m_streamId]->time_base); ++ m_position = double(frame->pkt_pts) * av_q2d(m_formatContext->streams[m_streamId]->time_base) ++ - double(m_formatContext->start_time) / AV_TIME_BASE; + } +- if (m_mediaType == AVMEDIA_TYPE_VIDEO) processVideo(frame); else processAudio(frame); ++ if (m_mediaType == AVMEDIA_TYPE_VIDEO) processVideo(frame.get()); else processAudio(frame.get()); + } + } + +@@ -189,9 +171,28 @@ + } + + void FFmpeg::processAudio(AVFrame* frame) { ++ void* data = frame->data[0]; ++ // New FFmpeg versions use non-interleaved audio decoding and samples may be in float format. ++ // Do a conversion here, allowing us to use the old (deprecated) avcodec audio_resample(). ++ std::vector input; ++ unsigned inFrames = frame->nb_samples; ++ if (frame->data[1]) { ++ unsigned channels = m_codecContext->channels; ++ input.reserve(channels * inFrames); ++ for (unsigned i = 0; i < inFrames; ++i) { ++ for (unsigned ch = 0; ch < channels; ++ch) { ++ data = frame->data[ch]; ++ input.push_back(m_codecContext->sample_fmt == AV_SAMPLE_FMT_FLTP ? ++ da::conv_to_s16(reinterpret_cast(data)[i]) : ++ reinterpret_cast(data)[i] ++ ); ++ } ++ } ++ data = &input[0]; ++ } + // Resample to output sample rate, then push to audio queue and increment timecode + std::vector resampled(AVCODEC_MAX_AUDIO_FRAME_SIZE); +- int frames = audio_resample(m_resampleContext, &resampled[0], (short*)frame->data[0], frame->nb_samples); ++ int frames = audio_resample(m_resampleContext, &resampled[0], reinterpret_cast(data), inFrames); + resampled.resize(frames * AUDIO_CHANNELS); + audioQueue.push(resampled, m_position); // May block + m_position += double(frames)/m_formatContext->streams[m_streamId]->codec->sample_rate; +diff -dur performous-0.7.0.orig/game/ffmpeg.hh performous-0.7.0/game/ffmpeg.hh +--- performous-0.7.0.orig/game/ffmpeg.hh 2012-11-12 23:14:08.000000000 +0100 ++++ performous-0.7.0/game/ffmpeg.hh 2013-05-20 21:07:49.501722941 +0200 +@@ -3,7 +3,7 @@ + #include "util.hh" + #include "libda/sample.hpp" + #include +-#include ++#include + #include + #include + #include +@@ -47,25 +47,19 @@ + } + }; + +-static bool operator<(VideoFrame const& a, VideoFrame const& b) { +- return a.timestamp < b.timestamp; +-} +- + /// video queue: first in first out + class VideoFifo { + public: +- VideoFifo(): m_available(), m_timestamp(), m_eof() {} ++ VideoFifo(): m_timestamp(), m_eof() {} + /// trys to pop a VideoFrame from queue + bool tryPop(VideoFrame& f) { + boost::mutex::scoped_lock l(m_mutex); +- if (!m_queue.empty() && m_queue.begin()->data.empty()) { m_eof = true; return false; } +- statsUpdate(); +- if (m_available == 0) return false; // Nothing to deliver ++ if (m_queue.empty()) return false; // Nothing to deliver ++ if (m_queue.begin()->data.empty()) { m_eof = true; return false; } + f.swap(*m_queue.begin()); +- m_queue.erase(m_queue.begin()); ++ m_queue.pop_front(); + m_cond.notify_all(); + m_timestamp = f.timestamp; +- statsUpdate(); + return true; + } + /// pushes VideoFrames to queue +@@ -73,38 +67,27 @@ + boost::mutex::scoped_lock l(m_mutex); + while (m_queue.size() > m_max) m_cond.wait(l); + if (m_queue.empty()) m_timestamp = f->timestamp; +- m_queue.insert(f); +- statsUpdate(); +- } +- /// updates stats +- void statsUpdate() { +- m_available = std::max(0, int(m_queue.size()) - int(m_min)); +- if (m_available == 0 && !m_queue.empty() && m_queue.rbegin()->data.empty()) m_available = m_queue.size() - 1; ++ m_queue.push_back(f); + } + /// resets video queue + void reset() { + boost::mutex::scoped_lock l(m_mutex); + m_queue.clear(); + m_cond.notify_all(); +- statsUpdate(); + m_eof = false; + } + /// returns current position + double position() const { return m_timestamp; } +- /// returns m_available / m_max +- double percentage() const { return double(m_available) / m_max; } + /// simple eof check + double eof() const { return m_eof; } + + private: +- boost::ptr_set m_queue; ++ boost::ptr_deque m_queue; + mutable boost::mutex m_mutex; + boost::condition m_cond; +- volatile unsigned m_available; + double m_timestamp; + bool m_eof; +- static const unsigned m_min = 16; // H.264 may have 16 consecutive B frames +- static const unsigned m_max = 50; ++ static const unsigned m_max = 20; + }; + + class AudioBuffer { +@@ -195,7 +178,6 @@ + + // ffmpeg forward declarations + extern "C" { +- struct AVCodec; + struct AVCodecContext; + struct AVFormatContext; + struct AVFrame; +@@ -206,8 +188,8 @@ + /// ffmpeg class + class FFmpeg { + public: +- /// constructor +- FFmpeg(bool decodeVideo, bool decodeAudio, std::string const& file, unsigned int rate = 48000); ++ /// Decode file; if no rate is specified, decode video, otherwise decode audio. ++ FFmpeg(std::string const& file, unsigned int rate = 0); + ~FFmpeg(); + void operator()(); ///< Thread runs here, don't call directly + unsigned width, ///< width of video +@@ -220,8 +202,6 @@ + void seek(double time, bool wait = true); + /// duration + double duration() const; +- /// return current position +- double position() { return videoQueue.position(); /* FIXME: remove */ } + bool terminating() const { return m_quit; } + + class eof_error: public std::exception {}; +@@ -234,16 +214,14 @@ + std::string m_filename; + unsigned int m_rate; + volatile bool m_quit; +- volatile bool m_running; +- volatile bool m_eof; + volatile double m_seekTarget; + double m_position; ++ double m_duration; + // libav-specific variables + int m_streamId; + int m_mediaType; // enum AVMediaType + AVFormatContext* m_formatContext; + AVCodecContext* m_codecContext; +- AVCodec* m_codec; + ReSampleContext* m_resampleContext; + SwsContext* m_swsContext; + // Make sure the thread starts only after initializing everything else +diff -dur performous-0.7.0.orig/game/video.cc performous-0.7.0/game/video.cc +--- performous-0.7.0.orig/game/video.cc 2012-11-12 23:14:08.000000000 +0100 ++++ performous-0.7.0/game/video.cc 2013-05-20 21:07:49.501722941 +0200 +@@ -3,7 +3,7 @@ + #include "util.hh" + #include + +-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) {} ++Video::Video(std::string const& _videoFile, double videoGap): m_mpeg(_videoFile), m_videoGap(videoGap), m_surfaceTime(), m_lastTime(), m_alpha(-0.5, 1.5) {} + + void Video::prepare(double time) { + time += m_videoGap; diff --git a/performous.spec b/performous.spec index acf511a..9bfed67 100644 --- a/performous.spec +++ b/performous.spec @@ -6,13 +6,14 @@ Summary: Performous - The All-in-One Music Game Summary(pl.UTF-8): Performous - wiele gier muzycznych w jednej Name: performous Version: 0.7.0 -Release: 3 +Release: 4 License: GPL v2+ Group: Applications Source0: http://downloads.sourceforge.net/performous/performous-%{version}.tar.bz2 # Source0-md5: 60f3f72b51a630a4aa996484aedb9526 Patch1: %{name}-libpng15.patch Patch2: cmake.patch +Patch3: %{name}-ffmpeg.patch URL: http://performous.org/ BuildRequires: ImageMagick-c++-devel BuildRequires: SDL-devel @@ -72,6 +73,7 @@ Zestaw narzędzi do konwersji danych dla programu Performous. %setup -q %patch1 -p1 %patch2 -p1 +%patch3 -p1 mkdir build %build