`

XBMC源代码分析 4:视频播放器(dvdplayer)-解码器(以ffmpeg为例)

    博客分类:
  • XBMC
 
阅读更多

XBMC分析系列文章:

XBMC源代码分析 1:整体结构以及编译方法

XBMC源代码分析 2:Addons(皮肤Skin)

XBMC源代码分析 3:核心部分(core)-综述

 

本文我们分析XBMC中视频播放器(dvdplayer)中的解码器部分。由于解码器种类很多,不可能一一分析,因此以ffmpeg解码器为例进行分析。

XBMC解码器部分文件目录如下图所示:

解码器分为音频解码器和视频解码器。在这里我们看一下视频解码器中的FFMPEG解码器。对应DVDVideoCodecFFmpeg.h和DVDVideoCodecFFmpeg.cpp。

DVDVideoCodecFFmpeg.h源代码如下所示:

 

/*
 * 雷霄骅
 * leixiaohua1020@126.com
 * 中国传媒大学/数字电视技术
 *
 */

#include "DVDVideoCodec.h"
#include "DVDResource.h"
#include "DllAvCodec.h"
#include "DllAvFormat.h"
#include "DllAvUtil.h"
#include "DllSwScale.h"
#include "DllAvFilter.h"
#include "DllPostProc.h"

class CCriticalSection;
//封装的FFMPEG视频解码器
class CDVDVideoCodecFFmpeg : public CDVDVideoCodec
{
public:
  class IHardwareDecoder : public IDVDResourceCounted<IHardwareDecoder>
  {
    public:
             IHardwareDecoder() {}
    virtual ~IHardwareDecoder() {};
    virtual bool Open      (AVCodecContext* avctx, const enum PixelFormat, unsigned int surfaces) = 0;
    virtual int  Decode    (AVCodecContext* avctx, AVFrame* frame) = 0;
    virtual bool GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture) = 0;
    virtual int  Check     (AVCodecContext* avctx) = 0;
    virtual void Reset     () {}
    virtual unsigned GetAllowedReferences() { return 0; }
    virtual const std::string Name() = 0;
    virtual CCriticalSection* Section() { return NULL; }
  };

  CDVDVideoCodecFFmpeg();
  virtual ~CDVDVideoCodecFFmpeg();
  virtual bool Open(CDVDStreamInfo &hints, CDVDCodecOptions &options);//打开
  virtual void Dispose();//关闭
  virtual int Decode(uint8_t* pData, int iSize, double dts, double pts);//解码
  virtual void Reset();
  bool GetPictureCommon(DVDVideoPicture* pDvdVideoPicture);
  virtual bool GetPicture(DVDVideoPicture* pDvdVideoPicture);
  virtual void SetDropState(bool bDrop);
  virtual unsigned int SetFilters(unsigned int filters);
  virtual const char* GetName() { return m_name.c_str(); }; // m_name is never changed after open
  virtual unsigned GetConvergeCount();
  virtual unsigned GetAllowedReferences();

  bool               IsHardwareAllowed()                     { return !m_bSoftware; }
  IHardwareDecoder * GetHardware()                           { return m_pHardware; };
  void               SetHardware(IHardwareDecoder* hardware) 
  {
    SAFE_RELEASE(m_pHardware);
    m_pHardware = hardware;
    UpdateName();
  }

protected:
  static enum PixelFormat GetFormat(struct AVCodecContext * avctx, const PixelFormat * fmt);

  int  FilterOpen(const CStdString& filters, bool scale);
  void FilterClose();
  int  FilterProcess(AVFrame* frame);

  void UpdateName()
  {
    if(m_pCodecContext->codec->name)
      m_name = CStdString("ff-") + m_pCodecContext->codec->name;
    else
      m_name = "ffmpeg";

    if(m_pHardware)
      m_name += "-" + m_pHardware->Name();
  }

  AVFrame* m_pFrame;
  AVCodecContext* m_pCodecContext;

  CStdString       m_filters;
  CStdString       m_filters_next;
  AVFilterGraph*   m_pFilterGraph;
  AVFilterContext* m_pFilterIn;
  AVFilterContext* m_pFilterOut;
#if defined(LIBAVFILTER_AVFRAME_BASED)
  AVFrame*         m_pFilterFrame;
#else
  AVFilterBufferRef* m_pBufferRef;
#endif

  int m_iPictureWidth;
  int m_iPictureHeight;

  int m_iScreenWidth;
  int m_iScreenHeight;
  int m_iOrientation;// orientation of the video in degress counter clockwise

  unsigned int m_uSurfacesCount;
  //封装Dll的各种类
  DllAvCodec m_dllAvCodec;
  DllAvUtil  m_dllAvUtil;
  DllSwScale m_dllSwScale;
  DllAvFilter m_dllAvFilter;
  DllPostProc m_dllPostProc;

  std::string m_name;
  bool              m_bSoftware;
  bool  m_isHi10p;
  IHardwareDecoder *m_pHardware;
  int m_iLastKeyframe;
  double m_dts;
  bool   m_started;
  std::vector<PixelFormat> m_formats;
};

该类中以下几个函数包含了解码器的几种功能:

 

virtual bool Open(CDVDStreamInfo &hints, CDVDCodecOptions &options);//打开
virtual void Dispose();//关闭
virtual int Decode(uint8_t* pData, int iSize, double dts, double pts);//解码
virtual void Reset();//复位

为了说明这一点,我们可以看一下视频解码器中的libmpeg2解码器,对应DVDVideoCodecLibMpeg2.h。可以看出这几个函数是一样的。

DVDVideoCodecLibMpeg2.h源代码如下:

 

/*
 * 雷霄骅
 * leixiaohua1020@126.com
 * 中国传媒大学/数字电视技术
 *
 */
#include "DVDVideoCodec.h"
#include "DllLibMpeg2.h"

class CDVDVideoCodecLibMpeg2 : public CDVDVideoCodec
{
public:
  CDVDVideoCodecLibMpeg2();
  virtual ~CDVDVideoCodecLibMpeg2();
  virtual bool Open(CDVDStreamInfo &hints, CDVDCodecOptions &options);
  virtual void Dispose();
  virtual int Decode(uint8_t* pData, int iSize, double dts, double pts);
  virtual void Reset();
  virtual bool GetPicture(DVDVideoPicture* pDvdVideoPicture);
  virtual bool GetUserData(DVDVideoUserData* pDvdVideoUserData);

  virtual void SetDropState(bool bDrop);
  virtual const char* GetName() { return "libmpeg2"; }

protected:
  DVDVideoPicture* GetBuffer(unsigned int width, unsigned int height);
  inline void ReleaseBuffer(DVDVideoPicture* pPic);
  inline void DeleteBuffer(DVDVideoPicture* pPic);

  static int GuessAspect(const mpeg2_sequence_t *sequence, unsigned int *pixel_width, unsigned int *pixel_height);

  mpeg2dec_t* m_pHandle;
  const mpeg2_info_t* m_pInfo;
  DllLibMpeg2 m_dll;

  unsigned int m_irffpattern;
  bool m_bFilm; //Signals that we have film material
  bool m_bIs422;

  int m_hurry;
  double m_dts;
  double m_dts2;
  //The buffer of pictures we need
  DVDVideoPicture m_pVideoBuffer[3];
  DVDVideoPicture* m_pCurrentBuffer;
};


现在回到DVDVideoCodecFFmpeg.h。我们可以看一下上文所示的4个函数。

 

Open()

 

//打开
bool CDVDVideoCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
{
  AVCodec* pCodec;

  if(!m_dllAvUtil.Load()
  || !m_dllAvCodec.Load()
  || !m_dllSwScale.Load()
  || !m_dllPostProc.Load()
  || !m_dllAvFilter.Load()
  ) return false;
  //注册解码器
  m_dllAvCodec.avcodec_register_all();
  m_dllAvFilter.avfilter_register_all();

  m_bSoftware     = hints.software;
  m_iOrientation  = hints.orientation;

  for(std::vector<ERenderFormat>::iterator it = options.m_formats.begin(); it != options.m_formats.end(); ++it)
  {
    m_formats.push_back((PixelFormat)CDVDCodecUtils::PixfmtFromEFormat(*it));
    if(*it == RENDER_FMT_YUV420P)
      m_formats.push_back(PIX_FMT_YUVJ420P);
  }
  m_formats.push_back(PIX_FMT_NONE); /* always add none to get a terminated list in ffmpeg world */

  pCodec = NULL;
  m_pCodecContext = NULL;

  if (hints.codec == AV_CODEC_ID_H264)
  {
    switch(hints.profile)
    {
      case FF_PROFILE_H264_HIGH_10:
      case FF_PROFILE_H264_HIGH_10_INTRA:
      case FF_PROFILE_H264_HIGH_422:
      case FF_PROFILE_H264_HIGH_422_INTRA:
      case FF_PROFILE_H264_HIGH_444_PREDICTIVE:
      case FF_PROFILE_H264_HIGH_444_INTRA:
      case FF_PROFILE_H264_CAVLC_444:
      // this is needed to not open the decoders
      m_bSoftware = true;
      // this we need to enable multithreading for hi10p via advancedsettings
      m_isHi10p = true;
      break;
    }
  }
  //查找解码器
  if(pCodec == NULL)
    pCodec = m_dllAvCodec.avcodec_find_decoder(hints.codec);

  if(pCodec == NULL)
  {
    CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Unable to find codec %d", hints.codec);
    return false;
  }

  CLog::Log(LOGNOTICE,"CDVDVideoCodecFFmpeg::Open() Using codec: %s",pCodec->long_name ? pCodec->long_name : pCodec->name);

  if(m_pCodecContext == NULL)
    m_pCodecContext = m_dllAvCodec.avcodec_alloc_context3(pCodec);

  m_pCodecContext->opaque = (void*)this;
  m_pCodecContext->debug_mv = 0;
  m_pCodecContext->debug = 0;
  m_pCodecContext->workaround_bugs = FF_BUG_AUTODETECT;
  m_pCodecContext->get_format = GetFormat;
  m_pCodecContext->codec_tag = hints.codec_tag;
  /* Only allow slice threading, since frame threading is more
   * sensitive to changes in frame sizes, and it causes crashes
   * during HW accell - so we unset it in this case.
   *
   * When we detect Hi10p and user did not disable hi10pmultithreading
   * via advancedsettings.xml we keep the ffmpeg default thread type.
   * */
  if(m_isHi10p && !g_advancedSettings.m_videoDisableHi10pMultithreading)
  {
    CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Keep default threading for Hi10p: %d",
                        m_pCodecContext->thread_type);
  }
  else if (CSettings::Get().GetBool("videoplayer.useframemtdec"))
  {
    CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Keep default threading %d by videoplayer.useframemtdec",
                        m_pCodecContext->thread_type);
  }
  else
    m_pCodecContext->thread_type = FF_THREAD_SLICE;

#if defined(TARGET_DARWIN_IOS)
  // ffmpeg with enabled neon will crash and burn if this is enabled
  m_pCodecContext->flags &= CODEC_FLAG_EMU_EDGE;
#else
  if (pCodec->id != AV_CODEC_ID_H264 && pCodec->capabilities & CODEC_CAP_DR1
      && pCodec->id != AV_CODEC_ID_VP8
     )
    m_pCodecContext->flags |= CODEC_FLAG_EMU_EDGE;
#endif

  // if we don't do this, then some codecs seem to fail.
  m_pCodecContext->coded_height = hints.height;
  m_pCodecContext->coded_width = hints.width;
  m_pCodecContext->bits_per_coded_sample = hints.bitsperpixel;

  if( hints.extradata && hints.extrasize > 0 )
  {
    m_pCodecContext->extradata_size = hints.extrasize;
    m_pCodecContext->extradata = (uint8_t*)m_dllAvUtil.av_mallocz(hints.extrasize + FF_INPUT_BUFFER_PADDING_SIZE);
    memcpy(m_pCodecContext->extradata, hints.extradata, hints.extrasize);
  }

  // advanced setting override for skip loop filter (see avcodec.h for valid options)
  // TODO: allow per video setting?
  if (g_advancedSettings.m_iSkipLoopFilter != 0)
  {
    m_pCodecContext->skip_loop_filter = (AVDiscard)g_advancedSettings.m_iSkipLoopFilter;
  }

  // set any special options
  for(std::vector<CDVDCodecOption>::iterator it = options.m_keys.begin(); it != options.m_keys.end(); ++it)
  {
    if (it->m_name == "surfaces")
      m_uSurfacesCount = std::atoi(it->m_value.c_str());
    else
      m_dllAvUtil.av_opt_set(m_pCodecContext, it->m_name.c_str(), it->m_value.c_str(), 0);
  }

  int num_threads = std::min(8 /*MAX_THREADS*/, g_cpuInfo.getCPUCount());
  if( num_threads > 1 && !hints.software && m_pHardware == NULL // thumbnail extraction fails when run threaded
  && ( pCodec->id == AV_CODEC_ID_H264
    || pCodec->id == AV_CODEC_ID_MPEG4 ))
    m_pCodecContext->thread_count = num_threads;
  //打开解码器
  if (m_dllAvCodec.avcodec_open2(m_pCodecContext, pCodec, NULL) < 0)
  {
    CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Unable to open codec");
    return false;
  }
  //初始化AVFrame
  m_pFrame = m_dllAvCodec.avcodec_alloc_frame();
  if (!m_pFrame) return false;

#if defined(LIBAVFILTER_AVFRAME_BASED)
  m_pFilterFrame = m_dllAvUtil.av_frame_alloc();
  if (!m_pFilterFrame) return false;
#endif

  UpdateName();
  return true;
}

 

 

Dispose()

//关闭
void CDVDVideoCodecFFmpeg::Dispose()
{
	//释放
  if (m_pFrame) m_dllAvUtil.av_free(m_pFrame);
  m_pFrame = NULL;

#if defined(LIBAVFILTER_AVFRAME_BASED)
  m_dllAvUtil.av_frame_free(&m_pFilterFrame);
#endif

  if (m_pCodecContext)
  {
	  //关闭解码器
    if (m_pCodecContext->codec) m_dllAvCodec.avcodec_close(m_pCodecContext);
    if (m_pCodecContext->extradata)
    {
      m_dllAvUtil.av_free(m_pCodecContext->extradata);
      m_pCodecContext->extradata = NULL;
      m_pCodecContext->extradata_size = 0;
    }
    m_dllAvUtil.av_free(m_pCodecContext);
    m_pCodecContext = NULL;
  }
  SAFE_RELEASE(m_pHardware);

  FilterClose();

  m_dllAvCodec.Unload();
  m_dllAvUtil.Unload();
  m_dllAvFilter.Unload();
  m_dllPostProc.Unload();
}


Decode()

 

//解码
int CDVDVideoCodecFFmpeg::Decode(uint8_t* pData, int iSize, double dts, double pts)
{
  int iGotPicture = 0, len = 0;

  if (!m_pCodecContext)
    return VC_ERROR;

  if(pData)
    m_iLastKeyframe++;

  shared_ptr<CSingleLock> lock;
  if(m_pHardware)
  {
    CCriticalSection* section = m_pHardware->Section();
    if(section)
      lock = shared_ptr<CSingleLock>(new CSingleLock(*section));

    int result;
    if(pData)
      result = m_pHardware->Check(m_pCodecContext);
    else
      result = m_pHardware->Decode(m_pCodecContext, NULL);

    if(result)
      return result;
  }

  if(m_pFilterGraph)
  {
    int result = 0;
    if(pData == NULL)
      result = FilterProcess(NULL);
    if(result)
      return result;
  }

  m_dts = dts;
  m_pCodecContext->reordered_opaque = pts_dtoi(pts);
  //初始化AVPacket
  AVPacket avpkt;
  m_dllAvCodec.av_init_packet(&avpkt);
  avpkt.data = pData;
  avpkt.size = iSize;
  /* We lie, but this flag is only used by pngdec.c.
   * Setting it correctly would allow CorePNG decoding. */
  avpkt.flags = AV_PKT_FLAG_KEY;
  //解码
  len = m_dllAvCodec.avcodec_decode_video2(m_pCodecContext, m_pFrame, &iGotPicture, &avpkt);

  if(m_iLastKeyframe < m_pCodecContext->has_b_frames + 2)
    m_iLastKeyframe = m_pCodecContext->has_b_frames + 2;

  if (len < 0)
  {
    CLog::Log(LOGERROR, "%s - avcodec_decode_video returned failure", __FUNCTION__);
    return VC_ERROR;
  }

  if (!iGotPicture)
    return VC_BUFFER;

  if(m_pFrame->key_frame)
  {
    m_started = true;
    m_iLastKeyframe = m_pCodecContext->has_b_frames + 2;
  }

  /* put a limit on convergence count to avoid huge mem usage on streams without keyframes */
  if(m_iLastKeyframe > 300)
    m_iLastKeyframe = 300;

  /* h264 doesn't always have keyframes + won't output before first keyframe anyway */
  if(m_pCodecContext->codec_id == AV_CODEC_ID_H264
  || m_pCodecContext->codec_id == AV_CODEC_ID_SVQ3)
    m_started = true;

  if(m_pHardware == NULL)
  {
    bool need_scale = std::find( m_formats.begin()
                               , m_formats.end()
                               , m_pCodecContext->pix_fmt) == m_formats.end();

    bool need_reopen  = false;
    if(!m_filters.Equals(m_filters_next))
      need_reopen = true;

    if(m_pFilterIn)
    {
      if(m_pFilterIn->outputs[0]->format != m_pCodecContext->pix_fmt
      || m_pFilterIn->outputs[0]->w      != m_pCodecContext->width
      || m_pFilterIn->outputs[0]->h      != m_pCodecContext->height)
        need_reopen = true;
    }

    // try to setup new filters
    if (need_reopen || (need_scale && m_pFilterGraph == NULL))
    {
      m_filters = m_filters_next;

      if(FilterOpen(m_filters, need_scale) < 0)
        FilterClose();
    }
  }

  int result;
  if(m_pHardware)
    result = m_pHardware->Decode(m_pCodecContext, m_pFrame);
  else if(m_pFilterGraph)
    result = FilterProcess(m_pFrame);
  else
    result = VC_PICTURE | VC_BUFFER;

  if(result & VC_FLUSHED)
    Reset();

  return result;
}

Reset()

//复位
void CDVDVideoCodecFFmpeg::Reset()
{
  m_started = false;
  m_iLastKeyframe = m_pCodecContext->has_b_frames;
  m_dllAvCodec.avcodec_flush_buffers(m_pCodecContext);

  if (m_pHardware)
    m_pHardware->Reset();

  m_filters = "";
  FilterClose();
}



 

 

 

分享到:
评论

相关推荐

    repository.xbmc-addons-chinese-1.2.1.zip

    3. 输入路径(例如,如果文件在USB驱动器上,路径可能是“设备&gt;\repository.xbmc-addons-chinese”),然后给源命名。 4. 返回主菜单,进入“程序”&gt;“添加-ons”&gt;“我的添加-ons”&gt;“安装从zip文件”。 5. 导航到...

    script.bluray.com:用于访问 blu-ray.com 功能的 Kodi (XBMC) 插件

    script.bluray.com用于访问 blu-ray.com 功能的 Kodi (XBMC) 插件它目前仅在我的可用。 存储库的安装应通过 Kodi System::Settings::Add-ons::Install from zip 文件完成插件的安装应通过 Kodi System::Settings::...

    repository.xbmc-addons-chinese-2.0.0.zip

    标题中的"repository.xbmc-addons-chinese-2.0.0.zip"是一个针对KODI媒体中心的中文插件库的压缩包,版本号为2.0.0。这个压缩包包含了多款专为中国用户设计的KODI插件,旨在提升KODI在中文环境下的用户体验和功能...

    ffmpeg-2.8.5-Jarvis-rc1.tar.gz

    综上所述,"ffmpeg-2.8.5-Jarvis-rc1.tar.gz" 是为xbmc/kodi开发提供的一个关键的多媒体处理库,包含了一组特定版本的FFmpeg组件,方便开发者快速集成到他们的项目中,以实现多媒体内容的流畅播放。

    xbmc-forum:XBMC论坛的源代码-Forum source code

    "xbmc-forum:XBMC论坛的源代码"这个标题表明我们讨论的是XBMC官方论坛的源代码,这意味着我们可以深入理解其背后的开发过程、社区互动机制以及论坛的构建方式。 源代码通常对开发者来说是非常宝贵的资源,因为它...

    ru:seppius-xbmc-repo(叉子)

    【标题】"ru:seppius-xbmc-repo(叉子)" 指的是一种基于XBMC或Kodi的俄罗斯开发者论坛扩展资源库的克隆版本。XBMC(Xbox Media Center)是一个开源的媒体中心软件,后来更名为Kodi,它允许用户组织和播放各种多媒体...

    QtAV:基于Qt和FFmpeg(https:github.comwang-binavbuild)的跨平台多媒体框架。 高性能。 用户和开发人员友好。 支持Android,iOS,Windows应用商店和台式机。基于Qt和FFmpeg的跨平台高级音视频播放框架

    它可以帮助您以比以往更少的精力编写播放器。 QtAV已添加到FFmpeg项目页面 QtAV是根据LGPL v2.1条款获得许可的免费软件。 播放器示例已根据GPL v3许可。 如果您使用QtAV或其组成库,则必须遵守相关许可条款。 ...

    aac-rtmp-red5

    如果你没有这个工具,可以自行编译或从源代码构建。 使用rtmpdump向Red5推送AAC流的命令通常会包括以下参数: ``` rtmpdump -r "rtmp://your-server-address/your-app/stream-name" -v -a "your-aac-stream-file....

    kodi中文插件最新2022 repository.xbmc-addons-chinese-2.0.0

    kodi中文插件最新2022 repository.xbmc-addons-chinese-2.0.0

    xbmc 移植到ANDROID 方法

    XBMC(Xbox Media Center)是一款开源媒体播放器应用,最初为Xbox游戏机开发,后来被移植到了多个操作系统上,包括Android。将XBMC移植到Android平台上需要进行一系列准备工作,包括设置Android开发环境、获取源代码...

    repository.xbmc-addons-chinese-2.0.1.zip亲测可用kodi中文插件库下载

    标题中的“repository.xbmc-addons-chinese-2.0.1.zip”是一个针对Kodi的中文插件库的压缩包,版本号为2.0.1。Kodi是一款开源的媒体中心软件,允许用户在各种设备上管理和播放多媒体内容,如视频、音乐和图片。这个...

    XBMC-OBD2:XBMC OBD2 插件-开源

    XBMC-OBD2 是一款基于XBMC(Xbox Media Center,现称为Kodi)平台的开源插件,专为汽车爱好者设计,它允许用户通过ELM327兼容的OBD2诊断适配器获取车辆的实时数据。下面将详细阐述这款插件的功能、工作原理以及如何...

    XBMC-Trailer-Downloader:抓取 hd-trailers.net 并下载用于 XBMC 和 Cinema Experience 脚本的预告片

    XBMC-Trailer-Downloader 抓取 hd-trailers.net 并下载用于 XBMC 和 Cinema Experience 脚本的预告片。 安装:pip install -r requirements.txt 可以以 480/720/1080 的分辨率下载预告片。 可以删除已下载且超过...

    Ubuntu 11.04 安装后要做的20件事情.txt

    sudo apt-get install non-free-codecs libxine1-ffmpeg gxinemencoder libmpcdec3 libquicktime1 flac faac faad sox ffmpeg2theora libmpeg2-4 uudeview flac libmpeg3-1 mpeg3-utils mpg321 mpg123 libflac++6 ...

    repository.kodi-chinese-addons:repository.kodi-chinese-addons xbmckodi用于中文插件的存储库

    这意味着用户将获得最新的插件和更新,开发者可以在这里找到最新的源代码以进行维护和开发。 通过这个仓库,Kodi用户可以轻松地安装和管理各种中文插件,比如: 1. **中文界面**:提供完全汉化的Kodi界面,让操作...

    xbmc-bilibili-main.zip

    在这个xbmc-bilibili-main插件中,Python被用来编写与Bilibili API进行交互的代码,实现视频搜索、播放、评论等功能。 安装此插件的步骤一般包括以下几步: 1. 下载"xbmc-bilibili-main.zip"文件到本地。 2. 在Kodi...

    ubuntu下的kodi(XBMC)编译

    标题中的“ubuntu下的kodi(XBMC)编译”指的是在Ubuntu操作系统环境下,对Kodi(以前称为XBMC,Xbox Media Center)媒体中心软件进行源代码编译的过程。Kodi是一款开源的多媒体中心应用,它能播放各种音频、视频格式...

    xbmc-12.1-Frodo-armeabi-v7a.apk

    XBMC最初为Xbox而开发,现在可以运行在Linux、OSX、Windows系统。 2003年,一些兴趣相投的程序员创建了这个项目。XBMC是一个非盈利的项目,由遍布世界各地的自愿者开发维护。超过50名软件开发人员为XBMC作出贡献,...

    kodi(xbmc)中文字幕插件

    在“repository.xbmc-addons-chinese”这个压缩包中,包含了一些为中国用户定制的Kodi插件,尤其是与中文字幕相关的插件。 安装插件的过程如下: 1. **打开Kodi**:启动你的Kodi应用程序,进入主界面。 2. **进入...

    screensaver.plugin.slideshow:适用于Kodi(XBMC)的插件幻灯片屏幕保护程序插件-插件中的图像幻灯片

    适用于Kodi(XBMC)的插件幻灯片屏幕保护程序插件-插件中的图像幻灯片 此插件允许使用图片插件作为图片幻灯片屏幕保护程序的源。 安装应该通过Kodi System :: Settings :: Add-ons :: Get Add-Ons :: All Add-Ons :...

Global site tag (gtag.js) - Google Analytics