示例#1
0
	void Timer::removeObserver(Observer& anObserver)
	{
		std::set<Observer*>::iterator it = m_observers.find(&anObserver);
		
		if (it == m_observers.end()) {
			sfeLogWarning("Timer::removeObserver() - removing an unregistered observer. Ignored.");
		} else {
			m_observers.erase(it);
		}
	}
示例#2
0
 void Stream::flushBuffers()
 {
     sf::Lock l(m_readerMutex);
     if (getStatus() == Playing)
     {
         sfeLogWarning("packets flushed while the stream is still playing");
     }
     
     if (m_formatCtx && m_stream)
         avcodec_flush_buffers(m_stream->codec);
     
     AVPacket* pkt = nullptr;
     
     while (m_packetList.size())
     {
         pkt = m_packetList.front();
         m_packetList.pop_front();
         
         av_free_packet(pkt);
         av_free(pkt);
     }
 }
示例#3
0
 bool Demuxer::didSeek(const Timer &timer, sf::Time oldPosition)
 {
     resetEndOfFileStatus();
     sf::Time newPosition = timer.getOffset();
     std::set< std::shared_ptr<Stream> > connectedStreams;
     
     if (m_connectedVideoStream)
         connectedStreams.insert(m_connectedVideoStream);
     if (m_connectedAudioStream)
         connectedStreams.insert(m_connectedAudioStream);
     if (m_connectedSubtitleStream)
         connectedStreams.insert(m_connectedSubtitleStream);
     
     CHECK(!connectedStreams.empty(), "Inconcistency error: seeking with no active stream");
     
     // Trivial seeking to beginning
     if (newPosition == sf::Time::Zero)
     {
         int64_t timestamp = 0;
         
         if (m_formatCtx->iformat->flags & AVFMT_SEEK_TO_PTS && m_formatCtx->start_time != AV_NOPTS_VALUE)
             timestamp += m_formatCtx->start_time;
         
         
         // Flush all streams
         for (std::shared_ptr<Stream> stream : connectedStreams)
             stream->flushBuffers();
         flushBuffers();
         
         // Seek to beginning
         int err = avformat_seek_file(m_formatCtx, -1, INT64_MIN, timestamp, INT64_MAX, AVSEEK_FLAG_BACKWARD);
         if (err < 0)
         {
             sfeLogError("Error while seeking at time " + s(newPosition.asMilliseconds()) + "ms");
             return false;
         }
     }
     else // Seeking to some other position
     {
         // Initial target seek point
         int64_t timestamp = newPosition.asSeconds() * AV_TIME_BASE;
         
         // < 0 = before seek point
         // > 0 = after seek point
         std::map< std::shared_ptr<Stream>, sf::Time> seekingGaps;
         
         static const float brokenSeekingThreshold = 60.f; // seconds
         bool didReseekBackward = false;
         bool didReseekForward = false;
         int tooEarlyCount = 0;
         int tooLateCount = 0;
         int brokenSeekingCount = 0;
         int ffmpegSeekFlags = AVSEEK_FLAG_BACKWARD;
         
         do
         {
             // Flush all streams
             for (std::shared_ptr<Stream> stream : connectedStreams)
                 stream->flushBuffers();
             flushBuffers();
             
             // Seek to new estimated target
             if (m_formatCtx->iformat->flags & AVFMT_SEEK_TO_PTS && m_formatCtx->start_time != AV_NOPTS_VALUE)
                 timestamp += m_formatCtx->start_time;
             
             int err = avformat_seek_file(m_formatCtx, -1, timestamp - 10 * AV_TIME_BASE,
                                          timestamp, timestamp, ffmpegSeekFlags);
             CHECK0(err, "avformat_seek_file failure");
             
             // Compute the new gap
             for (std::shared_ptr<Stream> stream : connectedStreams)
             {
                 sf::Time gap = stream->computeEncodedPosition() - newPosition;
                 seekingGaps[stream] = gap;
             }
             
             tooEarlyCount = 0;
             tooLateCount = 0;
             brokenSeekingCount = 0;
             
             // Check the current situation
             for (std::pair< std::shared_ptr<Stream>, sf::Time>&& gapByStream : seekingGaps)
             {
                 // < 0 = before seek point
                 // > 0 = after seek point
                 const sf::Time& gap = gapByStream.second;
                 float absoluteDiff = fabs(gap.asSeconds());
                 
                 // Before seek point
                 if (gap < sf::Time::Zero)
                 {
                     if (absoluteDiff > brokenSeekingThreshold)
                     {
                         brokenSeekingCount++;
                         tooEarlyCount++;
                     }
                 
                     // else: a bit early but not too much, this is the final situation we want
                 }
                 // After seek point
                 else if (gap > sf::Time::Zero)
                 {
                     tooLateCount++;
                 
                     if (absoluteDiff > brokenSeekingThreshold)
                         brokenSeekingCount++; // TODO: unhandled for now => should seek to non-key frame
                 }
                 
                 if (brokenSeekingCount > 0)
                     sfeLogWarning("Seeking on " + gapByStream.first->description() + " is broken! Gap: "
                                   + s(gap.asSeconds()) + "s");
             }
             
             CHECK(false == (tooEarlyCount && tooLateCount),
                   "Both too late and too early for different streams, unhandled situation!");
             
             // Define what to do next
             if (tooEarlyCount)
             {
                 // Go forward by 1 sec
                 timestamp += AV_TIME_BASE;
                 didReseekForward = true;
             }
             else if (tooLateCount)
             {
                 // Go backward by 1 sec
                 timestamp -= AV_TIME_BASE;
                 didReseekBackward = true;
             }
             
             if (brokenSeekingCount)
             {
                 if (ffmpegSeekFlags & AVSEEK_FLAG_ANY)
                 {
                     sfeLogError("Seeking is really broken in the media, giving up");
                     return false;
                 }
                 else
                 {
                     // Try to seek to non-key frame before giving up
                     // Image may be wrong but it's better than nothing :)
                     ffmpegSeekFlags |= AVSEEK_FLAG_ANY;
                     sfeLogError("Media has broken seeking index, trying to seek to non-key frame");
                 }
             }
             
             CHECK(!(didReseekBackward && didReseekForward), "infinitely seeking backward and forward");
         }
         while (tooEarlyCount != 0 || tooLateCount != 0);
     }
     
     return true;
 }
