分享

KODI(原XBMC)二次开发完全解析(二)

 昵称14340561 2020-08-07

  使用过kodi的都知道,kodi播放之前有一个影片列表页面,可以变换LowPanel,list等形式,那个页面对应的源代码路径就是/xbmc/video/windows/GUIVideoWindowNav.cpp,,这个cpp文件里面,可以处理一系列包括变换模板播放视频等操作,本篇只介绍播放视频的创建player逻辑。

  最直观的的入口/xbmc/video/windows/GUIVideoWindowBase.cpp,GUIVideoWindowNav.cpp就是extends这个cpp,直接索引到

void CGUIWindowVideoBase :: PlayMovie(const CFileItem * item,const std :: string&player){CFileItemPtr movieItem(new CFileItem(* item));//创建一个FileItemg_playlistPlayer.Reset();g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_VIDEO);CPlayList& playlist = g_playlistPlayer.GetPlaylist(PLAYLIST_VIDEO);playlist.Clear();playlist.Add(movieItem);//添加一个playlistif(m_thumbLoader.IsLoading())m_thumbLoader.StopAsync();// play movie...g_playlistPlayer.Play(0,player);//调用一个全局的player播放if(!g_application.m_pPlayer-> IsPlayingVideo())m_thumbLoader.Load(* m_vecItems);}

(代码中有一个构建播放列表的操作,这个也可以用作python addon调播播放,播放列表顾名思义可以放很多项目,还可以设置循环播放,kodi支持局域网内部端口调用以及自定义的插件调用,可以在后台或者外部偷偷做一些事~~咳~~具体就不再多说了,有需要可以参考wiki https:///view/Main_Page或者文末留言)

  下面看一看这个g_playlistPlayer.Play函数,在xbmc/PlayListPlayer.cpp,只贴关键部分

bool CPlayListPlayer::Play(int iSong, std::string player, bool bAutoPlay /* = false */, bool bPlayPrevious /* = false */){if (m_iCurrentPlayList == PLAYLIST_NONE)return false;CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);if (playlist.size() <= 0)return false;if (iSong < 0)iSong = 0;if (iSong >= playlist.size())iSong = playlist.size() - 1;m_iCurrentSong = iSong;CFileItemPtr item = playlist[m_iCurrentSong];playlist.SetPlayed(true);m_bPlaybackStarted = false;unsigned int playAttempt = XbmcThreads::SystemClockMillis();PlayBackRet ret = g_application.PlayFile(*item, player, bAutoPlay);//去播放return true;}
  查找g_application,这是一个Application全局的instance,在xbmc/Application.cpp
PlayBackRet CApplication::PlayFile(CFileItem item, const std::string& player, bool bRestart){// Ensure the MIME type has been retrieved for http:// and shout:// streamsif (item.GetMimeType().empty())item.FillInMimeType();if (!bRestart){SaveFileState(true);// Switch to default optionsCMediaSettings::GetInstance().GetCurrentVideoSettings() = CMediaSettings::GetInstance().GetDefaultVideoSettings();CMediaSettings::GetInstance().GetCurrentAudioSettings() = CMediaSettings::GetInstance().GetDefaultAudioSettings();// see if we have saved options in the databasem_pPlayer->SetPlaySpeed(1);m_itemCurrentFile.reset(new CFileItem(item));m_nextPlaylistItem = -1;m_currentStackPosition = 0;m_currentStack->Clear();if (item.IsVideo())CUtil::ClearSubtitles();}// if we have a stacked set of files, we need to setup our stack routines for// "seamless" seeking and total time of the movie etc.// will recall with restart set to trueif (item.IsStack())return PlayStack(item, bRestart);CPlayerOptions options;if (bRestart){// have to be set here due to playstack using this for starting the fileoptions.starttime = item.m_lStartOffset / 75.0;if (m_itemCurrentFile->IsStack() && m_currentStack->Size() > 0 && m_itemCurrentFile->m_lStartOffset != 0)m_itemCurrentFile->m_lStartOffset = STARTOFFSET_RESUME; // to force fullscreen switching}else{options.starttime = item.m_lStartOffset / 75.0;LoadVideoSettings(item);if (item.IsVideo()){// open the d/b and retrieve the bookmarks for the current movieCVideoDatabase dbs;dbs.Open();if( item.m_lStartOffset == STARTOFFSET_RESUME )//确定是不是继续上次播放的位置播放{options.starttime = 0.0f;CBookmark bookmark;std::string path = item.GetPath();if (item.HasVideoInfoTag() && StringUtils::StartsWith(item.GetVideoInfoTag()->m_strFileNameAndPath, "removable://"))path = item.GetVideoInfoTag()->m_strFileNameAndPath;else if (item.HasProperty("original_listitem_url") && URIUtils::IsPlugin(item.GetProperty("original_listitem_url").asString()))path = item.GetProperty("original_listitem_url").asString();if(dbs.GetResumeBookMark(path, bookmark)){options.starttime = bookmark.timeInSeconds;options.state = bookmark.playerState;}/*override with information from the actual item if available.  We do this as the VFS (eg plugins)may set the resume point to override whatever XBMC has stored, yet we ignore it until now so that,should the playerState be required, it is fetched from the database.See the note in CGUIWindowVideoBase::ShowResumeMenu.*/if (item.IsResumePointSet())options.starttime = item.GetCurrentResumeTime();else if (item.HasVideoInfoTag()){// No resume point is set, but check if this item is part of a multi-episode fileconst CVideoInfoTag *tag = item.GetVideoInfoTag();if (tag->m_iBookmarkId > 0){CBookmark bookmark;dbs.GetBookMarkForEpisode(*tag, bookmark);options.starttime = bookmark.timeInSeconds;options.state = bookmark.playerState;}}}else if (item.HasVideoInfoTag()){const CVideoInfoTag *tag = item.GetVideoInfoTag();if (tag->m_iBookmarkId > 0){CBookmark bookmark;dbs.GetBookMarkForEpisode(*tag, bookmark);options.starttime = bookmark.timeInSeconds;options.state = bookmark.playerState;}}dbs.Close();}}// this really aught to be inside !bRestart, but since PlayStack// uses that to init playback, we have to keep it outsideint playlist = g_playlistPlayer.GetCurrentPlaylist();//获取播放列表if (item.IsVideo() && playlist == PLAYLIST_VIDEO && g_playlistPlayer.GetPlaylist(playlist).size() > 1){ // playing from a playlist by the looks// don't switch to fullscreen if we are not playing the first item...options.fullscreen = !g_playlistPlayer.HasPlayedFirstFile() && g_advancedSettings.m_fullScreenOnMovieStart && !CMediaSettings::GetInstance().DoesVideoStartWindowed();}else if(m_itemCurrentFile->IsStack() && m_currentStack->Size() > 0){//! @todo - this will fail if user seeks back to first file in stackif(m_currentStackPosition == 0 || m_itemCurrentFile->m_lStartOffset == STARTOFFSET_RESUME)options.fullscreen = g_advancedSettings.m_fullScreenOnMovieStart && !CMediaSettings::GetInstance().DoesVideoStartWindowed();elseoptions.fullscreen = false;// reset this so we don't think we are resuming on seekm_itemCurrentFile->m_lStartOffset = 0;}elseoptions.fullscreen = g_advancedSettings.m_fullScreenOnMovieStart && !CMediaSettings::GetInstance().DoesVideoStartWindowed();// reset VideoStartWindowed as it's a temp settingCMediaSettings::GetInstance().SetVideoStartWindowed(false);{CSingleLock lock(m_playStateMutex);// tell system we are starting a filem_bPlaybackStarting = true;// for playing a new item, previous playing item's callback may already// pushed some delay message into the threadmessage list, they are not// expected be processed after or during the new item playback starting.// so we clean up previous playing item's playback callback delay messages here.int previousMsgsIgnoredByNewPlaying[] = {GUI_MSG_PLAYBACK_STARTED,GUI_MSG_PLAYBACK_ENDED,GUI_MSG_PLAYBACK_STOPPED,GUI_MSG_PLAYLIST_CHANGED,GUI_MSG_PLAYLISTPLAYER_STOPPED,GUI_MSG_PLAYLISTPLAYER_STARTED,GUI_MSG_PLAYLISTPLAYER_CHANGED,GUI_MSG_QUEUE_NEXT_ITEM,0};int dMsgCount = g_windowManager.RemoveThreadMessageByMessageIds(&previousMsgsIgnoredByNewPlaying[0]);if (dMsgCount > 0)CLog::LogF(LOGDEBUG,"Ignored %d playback thread messages", dMsgCount);}std::string newPlayer;//创建playerif (!player.empty())newPlayer = player;else if (bRestart && !m_pPlayer->GetCurrentPlayer().empty())newPlayer = m_pPlayer->GetCurrentPlayer();elsenewPlayer = CPlayerCoreFactory::GetInstance().GetDefaultPlayer(item);// We should restart the player, unless the previous and next tracks are using// one of the players that allows gapless playback (paplayer, VideoPlayer)m_pPlayer->ClosePlayerGapless(newPlayer);// now reset play state to starting, since we already stopped the previous playing item if there is.// and from now there should be no playback callback from previous playing item be called.m_ePlayState = PLAY_STATE_STARTING;m_pPlayer->CreatePlayer(newPlayer, *this);//创建player,下篇重点讲PlayBackRet iResult;if (m_pPlayer->HasPlayer()){/* When playing video pause any low priority jobs, they will be unpaused  when playback stops.* This should speed up player startup for files on internet filesystems (eg. webdav) and* increase performance on low powered systems (Atom/ARM).*/if (item.IsVideo()){CJobManager::GetInstance().PauseJobs();}// don't hold graphicscontext here since player// may wait on another thread, that requires gfxCSingleExit ex(g_graphicsContext);iResult = m_pPlayer->OpenFile(item, options);}else{CLog::Log(LOGERROR, "Error creating player for item %s (File doesn't exist?)", item.GetPath().c_str());iResult = PLAYBACK_FAIL;}if (iResult == PLAYBACK_OK){m_pPlayer->SetVolume(m_volumeLevel);m_pPlayer->SetMute(m_muted);if(m_pPlayer->IsPlayingAudio()){if (g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO)g_windowManager.ActivateWindow(WINDOW_VISUALISATION);}#ifdef HAS_VIDEO_PLAYBACKelse if(m_pPlayer->IsPlayingVideo()){// if player didn't manage to switch to fullscreen by itself do it hereif (options.fullscreen && m_pPlayer->IsRenderingVideo() &&g_windowManager.GetActiveWindow() != WINDOW_FULLSCREEN_VIDEO )SwitchToFullScreen(true);//切换全屏模式}#endifelse{if (g_windowManager.GetActiveWindow() == WINDOW_VISUALISATION ||g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO)g_windowManager.PreviousWindow();}#if !defined(TARGET_POSIX)g_audioManager.Enable(false);#endifif (item.HasPVRChannelInfoTag())g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_NONE);}CSingleLock lock(m_playStateMutex);m_bPlaybackStarting = false;if (iResult == PLAYBACK_OK){// play state: none, starting; playing; stopped; ended.// last 3 states are set by playback callback, they are all ignored during starting,// but we recorded the state, here we can make up the callback for the state.CLog::LogF(LOGDEBUG,"OpenFile succeed, play state %d", m_ePlayState);switch (m_ePlayState){case PLAY_STATE_PLAYING:OnPlayBackStarted();//通知其他所有平台,播放开始break;// FIXME: it seems no meaning to callback started here if there was an started callback//        before this stopped/ended callback we recorded. if we callback started here//        first, it will delay send OnPlay announce, but then we callback stopped/ended//        which will send OnStop announce at once, so currently, just call stopped/ended.case PLAY_STATE_ENDED:OnPlayBackEnded();//播放结束break;case PLAY_STATE_STOPPED:OnPlayBackStopped();//播放停止break;case PLAY_STATE_STARTING:// neither started nor stopped/ended callback be called, that means the item still// not started, we need not make up any callback, just leave this and// let the player callback do its work.break;default:break;}}else if (iResult == PLAYBACK_FAIL){// we send this if it isn't playlistplayer that is doing thisint next = g_playlistPlayer.GetNextSong();int size = g_playlistPlayer.GetPlaylist(g_playlistPlayer.GetCurrentPlaylist()).size();if(next < 0|| next >= size)OnPlayBackStopped();m_ePlayState = PLAY_STATE_NONE;}return iResult;}
  player创建及使用下篇再讲,主要涉及到demux及解封装,下篇再见。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多