使用过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));//创建一个FileItem
g_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://kodi.wiki/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及解封装,下篇再见。

KODI(原XBMC)二次开发完全解析(二)-------创建player相关推荐

  1. KODI(原XBMC)二次开发完全解析(三)-------获取视频输入流

    上篇讲到player的建立流程KODI(原XBMC)二次开发完全解析(二)-------创建player,接着上篇确定哪一个才是真正干活的player. m_pPlayer->CreatePla ...

  2. 利用ZXing工具生成二维码以及解析二维码

    今天突然想到二维码是如何存储信息的.于是就开始各种搜索,最终自己也利用Google的ZXing工具完成了一个生成二维码和解析二维码的简单程序. 一. 二维码生成原理(即工作原理) 二维码官方叫版本Ve ...

  3. catia二维图坐标如何表示_【二次开发】CATIA二维图一键标注投影点信息

    原标题:[二次开发]CATIA二维图一键标注投影点信息 CATIA软件不仅可以用来建立三维模型,用它来出二维图纸也是个不错的选择.且相对于传统的铅笔橡皮丁字尺和AutoCAD来说,CATIA二维图中的 ...

  4. llqrcode.js识别二维码,解析二维码信息

    llqrcode.js具有扫描二维码功能,用来进行从图片中识别二维码,可解析二维码的信息. 直接上代码 <!DOCTYPE html> <html> <head>& ...

  5. Revit二次开发——Ribbon菜单的创建以及各种不同的button(按钮)的代码总结

    目录 一.创建普通的一个panel里面三个32px*32px的pushbutton,剩下两个写法一样 二.创建三个层叠按钮 层叠按钮图标需为16px*16px 层叠按钮最多为一列放三个,这个也要创建个 ...

  6. pythoncad二次开发视频_revit二次开发|bim软件二次开发|revit二次开发教程|Revit二次开发技术文档...

    二次开发 revit二次开发|bim软件二次开发|revit二次开发教程|Revit二次开发技术文档2019-07-08赞( 0 ) 记录一下CAD二次开发的一些简单实例. 1.helloworld ...

  7. tekla二次开发用C语言,Tekla能够进行哪些二次开发?Tekla二次开发功能详解

    我们话不多说,这次小编来给你讲一些搜罗而来的Tekla软件二次开发的功能.虽然是科普,不过这些二次开发功能有的还真的是很好用的,帮了大忙. 一.桥梁功能 如果向Tekla插件中加入了桥梁模块的功能,通 ...

  8. lisp 圆柱螺旋线_Auto LISP对AutoCAD2002进行二次开发实例——绘制二维函数曲线

    Auto LISP 对 AutoCAD 2002 进行二次开发实例 ---绘制二维函数曲线Ξ李旭荣 ,任奕玲 ,梁秀英 ,刘梅英 (华中农业大学 工程技术学院 ,湖南 武汉 430070) 摘 要:主 ...

  9. Java利用Zxing生成二维码及解析二维码内容

    前言 Java 操作二维码的开源项目很多,如 SwetakeQRCode.BarCode4j.Zxing 等等 本篇文章是介绍利用Zxing来生成二维码图片在web网页上展示,同时解析二维码图片. Z ...

最新文章

  1. 交换机短路_你了解交换机的相关知识吗?还不赶快收藏起来
  2. poll()函数详解
  3. 阿伯特:芝加哥大学与社会学研究
  4. 最长递增字串的三种做法
  5. Android开发之发送短信
  6. linux内核中获取虚拟地址api,Linux内核-系统调用
  7. hdmi 屏幕旋转 树莓派_使用树莓派的轻量级远征工具套装
  8. 如何在小程序里面放入企业官网
  9. pp加速器各种问题官方最新回答
  10. html5 gif 只播放一次,使用JS和canvas实现gif动图的停止和播放代码
  11. Android 6.0(棉花糖)新特性
  12. oracle 序列和表关联,Oracle 创建和管理表、集群和序列
  13. TOGAF企业架构的主要内容——上海信息化培训中心
  14. 视频剪辑后期处理软件生态
  15. spring-依赖注入(DI)
  16. POJ 1036 Gangsters 笔记
  17. TP-LINK WDR6500 V6刷入Breed + OpenWRT
  18. 分享电脑浏览器上实用的speedceo插件,选中文字简单快速检索
  19. 一不小心踏进Android开发: TPMini大眼睛使用PS3蓝牙手柄(一)各种尝试(1)
  20. 2019最新后盾Thinkphp5 博客项目实战全套

热门文章

  1. 版权登记是对商标最好的保护!
  2. Jetpack组件WorkManager简单demo
  3. 如果你依旧,别来无恙
  4. Hive向表中导入数据报错:Invalid postscript
  5. 分组每一组数据最开始添加一行,内容与每组第二行一致
  6. Win7,64位设置成豆沙绿护眼颜色后,vc6的visual assist 颜色失效,解决方法
  7. Kafka 认证三:添加 Kerberos 认证详细流程
  8. 发现最优秀的人工智能对话体验
  9. 元宵节祝福短信(经典/祝福/搞笑)
  10. c++指针数组(入门)