示例#4
0
 Demuxer::Demuxer(const std::string& sourceFile, std::shared_ptr<Timer> timer,
                  VideoStream::Delegate& videoDelegate, SubtitleStream::Delegate& subtitleDelegate) :
 m_formatCtx(nullptr),
 m_eofReached(false),
 m_streams(),
 m_ignoredStreams(),
 m_synchronized(),
 m_timer(timer),
 m_connectedAudioStream(nullptr),
 m_connectedVideoStream(nullptr),
 m_connectedSubtitleStream(nullptr),
 m_duration(sf::Time::Zero)
 {
     CHECK(sourceFile.size(), "Demuxer::Demuxer() - invalid argument: sourceFile");
     CHECK(timer, "Inconsistency error: null timer");
     
     int err = 0;
     
     // Load all the decoders
     loadFFmpeg();
     
     // Open the movie file
     err = avformat_open_input(&m_formatCtx, sourceFile.c_str(), nullptr, nullptr);
     CHECK0(err, "Demuxer::Demuxer() - error while opening media: " + sourceFile);
     CHECK(m_formatCtx, "Demuxer() - inconsistency: media context cannot be nullptr");
     
     // Read the general movie informations
     err = avformat_find_stream_info(m_formatCtx, nullptr);
     CHECK0(err, "Demuxer::Demuxer() - error while retreiving media information");
     
     // Get the media duration if possible (otherwise rely on the streams)
     if (m_formatCtx->duration != AV_NOPTS_VALUE)
     {
         int64_t secs, us;
         secs = m_formatCtx->duration / AV_TIME_BASE;
         us = m_formatCtx->duration % AV_TIME_BASE;
         m_duration = sf::seconds(secs + (float)us / AV_TIME_BASE);
     }
     
     // Find all interesting streams
     for (unsigned int i = 0; i < m_formatCtx->nb_streams; i++)
     {
         AVStream* & ffstream = m_formatCtx->streams[i];
         
         try
         {
             std::shared_ptr<Stream> stream;
             
             switch (ffstream->codec->codec_type)
             {
                 case AVMEDIA_TYPE_VIDEO:
                     stream = std::make_shared<VideoStream>(m_formatCtx, ffstream, *this, timer, videoDelegate);
                     
                     if (m_duration == sf::Time::Zero)
                     {
                         extractDurationFromStream(ffstream);
                     }
                     
                     sfeLogDebug("Loaded " + avcodec_get_name(ffstream->codec->codec_id) + " video stream");
                     break;
                     
                 case AVMEDIA_TYPE_AUDIO:
                     stream = std::make_shared<AudioStream>(m_formatCtx, ffstream, *this, timer);
                     
                     if (m_duration == sf::Time::Zero)
                     {
                         extractDurationFromStream(ffstream);
                     }
                     
                     sfeLogDebug("Loaded " + avcodec_get_name(ffstream->codec->codec_id) + " audio stream");
                     break;
                 case AVMEDIA_TYPE_SUBTITLE:
                     stream = std::make_shared<SubtitleStream>(m_formatCtx, ffstream, *this, timer, subtitleDelegate);
                     
                     sfeLogDebug("Loaded " + avcodec_get_name(ffstream->codec->codec_id) + " subtitle stream");
                     break;
                 default:
                     m_ignoredStreams[ffstream->index] = Stream::AVStreamDescription(ffstream);
                     sfeLogDebug(m_ignoredStreams[ffstream->index] + " ignored");
                     break;
             }
             
             // Don't create an entry in the map unless everything went well and stream did not get ignored
             if (stream)
                 m_streams[ffstream->index] = stream;
         }
         catch (std::runtime_error& e)
         {
             std::string streamDesc = Stream::AVStreamDescription(ffstream);
             
             sfeLogError("error while loading " + streamDesc + ": " + e.what());
             CHECK(m_streams.find(ffstream->index) == m_streams.end(),
                   "Internal inconcistency error: stream whose loading failed should not be stored");
         }
     }
     
     if (m_duration == sf::Time::Zero)
     {
         sfeLogWarning("The media duration could not be retreived");
     }
     
     m_timer->addObserver(*this, DemuxerTimerPriority);
 }