上篇讲到player的建立流程KODI(原XBMC)二次开发完全解析(二)-------创建player,接着上篇确定哪一个才是真正干活的player。
m_pPlayer->CreatePlayer(newPlayer, *this);//创建player,下篇重点讲 这个就开始创建player,这个m_pPlayer就是xbmc/ApplicationPlayer.h的实例,调用源码如下:
void CApplicationPlayer::CreatePlayer(const std::string &player, IPlayerCallback& callback)
{
CSingleLock lock(m_player_lock);
if (!m_pPlayer)
{
m_pPlayer.reset(CPlayerCoreFactory::GetInstance().CreatePlayer(player, callback));
}
}
player就是需要创建的player,继续查看CPlayerCoreFactory::GetInstance().CreatePlayer(player, callback)
IPlayer* CPlayerCoreFactory::CreatePlayer(const std::string& nameId, IPlayerCallback& callback) const
{
CSingleLock lock(m_section);
size_t idx = GetPlayerIndex(nameId);
if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size())
return nullptr;
return m_vecPlayerConfigs[idx]->CreatePlayer(callback);
}
根据nameId调用PlayerCoreConfig.h创建Player
IPlayer* CreatePlayer(IPlayerCallback& callback) const
{
IPlayer* pPlayer;
if (m_type.compare("video") == 0)
{
pPlayer = new CVideoPlayer(callback);//创建VideoPlayer
}
else if (m_type.compare("music") == 0)
{
pPlayer = new PAPlayer(callback);
}
else if (m_type.compare("external") == 0)
{
pPlayer = new CExternalPlayer(callback);
}
#if defined(HAS_UPNP)
else if (m_type.compare("remote") == 0)
{
pPlayer = new UPNP::CUPnPPlayer(callback, m_id.c_str());
}
#endif
else
return nullptr;
pPlayer->m_name = m_name;
pPlayer->m_type = m_type;
if (pPlayer->Initialize(m_config))
{
return pPlayer;
}
else
{
SAFE_DELETE(pPlayer);
return nullptr;
}
}
根据type类型,创建需要的player,至此,我们找到了VideoPlayer,这个才是这篇要说的重点,下面先查看VideoPlayer.h文件,位于xbmc/cores/VideoPlayer/VideoPlayer.h
class CVideoPlayer : public IPlayer, public CThread, public IVideoPlayer, public IDispResource, public IRenderMsg
{
public:
CVideoPlayer(IPlayerCallback& callback);
virtual ~CVideoPlayer();
virtual bool OpenFile(const CFileItem& file, const CPlayerOptions &options);
virtual bool CloseFile(bool reopen = false);
virtual bool IsPlaying() const;
virtual void Pause() override;
virtual bool HasVideo() const;
virtual bool HasAudio() const;
virtual bool HasRDS() const;
virtual bool IsPassthrough() const;
virtual bool CanSeek();
virtual void Seek(bool bPlus, bool bLargeStep, bool bChapterOverride);
virtual bool SeekScene(bool bPlus = true);
virtual void SeekPercentage(float iPercent);
virtual float GetPercentage();
virtual float GetCachePercentage();
virtual void SetVolume(float nVolume) override;
virtual void SetMute(bool bOnOff) override;
virtual void SetDynamicRangeCompression(long drc) override;
virtual bool CanRecord();
virtual bool IsRecording();
virtual bool CanPause();
virtual bool Record(bool bOnOff);
virtual void SetAVDelay(float fValue = 0.0f);
virtual float GetAVDelay();
virtual bool IsInMenu() const override;
virtual bool HasMenu() const override;
virtual void SetSubTitleDelay(float fValue = 0.0f);
virtual float GetSubTitleDelay();
virtual int GetSubtitleCount();
virtual int GetSubtitle();
virtual void GetSubtitleStreamInfo(int index, SPlayerSubtitleStreamInfo &info);
virtual void SetSubtitle(int iStream);
virtual bool GetSubtitleVisible();
virtual void SetSubtitleVisible(bool bVisible);
virtual void AddSubtitle(const std::string& strSubPath);
virtual int GetAudioStreamCount();
virtual int GetAudioStream();
virtual void SetAudioStream(int iStream);
virtual int GetVideoStream() const override;
virtual int GetVideoStreamCount() const override;
virtual void GetVideoStreamInfo(int streamId, SPlayerVideoStreamInfo &info) override;
virtual void SetVideoStream(int iStream);
virtual TextCacheStruct_t* GetTeletextCache();
virtual void LoadPage(int p, int sp, unsigned char* buffer);
virtual std::string GetRadioText(unsigned int line);
virtual int GetChapterCount();
virtual int GetChapter();
virtual void GetChapterName(std::string& strChapterName, int chapterIdx=-1);
virtual int64_t GetChapterPos(int chapterIdx=-1);
virtual int SeekChapter(int iChapter);
virtual void SeekTime(int64_t iTime);
virtual bool SeekTimeRelative(int64_t iTime);
virtual int64_t GetTime();
virtual int64_t GetTotalTime();
virtual void SetSpeed(float speed) override;
virtual float GetSpeed() override;
virtual bool SupportsTempo() override;
virtual bool OnAction(const CAction &action);
virtual int GetSourceBitrate();
virtual bool GetStreamDetails(CStreamDetails &details);
virtual void GetAudioStreamInfo(int index, SPlayerAudioStreamInfo &info);
virtual std::string GetPlayerState();
virtual bool SetPlayerState(const std::string& state);
virtual std::string GetPlayingTitle();
virtual bool SwitchChannel(const PVR::CPVRChannelPtr &channel);
virtual void FrameMove();
virtual void Render(bool clear, uint32_t alpha = 255, bool gui = true);
virtual void FlushRenderer();
virtual void SetRenderViewMode(int mode);
float GetRenderAspectRatio();
virtual void TriggerUpdateResolution();
virtual bool IsRenderingVideo();
virtual bool IsRenderingGuiLayer();
virtual bool IsRenderingVideoLayer();
virtual bool Supports(EINTERLACEMETHOD method) override;
virtual EINTERLACEMETHOD GetDeinterlacingMethodDefault() override;
virtual bool Supports(ESCALINGMETHOD method) override;
virtual bool Supports(ERENDERFEATURE feature) override;
virtual unsigned int RenderCaptureAlloc();
virtual void RenderCapture(unsigned int captureId, unsigned int width, unsigned int height, int flags);
virtual void RenderCaptureRelease(unsigned int captureId);
virtual bool RenderCaptureGetPixels(unsigned int captureId, unsigned int millis, uint8_t *buffer, unsigned int size);
// IDispResource interface
virtual void OnLostDisplay();
virtual void OnResetDisplay();
virtual bool IsCaching() const override;
virtual int GetCacheLevel() const override;
virtual int OnDVDNavResult(void* pData, int iMessage) override;
void GetVideoResolution(unsigned int &width, unsigned int &height) override;
protected:
friend class CSelectionStreams;
virtual void OnStartup();
virtual void OnExit();
virtual void Process();
virtual void VideoParamsChange() override;
virtual void GetDebugInfo(std::string &audio, std::string &video, std::string &general) override;
virtual void UpdateClockSync(bool enabled) override;
virtual void UpdateRenderInfo(CRenderInfo &info) override;
virtual void UpdateRenderBuffers(int queued, int discard, int free) override;
void CreatePlayers();
void DestroyPlayers();
bool OpenStream(CCurrentStream& current, int64_t demuxerId, int iStream, int source, bool reset = true);
bool OpenAudioStream(CDVDStreamInfo& hint, bool reset = true);
bool OpenVideoStream(CDVDStreamInfo& hint, bool reset = true);
bool OpenSubtitleStream(CDVDStreamInfo& hint);
bool OpenTeletextStream(CDVDStreamInfo& hint);
bool OpenRadioRDSStream(CDVDStreamInfo& hint);
/** \brief Switches forced subtitles to forced subtitles matching the language of the current audio track.
* If these are not available, subtitles are disabled.
*/
void AdaptForcedSubtitles();
bool CloseStream(CCurrentStream& current, bool bWaitForBuffers);
bool CheckIsCurrent(CCurrentStream& current, CDemuxStream* stream, DemuxPacket* pkg);
void ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket);
void ProcessAudioData(CDemuxStream* pStream, DemuxPacket* pPacket);
void ProcessVideoData(CDemuxStream* pStream, DemuxPacket* pPacket);
void ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket);
void ProcessTeletextData(CDemuxStream* pStream, DemuxPacket* pPacket);
void ProcessRadioRDSData(CDemuxStream* pStream, DemuxPacket* pPacket);
bool ShowPVRChannelInfo();
int AddSubtitleFile(const std::string& filename, const std::string& subfilename = "");
void SetSubtitleVisibleInternal(bool bVisible);
/**
* one of the DVD_PLAYSPEED defines
*/
void SetPlaySpeed(int iSpeed);
int GetPlaySpeed() { return m_playSpeed; }
enum ECacheState
{
CACHESTATE_DONE = 0,
CACHESTATE_FULL, // player is filling up the demux queue
CACHESTATE_INIT, // player is waiting for first packet of each stream
CACHESTATE_PLAY, // player is waiting for players to not be stalled
CACHESTATE_FLUSH, // temporary state player will choose startup between init or full
};
void SetCaching(ECacheState state);
int64_t GetTotalTimeInMsec();
double GetQueueTime();
bool GetCachingTimes(double& play_left, double& cache_left, double& file_offset);
void FlushBuffers(double pts, bool accurate, bool sync);
void HandleMessages();
void HandlePlaySpeed();
bool IsInMenuInternal() const;
void SynchronizeDemuxer();
void CheckAutoSceneSkip();
bool CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket);
bool CheckSceneSkip(CCurrentStream& current);
bool CheckPlayerInit(CCurrentStream& current);
void UpdateCorrection(DemuxPacket* pkt, double correction);
void UpdateTimestamps(CCurrentStream& current, DemuxPacket* pPacket);
IDVDStreamPlayer* GetStreamPlayer(unsigned int player);
void SendPlayerMessage(CDVDMsg* pMsg, unsigned int target);
bool ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream);
bool IsValidStream(CCurrentStream& stream);
bool IsBetterStream(CCurrentStream& current, CDemuxStream* stream);
void CheckBetterStream(CCurrentStream& current, CDemuxStream* stream);
void CheckStreamChanges(CCurrentStream& current, CDemuxStream* stream);
bool CheckDelayedChannelEntry(void);
bool OpenInputStream();
bool OpenDemuxStream();
void CloseDemuxer();
void OpenDefaultStreams(bool reset = true);
void UpdateApplication(double timeout);
void UpdatePlayState(double timeout);
void UpdateStreamInfos();
void GetGeneralInfo(std::string& strVideoInfo);
double m_UpdateApplication;
bool m_players_created;
bool m_bAbortRequest;
ECacheState m_caching;
XbmcThreads::EndTime m_cachingTimer;
CFileItem m_item;
XbmcThreads::EndTime m_ChannelEntryTimeOut;
std::unique_ptr<CProcessInfo> m_processInfo;
CCurrentStream m_CurrentAudio;
CCurrentStream m_CurrentVideo;
CCurrentStream m_CurrentSubtitle;
CCurrentStream m_CurrentTeletext;
CCurrentStream m_CurrentRadioRDS;
CSelectionStreams m_SelectionStreams;
std::atomic_int m_playSpeed;
std::atomic_int m_newPlaySpeed;
int m_streamPlayerSpeed;
struct SSpeedState
{
double lastpts; // holds last display pts during ff/rw operations
int64_t lasttime;
int lastseekpts;
double lastabstime;
} m_SpeedState;
std::atomic_bool m_canTempo;
int m_errorCount;
double m_offset_pts;
CDVDMessageQueue m_messenger; // thread messenger
IDVDStreamPlayerVideo *m_VideoPlayerVideo; // video part
IDVDStreamPlayerAudio *m_VideoPlayerAudio; // audio part
CVideoPlayerSubtitle *m_VideoPlayerSubtitle; // subtitle part
CDVDTeletextData *m_VideoPlayerTeletext; // teletext part
CDVDRadioRDSData *m_VideoPlayerRadioRDS; // rds part
CDVDClock m_clock; // master clock
CDVDOverlayContainer m_overlayContainer;
CDVDInputStream* m_pInputStream; // input stream for current playing file
CDVDDemux* m_pDemuxer; // demuxer for current playing file
CDVDDemux* m_pSubtitleDemuxer;
CDVDDemuxCC* m_pCCDemuxer;
CRenderManager m_renderManager;
struct SDVDInfo
{
void Clear()
{
state = DVDSTATE_NORMAL;
iSelectedSPUStream = -1;
iSelectedAudioStream = -1;
iSelectedVideoStream = -1;
iDVDStillTime = 0;
iDVDStillStartTime = 0;
syncClock = false;
}
int state; // current dvdstate
bool syncClock;
unsigned int iDVDStillTime; // total time in ticks we should display the still before continuing
unsigned int iDVDStillStartTime; // time in ticks when we started the still
int iSelectedSPUStream; // mpeg stream id, or -1 if disabled
int iSelectedAudioStream; // mpeg stream id, or -1 if disabled
int iSelectedVideoStream; // mpeg stream id or angle, -1 if disabled
} m_dvd;
friend class CVideoPlayerVideo;
friend class CVideoPlayerAudio;
#ifdef HAS_OMXPLAYER
friend class OMXPlayerVideo;
friend class OMXPlayerAudio;
#endif
SPlayerState m_State;
CCriticalSection m_StateSection;
XbmcThreads::EndTime m_syncTimer;
CEvent m_ready;
CEdl m_Edl;
bool m_SkipCommercials;
CPlayerOptions m_PlayerOptions;
bool m_HasVideo;
bool m_HasAudio;
std::atomic<bool> m_displayLost;
// omxplayer variables
struct SOmxPlayerState m_OmxPlayerState;
bool m_omxplayer_mode; // using omxplayer acceleration
XbmcThreads::EndTime m_player_status_timer;
};
其中定义了很多诸如seek、pause函数,看完这个就确定我们找的VideoPlayer是没有错误的,而VideoPlayer extends Thread,这个子线程明显就是要解析视频的线程,查看当前thread的Process函数
void CVideoPlayer::Process()
{
CFFmpegLog::SetLogLevel(1);
if (!OpenInputStream())//创建输入流
{
m_bAbortRequest = true;
return;
}
if (CDVDInputStream::IMenus* ptr = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream))
{
CLog::Log(LOGNOTICE, "VideoPlayer: playing a file with menu's");
if(dynamic_cast<CDVDInputStreamNavigator*>(m_pInputStream))
m_PlayerOptions.starttime = 0;
if(!m_PlayerOptions.state.empty())
ptr->SetState(m_PlayerOptions.state);
else if(CDVDInputStreamNavigator* nav = dynamic_cast<CDVDInputStreamNavigator*>(m_pInputStream))
nav->EnableSubtitleStream(CMediaSettings::GetInstance().GetCurrentVideoSettings().m_SubtitleOn);
CMediaSettings::GetInstance().GetCurrentVideoSettings().m_SubtitleCached = true;
}
if(!OpenDemuxStream())//创建demux
{
m_bAbortRequest = true;
return;
}
// give players a chance to reconsider now codecs are known
CreatePlayers();
// allow renderer to switch to fullscreen if requested
m_VideoPlayerVideo->EnableFullscreen(m_PlayerOptions.fullscreen);
if (m_omxplayer_mode)
{
if (!m_OmxPlayerState.av_clock.OMXInitialize(&m_clock))
m_bAbortRequest = true;
if (CSettings::GetInstance().GetInt(CSettings::SETTING_VIDEOPLAYER_ADJUSTREFRESHRATE) != ADJUST_REFRESHRATE_OFF)
m_OmxPlayerState.av_clock.HDMIClockSync();
m_OmxPlayerState.av_clock.OMXStateIdle();
m_OmxPlayerState.av_clock.OMXStateExecute();
m_OmxPlayerState.av_clock.OMXStop();
m_OmxPlayerState.av_clock.OMXPause();
}
OpenDefaultStreams();
// look for any EDL files
m_Edl.Clear();
if (m_CurrentVideo.id >= 0 && m_CurrentVideo.hint.fpsrate > 0 && m_CurrentVideo.hint.fpsscale > 0)
{
float fFramesPerSecond = (float)m_CurrentVideo.hint.fpsrate / (float)m_CurrentVideo.hint.fpsscale;
m_Edl.ReadEditDecisionLists(m_item.GetPath(), fFramesPerSecond, m_CurrentVideo.hint.height);
}
/*
* Check to see if the demuxer should start at something other than time 0. This will be the case
* if there was a start time specified as part of the "Start from where last stopped" (aka
* auto-resume) feature or if there is an EDL cut or commercial break that starts at time 0.
*/
CEdl::Cut cut;
int starttime = 0;
if (m_PlayerOptions.starttime > 0 || m_PlayerOptions.startpercent > 0)
{
if (m_PlayerOptions.startpercent > 0 && m_pDemuxer)
{
int playerStartTime = (int)( ( (float) m_pDemuxer->GetStreamLength() ) * ( m_PlayerOptions.startpercent/(float)100 ) );
starttime = m_Edl.RestoreCutTime(playerStartTime);
}
else
{
starttime = m_Edl.RestoreCutTime(m_PlayerOptions.starttime * 1000); // s to ms
}
CLog::Log(LOGDEBUG, "%s - Start position set to last stopped position: %d", __FUNCTION__, starttime);
}
else if (m_Edl.InCut(starttime, &cut))
{
if (cut.action == CEdl::CUT)
{
starttime = cut.end;
CLog::Log(LOGDEBUG, "%s - Start position set to end of first cut: %d", __FUNCTION__, starttime);
}
else if (cut.action == CEdl::COMM_BREAK)
{
if (m_SkipCommercials)
{
starttime = cut.end;
CLog::Log(LOGDEBUG, "%s - Start position set to end of first commercial break: %d", __FUNCTION__, starttime);
}
std::string strTimeString = StringUtils::SecondsToTimeString(cut.end / 1000, TIME_FORMAT_MM_SS);
CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(25011), strTimeString);
}
}
if (starttime > 0)//判断上次播放时长,选择从上次播放时开始播放
{
double startpts = DVD_NOPTS_VALUE;
if (m_pDemuxer)
{
if (m_pDemuxer->SeekTime(starttime, false, &startpts))
CLog::Log(LOGDEBUG, "%s - starting demuxer from: %d", __FUNCTION__, starttime);
else
CLog::Log(LOGDEBUG, "%s - failed to start demuxing from: %d", __FUNCTION__, starttime);
}
if (m_pSubtitleDemuxer)
{
if(m_pSubtitleDemuxer->SeekTime(starttime, false, &startpts))
CLog::Log(LOGDEBUG, "%s - starting subtitle demuxer from: %d", __FUNCTION__, starttime);
else
CLog::Log(LOGDEBUG, "%s - failed to start subtitle demuxing from: %d", __FUNCTION__, starttime);
}
m_clock.Discontinuity(DVD_MSEC_TO_TIME(starttime));
}
// make sure application know our info
UpdateApplication(0);
UpdatePlayState(0);
if(m_PlayerOptions.identify == false)
m_callback.OnPlayBackStarted();
// we are done initializing now, set the readyevent
m_ready.Set();
SetCaching(CACHESTATE_FLUSH);
while (!m_bAbortRequest)//开启循环demux,将数据抛给下层decode
{
#ifdef HAS_OMXPLAYER
if (m_omxplayer_mode && OMXDoProcessing(m_OmxPlayerState, m_playSpeed, m_VideoPlayerVideo, m_VideoPlayerAudio, m_CurrentAudio, m_CurrentVideo, m_HasVideo, m_HasAudio, m_renderManager))
{
CloseStream(m_CurrentVideo, false);
OpenStream(m_CurrentVideo, m_CurrentVideo.demuxerId, m_CurrentVideo.id, m_CurrentVideo.source);
if (m_State.canseek)
{
CDVDMsgPlayerSeek::CMode mode;
mode.time = (int)GetTime();
mode.backward = true;
mode.accurate = true;
mode.sync = true;
m_messenger.Put(new CDVDMsgPlayerSeek(mode));
}
}
#endif
// check display lost
if (m_displayLost)
{
Sleep(50);
continue;
}
// check if in a cut or commercial break that should be automatically skipped
CheckAutoSceneSkip();
// handle messages send to this thread, like seek or demuxer reset requests
HandleMessages();
if(m_bAbortRequest)
break;
// should we open a new input stream?
if(!m_pInputStream)
{
if (OpenInputStream() == false)
{
m_bAbortRequest = true;
break;
}
}
// should we open a new demuxer?
if(!m_pDemuxer)
{
if (m_pInputStream->NextStream() == CDVDInputStream::NEXTSTREAM_NONE)
break;
if (m_pInputStream->IsEOF())
break;
if (OpenDemuxStream() == false)
{
m_bAbortRequest = true;
break;
}
// on channel switch we don't want to close stream players at this
// time. we'll get the stream change event later
if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) ||
!m_SelectionStreams.m_Streams.empty())
OpenDefaultStreams();
UpdateApplication(0);
UpdatePlayState(0);
}
// handle eventual seeks due to playspeed
HandlePlaySpeed();
// update player state
UpdatePlayState(200);
// update application with our state
UpdateApplication(1000);
// make sure we run subtitle process here
m_VideoPlayerSubtitle->Process(m_clock.GetClock() + m_State.time_offset - m_VideoPlayerVideo->GetSubtitleDelay(), m_State.time_offset);
if (CheckDelayedChannelEntry())
continue;
// if the queues are full, no need to read more
if ((!m_VideoPlayerAudio->AcceptsData() && m_CurrentAudio.id >= 0) ||
(!m_VideoPlayerVideo->AcceptsData() && m_CurrentVideo.id >= 0))
{
Sleep(10);
continue;
}
// always yield to players if they have data levels > 50 percent
if((m_VideoPlayerAudio->GetLevel() > 50 || m_CurrentAudio.id < 0)
&& (m_VideoPlayerVideo->GetLevel() > 50 || m_CurrentVideo.id < 0))
Sleep(0);
DemuxPacket* pPacket = NULL;
CDemuxStream *pStream = NULL;
ReadPacket(pPacket, pStream);//开始将流封包,方便传给下一层decode
if (pPacket && !pStream)
{
/* probably a empty packet, just free it and move on */
CDVDDemuxUtils::FreeDemuxPacket(pPacket);
continue;
}
if (!pPacket)
{
// when paused, demuxer could be be returning empty
if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
continue;
// check for a still frame state
if (CDVDInputStream::IMenus* pStream = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream))
{
// stills will be skipped
if(m_dvd.state == DVDSTATE_STILL)
{
if (m_dvd.iDVDStillTime > 0)
{
if ((XbmcThreads::SystemClockMillis() - m_dvd.iDVDStillStartTime) >= m_dvd.iDVDStillTime)
{
m_dvd.iDVDStillTime = 0;
m_dvd.iDVDStillStartTime = 0;
m_dvd.state = DVDSTATE_NORMAL;
pStream->SkipStill();
continue;
}
}
}
}
// if there is another stream available, reopen demuxer
CDVDInputStream::ENextStream next = m_pInputStream->NextStream();
if(next == CDVDInputStream::NEXTSTREAM_OPEN)
{
CloseDemuxer();
SetCaching(CACHESTATE_DONE);
CLog::Log(LOGNOTICE, "VideoPlayer: next stream, wait for old streams to be finished");
CloseStream(m_CurrentAudio, true);
CloseStream(m_CurrentVideo, true);
m_CurrentAudio.Clear();
m_CurrentVideo.Clear();
m_CurrentSubtitle.Clear();
continue;
}
// input stream asked us to just retry
if(next == CDVDInputStream::NEXTSTREAM_RETRY)
{
Sleep(100);
continue;
}
// make sure we tell all players to finish it's data
if (m_omxplayer_mode && !m_OmxPlayerState.bOmxSentEOFs)
{
if(m_CurrentAudio.inited)
m_OmxPlayerState.bOmxWaitAudio = true;
if(m_CurrentVideo.inited)
m_OmxPlayerState.bOmxWaitVideo = true;
m_OmxPlayerState.bOmxSentEOFs = true;
}
if(m_CurrentAudio.inited)
m_VideoPlayerAudio->SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF));
if(m_CurrentVideo.inited)
m_VideoPlayerVideo->SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF));
if(m_CurrentSubtitle.inited)
m_VideoPlayerSubtitle->SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF));
if(m_CurrentTeletext.inited)
m_VideoPlayerTeletext->SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF));
if(m_CurrentRadioRDS.inited)
m_VideoPlayerRadioRDS->SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF));
m_CurrentAudio.inited = false;
m_CurrentVideo.inited = false;
m_CurrentSubtitle.inited = false;
m_CurrentTeletext.inited = false;
m_CurrentRadioRDS.inited = false;
// if we are caching, start playing it again
SetCaching(CACHESTATE_DONE);
// while players are still playing, keep going to allow seekbacks
if(m_VideoPlayerAudio->HasData()
|| m_VideoPlayerVideo->HasData())
{
Sleep(100);
continue;
}
#ifdef HAS_OMXPLAYER
if (m_omxplayer_mode && OMXStillPlaying(m_OmxPlayerState.bOmxWaitVideo, m_OmxPlayerState.bOmxWaitAudio, m_VideoPlayerVideo->IsEOS(), m_VideoPlayerAudio->IsEOS()))
{
Sleep(100);
continue;
}
#endif
if (!m_pInputStream->IsEOF())
CLog::Log(LOGINFO, "%s - eof reading from demuxer", __FUNCTION__);
break;
}
// it's a valid data packet, reset error counter
m_errorCount = 0;
// see if we can find something better to play
CheckBetterStream(m_CurrentAudio, pStream);
CheckBetterStream(m_CurrentVideo, pStream);
CheckBetterStream(m_CurrentSubtitle, pStream);
CheckBetterStream(m_CurrentTeletext, pStream);
CheckBetterStream(m_CurrentRadioRDS, pStream);
// demux video stream
if (CSettings::GetInstance().GetBool(CSettings::SETTING_SUBTITLES_PARSECAPTIONS) && CheckIsCurrent(m_CurrentVideo, pStream, pPacket))
{
if (m_pCCDemuxer)
{
bool first = true;
while(!m_bAbortRequest)
{
DemuxPacket *pkt = m_pCCDemuxer->Read(first ? pPacket : NULL);
if (!pkt)
break;
first = false;
if (m_pCCDemuxer->GetNrOfStreams() != m_SelectionStreams.CountSource(STREAM_SUBTITLE, STREAM_SOURCE_VIDEOMUX))
{
m_SelectionStreams.Clear(STREAM_SUBTITLE, STREAM_SOURCE_VIDEOMUX);
m_SelectionStreams.Update(NULL, m_pCCDemuxer, "");
OpenDefaultStreams(false);
}
CDemuxStream *pSubStream = m_pCCDemuxer->GetStream(pkt->iStreamId);
if (pSubStream && m_CurrentSubtitle.id == pkt->iStreamId && m_CurrentSubtitle.source == STREAM_SOURCE_VIDEOMUX)
ProcessSubData(pSubStream, pkt);
else
CDVDDemuxUtils::FreeDemuxPacket(pkt);
}
}
}
if (IsInMenuInternal())
{
if (CDVDInputStream::IMenus* menu = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream))
{
double correction = menu->GetTimeStampCorrection();
if (pPacket->dts != DVD_NOPTS_VALUE && pPacket->dts > correction)
pPacket->dts -= correction;
if (pPacket->pts != DVD_NOPTS_VALUE && pPacket->pts > correction)
pPacket->pts -= correction;
}
if (m_dvd.syncClock)
{
m_clock.Discontinuity(pPacket->dts);
m_dvd.syncClock = false;
}
}
// process the packet
ProcessPacket(pStream, pPacket);//将音频流、视频流、字幕流等分开,创建decode的Player并将数据传递给他们
// update the player info for streams
if (m_player_status_timer.IsTimePast())
{
m_player_status_timer.Set(500);
UpdateStreamInfos();
}
}
}
第一步是OpenInputStream,调用
bool CVideoPlayer::OpenInputStream()
{
if(m_pInputStream)
SAFE_DELETE(m_pInputStream);
CLog::Log(LOGNOTICE, "Creating InputStream");
// correct the filename if needed
std::string filename(m_item.GetPath());
if (URIUtils::IsProtocol(filename, "dvd") ||
StringUtils::EqualsNoCase(filename, "iso9660://video_ts/video_ts.ifo"))
{
m_item.SetPath(g_mediaManager.TranslateDevicePath(""));
}
m_pInputStream = CDVDFactoryInputStream::CreateInputStream(this, m_item, true);//创建
if(m_pInputStream == NULL)
{
CLog::Log(LOGERROR, "CVideoPlayer::OpenInputStream - unable to create input stream for [%s]", CURL::GetRedacted(m_item.GetPath()).c_str());
return false;
}
if (!m_pInputStream->Open())
{
CLog::Log(LOGERROR, "CVideoPlayer::OpenInputStream - error opening [%s]", CURL::GetRedacted(m_item.GetPath()).c_str());
return false;
}
// find any available external subtitles for non dvd files
if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)
&& !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)
&& !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
{
// find any available external subtitles
std::vector<std::string> filenames;
CUtil::ScanForExternalSubtitles(m_item.GetPath(), filenames);
// load any subtitles from file item
std::string key("subtitle:1");
for(unsigned s = 1; m_item.HasProperty(key); key = StringUtils::Format("subtitle:%u", ++s))
filenames.push_back(m_item.GetProperty(key).asString());
for(unsigned int i=0;i<filenames.size();i++)
{
// if vobsub subtitle:
if (URIUtils::HasExtension(filenames[i], ".idx"))
{
std::string strSubFile;
if ( CUtil::FindVobSubPair( filenames, filenames[i], strSubFile ) )
AddSubtitleFile(filenames[i], strSubFile);
}
else
{
if ( !CUtil::IsVobSub(filenames, filenames[i] ) )
{
AddSubtitleFile(filenames[i]);
}
}
} // end loop over all subtitle files
CMediaSettings::GetInstance().GetCurrentVideoSettings().m_SubtitleCached = true;
}
SetAVDelay(CMediaSettings::GetInstance().GetCurrentVideoSettings().m_AudioDelay);
SetSubTitleDelay(CMediaSettings::GetInstance().GetCurrentVideoSettings().m_SubtitleDelay);
m_clock.Reset();
m_dvd.Clear();
m_errorCount = 0;
m_ChannelEntryTimeOut.SetInfinite();
if (CSettings::GetInstance().GetBool(CSettings::SETTING_VIDEOPLAYER_USEDISPLAYASCLOCK) &&
!m_pInputStream->IsRealtime())
{
m_canTempo = true;
}
else
{
m_canTempo = false;
}
return true;
}
调用m_pInputStream = CDVDFactoryInputStream::CreateInputStream(this, m_item, true);创建,查看位于xbmc/cores/VideoPlayer/DVDInputStreams/DVDFactoryInputStream.cpp
CDVDInputStream* CDVDFactoryInputStream::CreateInputStream(IVideoPlayer* pPlayer, const CFileItem &fileitem, bool scanforextaudio)//根据文件类型创建不同的InputStream
{
std::string file = fileitem.GetPath();
if (scanforextaudio)
{
// find any available external audio tracks
std::vector<std::string> filenames;
filenames.push_back(file);
CUtil::ScanForExternalAudio(file, filenames);
CUtil::ScanForExternalDemuxSub(file, filenames);
if (filenames.size() >= 2)
{
return CreateInputStream(pPlayer, fileitem, filenames);
}
}
ADDON::VECADDONS addons;
ADDON::CBinaryAddonCache &addonCache = CServiceBroker::GetBinaryAddonCache();
addonCache.GetAddons(addons, ADDON::ADDON_INPUTSTREAM);
for (size_t i=0; i<addons.size(); ++i)
{
std::shared_ptr<ADDON::CInputStream> input(std::static_pointer_cast<ADDON::CInputStream>(addons[i]));
if (input->Supports(fileitem))
{
std::shared_ptr<ADDON::CInputStream> addon = input;
if (!input->UseParent())
addon = std::shared_ptr<ADDON::CInputStream>(new ADDON::CInputStream(*input));
ADDON_STATUS status = addon->Create();
if (status == ADDON_STATUS_OK)
{
unsigned int videoWidth, videoHeight;
pPlayer->GetVideoResolution(videoWidth, videoHeight);
addon->SetVideoResolution(videoWidth, videoHeight);
return new CInputStreamAddon(fileitem, addon);
}
}
}
if (fileitem.IsDiscImage())
{
#ifdef HAVE_LIBBLURAY
CURL url("udf://");
url.SetHostName(file);
url.SetFileName("BDMV/index.bdmv");
if(XFILE::CFile::Exists(url.Get()))
return new CDVDInputStreamBluray(pPlayer, fileitem);
#endif
return new CDVDInputStreamNavigator(pPlayer, fileitem);
}
#ifdef HAS_DVD_DRIVE
if(file.compare(g_mediaManager.TranslateDevicePath("")) == 0)
{
#ifdef HAVE_LIBBLURAY
if(XFILE::CFile::Exists(URIUtils::AddFileToFolder(file, "BDMV", "index.bdmv")))
return new CDVDInputStreamBluray(pPlayer, fileitem);
#endif
return new CDVDInputStreamNavigator(pPlayer, fileitem);
}
#endif
if (fileitem.IsDVDFile(false, true))
return (new CDVDInputStreamNavigator(pPlayer, fileitem));
else if(file.substr(0, 6) == "pvr://")
return new CDVDInputStreamPVRManager(pPlayer, fileitem);
#ifdef HAVE_LIBBLURAY
else if (fileitem.IsType(".bdmv") || fileitem.IsType(".mpls") || file.substr(0, 7) == "bluray:")
return new CDVDInputStreamBluray(pPlayer, fileitem);
#endif
else if(file.substr(0, 6) == "rtp://"
|| file.substr(0, 7) == "rtsp://"
|| file.substr(0, 6) == "sdp://"
|| file.substr(0, 6) == "udp://"
|| file.substr(0, 6) == "tcp://"
|| file.substr(0, 6) == "mms://"
|| file.substr(0, 7) == "mmst://"
|| file.substr(0, 7) == "mmsh://")
return new CDVDInputStreamFFmpeg(fileitem);
#ifdef ENABLE_DVDINPUTSTREAM_STACK
else if(file.substr(0, 8) == "stack://")
return new CDVDInputStreamStack(fileitem);
#endif
else if(file.substr(0, 7) == "rtmp://"
|| file.substr(0, 8) == "rtmpt://"
|| file.substr(0, 8) == "rtmpe://"
|| file.substr(0, 9) == "rtmpte://"
|| file.substr(0, 8) == "rtmps://")
return new CDVDInputStreamFFmpeg(fileitem);
CFileItem finalFileitem(fileitem);
if (finalFileitem.IsInternetStream())
{
if (finalFileitem.ContentLookup())
{
CURL origUrl(finalFileitem.GetURL());
XFILE::CCurlFile curlFile;
// try opening the url to resolve all redirects if any
try
{
if (curlFile.Open(finalFileitem.GetURL()))
{
CURL finalUrl(curlFile.GetURL());
finalUrl.SetProtocolOptions(origUrl.GetProtocolOptions());
finalUrl.SetUserName(origUrl.GetUserName());
finalUrl.SetPassword(origUrl.GetPassWord());
finalFileitem.SetPath(finalUrl.Get());
}
curlFile.Close();
}
catch (XFILE::CRedirectException *pRedirectEx)
{
if (pRedirectEx)
{
delete pRedirectEx->m_pNewFileImp;
delete pRedirectEx;
}
}
}
if (finalFileitem.IsType(".m3u8"))
return new CDVDInputStreamFFmpeg(finalFileitem);
if (finalFileitem.GetMimeType() == "application/vnd.apple.mpegurl")
return new CDVDInputStreamFFmpeg(finalFileitem);
if (URIUtils::IsProtocol(finalFileitem.GetPath(), "udp"))
return new CDVDInputStreamFFmpeg(finalFileitem);
}
// our file interface handles all these types of streams
return (new CDVDInputStreamFile(finalFileitem));
}
其中很容易理解,kodi是一个全能的播放器,会支持许许多多的InputStream,根据类型判断初始化什么播放器,而DVDInputStreamFFmpeg能获取很多种类型的inputStream,下篇我们主要讲高大上的DVDInputStreamDDmpeg。