浏览代码

Z991239-1248 #comment other: mod_localmediaplay实体迁移

陈礼鹏80274480 4 年之前
父节点
当前提交
068fdb6c78
共有 34 个文件被更改,包括 4392 次插入245 次删除
  1. 6 0
      Module/mod_localmediaplay/AdvertBase/AdvertSync.cpp
  2. 172 67
      Module/mod_localmediaplay/AdvertManage/BaseFun.cpp
  3. 6 1
      Module/mod_localmediaplay/AdvertManage/BaseFun.h
  4. 53 33
      Module/mod_localmediaplay/AdvertManage/MediaManage.cpp
  5. 75 22
      Module/mod_localmediaplay/AdvertManage/resourceIniParse.cpp
  6. 6 1
      Module/mod_localmediaplay/AdvertManage/resourceIniParse.h
  7. 8 1
      Module/mod_localmediaplay/CMakeLists.txt
  8. 476 97
      Module/mod_localmediaplay/mod_localmediaplay.cpp
  9. 76 19
      Module/mod_localmediaplay/mod_localmediaplay.h
  10. 8 3
      Module/mod_localmediaplay/stdafx.h
  11. 45 0
      Other/libimgplayer/CMakeLists.txt
  12. 3 0
      Other/libimgplayer/libimgplayer.h
  13. 85 0
      Other/libmediaplayer/CMakeLists.txt
  14. 475 0
      Other/libmediaplayer/audio.cpp
  15. 8 0
      Other/libmediaplayer/audio.h
  16. 205 0
      Other/libmediaplayer/demux.cpp
  17. 8 0
      Other/libmediaplayer/demux.h
  18. 142 0
      Other/libmediaplayer/frame.cpp
  19. 20 0
      Other/libmediaplayer/frame.h
  20. 199 0
      Other/libmediaplayer/libmediaplayer.cpp
  21. 111 0
      Other/libmediaplayer/libmediaplayer.h
  22. 146 0
      Other/libmediaplayer/packet.cpp
  23. 13 0
      Other/libmediaplayer/packet.h
  24. 477 0
      Other/libmediaplayer/player.cpp
  25. 240 0
      Other/libmediaplayer/player.h
  26. 546 0
      Other/libmediaplayer/video.cpp
  27. 9 0
      Other/libmediaplayer/video.h
  28. 63 0
      Other/libpictureplayer/CMakeLists.txt
  29. 270 0
      Other/libpictureplayer/CPicturePlayer.cpp
  30. 91 0
      Other/libpictureplayer/CPicturePlayer.h
  31. 169 0
      Other/libpictureplayer/libpictureplayer.cpp
  32. 77 0
      Other/libpictureplayer/libpictureplayer.h
  33. 102 0
      Other/libwmpplayer/CMakeLists.txt
  34. 2 1
      Other/libwmpplayer/libwmpplayer.h

+ 6 - 0
Module/mod_localmediaplay/AdvertBase/AdvertSync.cpp

@@ -15,7 +15,13 @@ ErrorCodeEnum CAdvertSyncConnection::QryVaildAdvertList(const char *terminalNo,
 	ResourceListReq req = {0};
 	if (NULL == terminalNo)
 		return Error_Param;
+#ifdef RVC_OS_WIN
 	strncpy_s(req.terminalNo, terminalNo, sizeof(req.terminalNo) - 1);
+#else
+	strncpy(req.terminalNo, terminalNo, sizeof(req.terminalNo) - 1);
+#endif // RVC_OS_WIN
+
+	
 	req.resourceType = resourceType;
 	
 	pkt->AddStruct("GListR", false, false, (LPBYTE)&req, sizeof(ResourceListReq));

+ 172 - 67
Module/mod_localmediaplay/AdvertManage/BaseFun.cpp

@@ -1,26 +1,34 @@
 #include "stdafx.h"
 #include "BaseFun.h"
+#include "array.h"
+
+#ifdef RVC_OS_WIN
 #include <ShlDisp.h>
 #include <direct.h>
 #include <io.h>
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif // RVC_OS_WIN
 
-#pragma comment(lib,"Shell32.lib")
 
 bool Unzip2Folder(BSTR lpZipFile, BSTR lpFolder)
 {
-	IShellDispatch *pISD;
+#ifdef RVC_OS_WIN
+	IShellDispatch* pISD;
 
-	Folder  *pZippedFile = 0L;
-	Folder  *pDestination = 0L;
+	Folder* pZippedFile = 0L;
+	Folder* pDestination = 0L;
 
 	long FilesCount = 0;
 	IDispatch* pItem = 0L;
-	FolderItems *pFilesInside = 0L;
+	FolderItems* pFilesInside = 0L;
 
 	VARIANT Options, OutFolder, InZipFile, Item;
 	CoInitialize(NULL);
-	__try{
-		if (CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&pISD) != S_OK)
+	__try {
+		if (CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void**)& pISD) != S_OK)
 			return 1;
 
 		InZipFile.vt = VT_BSTR;
@@ -61,7 +69,7 @@ bool Unzip2Folder(BSTR lpZipFile, BSTR lpFolder)
 			return 0;
 		}
 
-		pFilesInside->QueryInterface(IID_IDispatch, (void**)&pItem);
+		pFilesInside->QueryInterface(IID_IDispatch, (void**)& pItem);
 
 		Item.vt = VT_DISPATCH;
 		Item.pdispVal = pItem;
@@ -84,8 +92,13 @@ bool Unzip2Folder(BSTR lpZipFile, BSTR lpFolder)
 	{
 		CoUninitialize();
 	}
+#else
+
+	return true;
+#endif 
 }
 
+
 void split(const string& src, const string& separator, vector<string>& dest)
 {
 	string str = src;
@@ -111,6 +124,7 @@ void split(const string& src, const string& separator, vector<string>& dest)
 
 bool  checkDirExist(const string &strPath)
 {
+#ifdef RVC_OS_WIN
 	WIN32_FIND_DATA  wfd;
 	bool rValue = false;
 	HANDLE hFind = FindFirstFile(strPath.c_str(), &wfd);
@@ -118,114 +132,189 @@ bool  checkDirExist(const string &strPath)
 		rValue = true;
 	FindClose(hFind);
 	return rValue;
+#else
+	if (ExistsDir(strPath.c_str()))
+	{
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+#endif 
 }
 
+
 bool checkFileExist(string fileName)
 {
+#ifdef RVC_OS_WIN
 	WIN32_FIND_DATA FindFileData;
 	HANDLE hFind;
 
-
 	hFind = FindFirstFile(fileName.c_str(), &FindFileData);
 
 	if (hFind == INVALID_HANDLE_VALUE)
 		return false;
-	
+
 	FindClose(hFind);
 	return true;
+#else
+	if (ExistsFile(fileName.c_str()))
+	{
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+#endif 
 }
 
 
 void Wchar_tToString(std::string& szDst, wchar_t *wchar)
 {
-	wchar_t * wText = wchar;
+#ifdef RVC_OS_WIN
+	wchar_t* wText = wchar;
 	DWORD dwNum = WideCharToMultiByte(CP_OEMCP, NULL, wText, -1, NULL, 0, NULL, FALSE);// WideCharToMultiByte的运用
-	char *psText; // psText为char*的临时数组,作为赋值给std::string的中间变量
+	char* psText; // psText为char*的临时数组,作为赋值给std::string的中间变量
 	psText = new char[dwNum];
 	WideCharToMultiByte(CP_OEMCP, NULL, wText, -1, psText, dwNum, NULL, FALSE);// WideCharToMultiByte的再次运用
 	szDst = psText;// std::string赋值
 	delete[]psText;// psText的清除
+#else
+	std::string curLocale = setlocale(LC_ALL, NULL);        // curLocale = "C";
+	setlocale(LC_ALL, "chs");
+	size_t _Dsize = 2 * wcslen(wchar) + 1;
+	char* _Dest = new char[_Dsize];
+	memset(_Dest, 0, _Dsize);
+	wcstombs(_Dest, wchar, _Dsize);
+	std::string result = _Dest;
+	delete[]_Dest;
+	setlocale(LC_ALL, curLocale.c_str());
+	szDst = result;
+#endif 
 }
 
 // string to wstring
 void StringToWstring(std::wstring& szDst, std::string str)
 {
+#ifdef RVC_OS_WIN
 	std::string temp = str;
 	int len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)temp.c_str(), -1, NULL, 0);
-	wchar_t * wszUtf8 = new wchar_t[len + 1];
+	wchar_t* wszUtf8 = new wchar_t[len + 1];
 	memset(wszUtf8, 0, len * 2 + 2);
 	MultiByteToWideChar(CP_ACP, 0, (LPCSTR)temp.c_str(), -1, (LPWSTR)wszUtf8, len);
 	szDst = wszUtf8;
 	std::wstring r = wszUtf8;
 	delete[] wszUtf8;
+#else
+	setlocale(LC_ALL, "chs");
+	const char* _Source = str.c_str();
+	size_t _Dsize = str.size() + 1;
+	wchar_t* _Dest = new wchar_t[_Dsize];
+	wmemset(_Dest, 0, _Dsize);
+	mbstowcs(_Dest, _Source, _Dsize);
+	std::wstring result = _Dest;
+	delete[]_Dest;
+	setlocale(LC_ALL, "C");
+	szDst = result;
+#endif 
 }
 
 bool createDir(const string &filePath)
 {
+#ifdef RVC_OS_WIN
 	return 0 == _mkdir(filePath.c_str());
+#else
+	if (CreateDirRecursive(filePath.c_str()))
+	{
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+#endif 
 }
 
 BOOL IsDirectory(const char *pDir)
 {
-    char szCurPath[500];
-    ZeroMemory(szCurPath, 500);
+	char szCurPath[500] = {0};
     sprintf_s(szCurPath, 500, "%s//*", pDir);
-    WIN32_FIND_DATAA FindFileData;
-    ZeroMemory(&FindFileData, sizeof(WIN32_FIND_DATAA));
-
-    HANDLE hFile = FindFirstFileA(szCurPath, &FindFileData); /**< find first file by given path. */
-
-    if (hFile == INVALID_HANDLE_VALUE)
-    {
-        FindClose(hFile);
-        return FALSE; /** 如果不能找到第一个文件,那么没有目录 */
-    }
-    else
-    {
-        FindClose(hFile);
-        return TRUE;
-    }
+#ifdef RVC_OS_WIN
+	WIN32_FIND_DATAA FindFileData;
+	ZeroMemory(&FindFileData, sizeof(WIN32_FIND_DATAA));
 
+	HANDLE hFile = FindFirstFileA(szCurPath, &FindFileData); /**< find first file by given path. */
+
+	if (hFile == INVALID_HANDLE_VALUE)
+	{
+		FindClose(hFile);
+		return FALSE; /** 如果不能找到第一个文件,那么没有目录 */
+	}
+	else
+	{
+		FindClose(hFile);
+		return TRUE;
+	}
+
+#else
+	struct stat buf;
+	if (0 == stat(szCurPath, &buf))
+	{
+		return S_ISDIR(buf.st_mode);
+	}
+	else
+	{
+		return FALSE;
+	}
+#endif // RVC_OS_WIN
 }
 
+
 BOOL DeleteDirectory(const char * DirName)
 {
-    //    CFileFind tempFind;        //声明一个CFileFind类变量,以用来搜索
-    char szCurPath[MAX_PATH];        //用于定义搜索格式
-    _snprintf(szCurPath, MAX_PATH, "%s//*.*", DirName);    //匹配格式为*.*,即该目录下的所有文件
-    WIN32_FIND_DATAA FindFileData;
-    ZeroMemory(&FindFileData, sizeof(WIN32_FIND_DATAA));
-    HANDLE hFile = FindFirstFileA(szCurPath, &FindFileData);
-    BOOL IsFinded = TRUE;
-    while (IsFinded)
-    {
-        IsFinded = FindNextFileA(hFile, &FindFileData);    //递归搜索其他的文件
-        if (strcmp(FindFileData.cFileName, ".") && strcmp(FindFileData.cFileName, "..")) //如果不是"." ".."目录
-        {
-            std::string strFileName = "";
-            strFileName = strFileName + DirName + "//" + FindFileData.cFileName;
-            std::string strTemp;
-            strTemp = strFileName;
-            if (IsDirectory(strFileName.c_str())) //如果是目录,则递归地调用
-            {
-                printf("目录为:%s/n", strFileName.c_str());
-                DeleteDirectory(strTemp.c_str());
-            }
-            else
-            {
-                DeleteFileA(strTemp.c_str());
-            }
-        }
-    }
-    FindClose(hFile);
-
-    BOOL bRet = RemoveDirectoryA(DirName);
-    if (bRet == 0) //删除目录
-    {
-        printf("删除%s目录失败!/n", DirName);
-        return FALSE;
-    }
-    return TRUE;
+#ifdef RVC_OS_WIN
+	//    CFileFind tempFind;        //声明一个CFileFind类变量,以用来搜索
+	char szCurPath[MAX_PATH];        //用于定义搜索格式
+	_snprintf(szCurPath, MAX_PATH, "%s//*.*", DirName);    //匹配格式为*.*,即该目录下的所有文件
+	WIN32_FIND_DATAA FindFileData;
+	ZeroMemory(&FindFileData, sizeof(WIN32_FIND_DATAA));
+	HANDLE hFile = FindFirstFileA(szCurPath, &FindFileData);
+	BOOL IsFinded = TRUE;
+	while (IsFinded)
+	{
+		IsFinded = FindNextFileA(hFile, &FindFileData);    //递归搜索其他的文件
+		if (strcmp(FindFileData.cFileName, ".") && strcmp(FindFileData.cFileName, "..")) //如果不是"." ".."目录
+		{
+			std::string strFileName = "";
+			strFileName = strFileName + DirName + "//" + FindFileData.cFileName;
+			std::string strTemp;
+			strTemp = strFileName;
+			if (IsDirectory(strFileName.c_str())) //如果是目录,则递归地调用
+			{
+				printf("目录为:%s/n", strFileName.c_str());
+				DeleteDirectory(strTemp.c_str());
+			}
+			else
+			{
+				DeleteFileA(strTemp.c_str());
+			}
+		}
+	}
+	FindClose(hFile);
+
+	BOOL bRet = RemoveDirectoryA(DirName);
+	if (bRet == 0) //删除目录
+	{
+		printf("删除%s目录失败!/n", DirName);
+		return FALSE;
+	}
+	return TRUE;
+
+#else
+	return RemoveDirRecursive(DirName);
+#endif // RVC_OS_WIN
 }
 
 bool removeDir(const string &filePaht)
@@ -235,14 +324,21 @@ bool removeDir(const string &filePaht)
 
 void stopForDebug()
 {
+#ifdef RVC_OS_WIN
 	DWORD processId = GetCurrentProcessId();
 	char showMsg[100] = "";
 	sprintf_s(showMsg, "Current pross id is %d", processId);
 	MessageBox(NULL, showMsg, NULL, 0);
+
+#else
+
+
+#endif // RVC_OS_WIN
 }
 
 void getDirs(string path, vector<string> &ownname)
 {
+#ifdef RVC_OS_WIN
 	long   hFile = 0;
 	struct _finddata_t fileinfo;
 	string p;
@@ -251,11 +347,20 @@ void getDirs(string path, vector<string> &ownname)
 		do
 		{
 			//如果是目录加入列表  
-			if ((fileinfo.attrib &  _A_SUBDIR) && strcmp(fileinfo.name, ".") && strcmp(fileinfo.name, ".."))
+			if ((fileinfo.attrib & _A_SUBDIR) && strcmp(fileinfo.name, ".") && strcmp(fileinfo.name, ".."))
 				ownname.push_back(fileinfo.name);
 		} while (_findnext(hFile, &fileinfo) == 0);
 		_findclose(hFile);
 	}
+#else
+	array_header_t* subdirs = fileutil_get_sub_files(path.c_str());
+	int i;
+	for (i = 0; i < subdirs->nelts; ++i) {
+		char* strsubdir = ARRAY_IDX(subdirs, i, char*);
+		ownname.push_back(strsubdir);
+	}
+
+#endif // RVC_OS_WIN
 }
 
 bool getUniqueDir(string path, string &dirName)

+ 6 - 1
Module/mod_localmediaplay/AdvertManage/BaseFun.h

@@ -1,6 +1,11 @@
-#include <windows.h>
 #include <vector>
 #include <string>
+#include "fileutil.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
 using namespace std;
 
 bool Unzip2Folder(BSTR lpZipFile, BSTR lpFolder);	//½âѹÎļþµ½Îļþ¼Ð

+ 53 - 33
Module/mod_localmediaplay/AdvertManage/MediaManage.cpp

@@ -6,16 +6,16 @@
 
 
 
-
-#define DEFAULT_DOWNLOAD_PATH	"D:\\rvc\\Downloads\\package"
-#define DEFAULT_RESOURSE_PATH	"D:\\rvc\\addata\\Video"
-#define DEFAULT_DOWNLOAD_PATH_C	"C:\\rvc\\Downloads\\package"
-#define DEFAULT_RESOURSE_PATH_C	"C:\\rvc\\addata\\Video"
+#define DEFAULT_DOWNLOAD_PATH	"D:/rvc/Downloads/package"
+#define DEFAULT_RESOURSE_PATH	"D:/rvc/addata/Video"
+#define DEFAULT_DOWNLOAD_PATH_C	"C:/rvc/Downloads/package"
+#define DEFAULT_RESOURSE_PATH_C	"C:/rvc/addata/Video"
 #define DEFAULT_HEAD_CONTAIN	"银行"
 #define DEFAULT_FEN_CONTAIN		"分行"
 #define DEFAULT_NETWORK_CONTAIN	"支行"
 #define DEFAULT_NETWORK2_CONTAIN "网点"
 
+
 mediaManage::mediaManage()
 {
 	m_curParse.clear();
@@ -31,8 +31,6 @@ mediaManage::mediaManage()
 		setDefaultAddvertPath(string(DEFAULT_RESOURSE_PATH_C));
 		setDefaultDownloadPath(string(DEFAULT_DOWNLOAD_PATH_C));
 	}
-
-	
 }
 
 void mediaManage::setDefaultAddvertPath(string addvertPath){ m_addvertPath = addvertPath; }
@@ -54,11 +52,19 @@ void mediaManage::GetPlayList(vector<ResourceParse> &resourceList)
 void mediaManage::GetPlayListByLocal(vector<ResourceParse>& resourceList)
 {
 	resourceList.clear();
-	for each(auto i in m_localList)
+
+#ifdef RVC_OS_WIN
+	for each (auto i in m_localList)
 	{
 		if (checkInVaildTime(i.vaildTime, true) && checkInPlayTime(i.playTime, true))
 			resourceList.push_back(i);
 	}
+#else
+	for (vector<ResourceParse>::iterator it = m_localList.begin(); it < m_localList.end(); it++)
+	{
+		resourceList.push_back(*it);
+	}
+#endif // _WIN32
 }
 
 bool mediaManage::InitResourseList(vector<string> resourceList)
@@ -99,8 +105,8 @@ bool mediaManage::AddResourceList(vector<string> resourceList)
 		if (-1 == pos)
 			continue;
 		string dirName = i->substr(0, pos);
-		string fileName = m_downloadPath + "\\" + *i;
-		string dirPath = m_addvertPath + "\\" + dirName;
+		string fileName = m_downloadPath + SPLIT_SLASH_STR + *i;
+		string dirPath = m_addvertPath + SPLIT_SLASH_STR + dirName;
 		Dbg("dirName:%s, fileName:%s, dirPath:%s", dirName.c_str(), fileName.c_str(), dirPath.c_str());
 
 		wstring wfileName, wdirPath;
@@ -134,7 +140,7 @@ bool mediaManage::AddResourceList(vector<string> resourceList)
 			continue;//配置文件读取失败
 		}
 
-		string resourcePath = dirPath + "\\";
+		string resourcePath = dirPath + SPLIT_SLASH_STR;
 /*		if (!getUniqueDir(dirPath, resourcePath))
 		{
 			Dbg("error:find multi resource dir!");
@@ -154,50 +160,67 @@ bool mediaManage::AddResourceList(vector<string> resourceList)
 
 void mediaManage::InitResourceListByLocal()
 {
-	string headFile = m_addvertPath + "\\" + HEADINI_NAME;
-	string branchFile = m_addvertPath + "\\" + BRANCHINI_NAME;
-	string networkFile = m_addvertPath + "\\" + NETWORKINI_NAME;
+	string headFile = m_addvertPath + SPLIT_SLASH_STR + HEADINI_NAME;
+	string branchFile = m_addvertPath + SPLIT_SLASH_STR + BRANCHINI_NAME;
+	string networkFile = m_addvertPath + SPLIT_SLASH_STR + NETWORKINI_NAME;
 
 	vector<ResourceParse> headList, branchList, networkList, allList;
 
 	if (!checkFileExist(headFile))
 		Dbg("head config %s not exist!", headFile.c_str());
-	else if(!parseResourceIni(headFile.c_str(), headList))
+	else if (!parseResourceIni(headFile.c_str(), headList))
 		Dbg("parse head config %s fail!", headFile.c_str());
 
-	if(!checkFileExist(branchFile))
+	if (!checkFileExist(branchFile))
 		Dbg("network  config %s not exist!", networkFile.c_str());
 	else if (!parseResourceIni(branchFile.c_str(), branchList))
 		Dbg("parse branch config %s fail!", branchFile.c_str());
 
 	if (!checkFileExist(networkFile))
-		Dbg("network config %s not exist!", networkFile.c_str()); 
-	else if(!parseResourceIni(networkFile.c_str(), networkList))
+		Dbg("network config %s not exist!", networkFile.c_str());
+	else if (!parseResourceIni(networkFile.c_str(), networkList))
 		Dbg("parse network config %s fail!", networkFile.c_str());
 
 	m_localList.clear();
-	for each(auto i in headList)
+#ifdef RVC_OS_WIN
+	for each (auto i in headList)
 		m_localList.push_back(i);
-	for each(auto i in branchList)
+	for each (auto i in branchList)
 		m_localList.push_back(i);
-	for each(auto i in networkList)
+	for each (auto i in networkList)
 		m_localList.push_back(i);
+#else
+	for (vector<ResourceParse>::iterator it = headList.begin(); it < headList.end(); ++it)
+	{
+		m_localList.push_back(*it);
+	}
+
+	for (vector<ResourceParse>::iterator it = branchList.begin(); it < branchList.end(); ++it)
+	{
+		m_localList.push_back(*it);
+	}
+
+	for (vector<ResourceParse>::iterator it = networkList.begin(); it < networkList.end(); ++it)
+	{
+		m_localList.push_back(*it);
+	}
+#endif // RVC_OS_WIN
 
 	std::stable_sort(m_localList.begin(), m_localList.end(), [](const ResourceParse& a, const ResourceParse& b) {
 		return a.priority > b.priority;
-	});//按priority排序
+		});//按priority排序
 
 
-	for(auto i = m_localList.begin(); i != m_localList.end(); i++)
+	for (auto i = m_localList.begin(); i != m_localList.end(); i++)
 	{
-		auto pos = i->videoNames.find('\\');
+		auto pos = i->videoNames.find(SPLIT_SLASH_STR);
 		if (pos > 0)
 		{
-			i->resourcePath = m_addvertPath + "\\" + i->videoNames.substr(0, pos + 1);
+			i->resourcePath = m_addvertPath + SPLIT_SLASH_STR + i->videoNames.substr(0, pos + 1);
 			i->videoNames = i->videoNames.substr(pos + 1, i->videoNames.length() - pos - 1);
 		}
 		else
-			i->resourcePath = m_addvertPath + "\\";
+			i->resourcePath = m_addvertPath + SPLIT_SLASH_STR;
 	}
 
 }
@@ -222,9 +245,9 @@ void mediaManage::DeleteResourceList(vector<string> resourceList)
 
 bool mediaManage::findVaildConfig(string dirPath, string &configPath)
 {
-	string headFile = dirPath + "\\" + HEADINI_NAME;
-	string branchFile = dirPath + "\\" + BRANCHINI_NAME;
-	string networkFile = dirPath + "\\" + NETWORKINI_NAME;
+	string headFile = dirPath + SPLIT_SLASH_STR + HEADINI_NAME;
+	string branchFile = dirPath + SPLIT_SLASH_STR + BRANCHINI_NAME;
+	string networkFile = dirPath + SPLIT_SLASH_STR + NETWORKINI_NAME;
 
 	if (checkFileExist(headFile))
 	{
@@ -268,15 +291,13 @@ void mediaManage::clearOutdataResource()
 				oldResourceList.insert(*i);
 		}
 
-
-
 		auto deletePos = set_difference(oldResourceList.begin(), oldResourceList.end(), newResourseList.begin(), newResourseList.end(), needDelete.begin());//need to delete
 		needDelete.resize(deletePos - needDelete.begin());//重新确定needDelete大小 
 
 
 		for (vector<string>::iterator i = needDelete.begin(); i != needDelete.end(); i++)
 		{
-			string path = m_addvertPath + "\\" + *i;
+			string path = m_addvertPath + SPLIT_SLASH_STR + *i;
 			Dbg("remove Dir %s %s!", *i, removeDir(path) ? "success" : "fail");
 		}
 	}
@@ -285,5 +306,4 @@ void mediaManage::clearOutdataResource()
 		Dbg("remove Dir exception:%s", e->what());
 	}
 
-	
 }

+ 75 - 22
Module/mod_localmediaplay/AdvertManage/resourceIniParse.cpp

@@ -1,72 +1,106 @@
 #include "stdafx.h"
 #include "resourceIniParse.h"
-#include <windows.h>
-#include <tchar.h>
 #include <time.h>
 #include "BaseFun.h"
 
+#ifdef RVC_OS_WIN
+#include <windows.h>
+#include <tchar.h>
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif 
 
 
 int ReadInterger(LPCTSTR szSection, LPCTSTR szKey, LPCTSTR szFileName, int iDefaultValue)
 {
+#ifdef RVC_OS_WIN
 	return GetPrivateProfileInt(szSection, szKey, iDefaultValue, szFileName);
+#else
+	return 0;
+#endif 
 }
 
 bool ReadBoolean(LPCTSTR szSection, LPCTSTR szKey, LPCTSTR szFileName, bool bDefaultValue)
 {
-	TCHAR szResult[255];
-	TCHAR szDefault[255];
-	bool bolResult;
+	TCHAR szResult[255] = {0};
+	TCHAR szDefault[255] = {0};
+	bool bolResult = false;
 	
+#ifdef RVC_OS_WIN
 	_stprintf_s(szDefault, _T("%s"), bDefaultValue ? _T("True") : _T("False"));
 	GetPrivateProfileString(szSection, szKey, (LPCTSTR)szDefault, szResult, 255, szFileName);
 	bolResult = (_tcscmp(szResult, _T("True")) == 0 || _tcscmp(szResult, _T("true")) == 0) ? true : false;
+#else
 
+#endif 
 	return bolResult;
 }
 
 string ReadString(LPCTSTR szSection, LPCTSTR szKey, LPCTSTR szFileName, LPCTSTR strDefaultValue)
 {
-	TCHAR  tempResult[512];
-	ZeroMemory(tempResult, 512);
-
+	TCHAR  tempResult[512] = {0};
+#ifdef RVC_OS_WIN
 	GetPrivateProfileString(szSection, szKey, strDefaultValue, tempResult, 512, szFileName);
+#else
+
+#endif 
+
 	return string(tempResult);
 }
 
 void WriteBoolean(LPCTSTR szSection, LPCTSTR szKey, BOOL szBool, LPCTSTR szFileName)
 {
-	TCHAR szDefault[255];
+	TCHAR szDefault[255] = {0};
+#ifdef RVC_OS_WIN
 	_stprintf_s(szDefault, _T("%d"), szBool);
 	WritePrivateProfileString(szSection, szKey, szDefault, szFileName);
+#else
+
+#endif 
 }
 
 void WriteInt(LPCTSTR szSection, LPCTSTR szKey, INT szInt, LPCTSTR szFileName)
 {
-	TCHAR szDefault[255];
+	TCHAR szDefault[255] = {0};
+#ifdef RVC_OS_WIN
 	_stprintf_s(szDefault, _T("%d"), szInt);
 	WritePrivateProfileString(szSection, szKey, szDefault, szFileName);
+#else
+
+#endif 
 }
 
 void WriteString(LPCTSTR szSection, LPCTSTR szKey, LPCTSTR szString, LPCTSTR szFileName)
 {
+#ifdef RVC_OS_WIN
 	WritePrivateProfileString(szSection, szKey, szString, szFileName);
+#else
+
+#endif 
 }
 
 bool checkInPlayTime(string playTime, bool checkCurTime)
 {
 	//get localTime
-	struct tm local;
+	struct tm *local;
 	time_t t;
+
+#ifdef RVC_OS_WIN
 	t = time(NULL);
-	localtime_s(&local, &t);
+	localtime_s(local, &t);
+#else
+	time(&t);
+	local = localtime(&t);
+#endif 
 
 	int t_beginHour, t_beginMin, t_endHour, t_endMin;
 	t_beginHour = t_beginMin = t_endHour = t_endMin = 0;
 	try
 	{
 		sscanf_s(playTime.c_str(), "%d:%d-%d:%d", &t_beginHour, &t_beginMin, &t_endHour, &t_endMin);
-		long curTime = local.tm_hour * 3600 + local.tm_min * 60 + local.tm_sec;
+		long curTime = local->tm_hour * 3600 + local->tm_min * 60 + local->tm_sec;
 		long beginTime = t_beginHour * 3600 + t_beginMin * 60;
 		long endTime = t_endHour * 3600 + t_endMin * 60 + 60;
 
@@ -77,9 +111,8 @@ bool checkInPlayTime(string playTime, bool checkCurTime)
 			else
 				return false;
 		}
-			
 	}
-	catch (exception *e)
+	catch (exception* e)
 	{
 		return false;
 	}
@@ -89,14 +122,20 @@ bool checkInPlayTime(string playTime, bool checkCurTime)
 bool checkInVaildTime(string vaildTime, bool checkCurData)
 {
 	//get localTime
-	struct tm local;
+	struct tm* local;
 	time_t t;
+	
+#ifdef RVC_OS_WIN
 	t = time(NULL);
-	localtime_s(&local, &t);
+	localtime_s(local, &t);
+#else
+	time(&t);
+	local = localtime(&t);
+#endif // RVC_OS_WIN
 
 	vector<string> dateList;
 	split(vaildTime, ",", dateList);
-	try{
+	try {
 		for (vector<string>::iterator i = dateList.begin(); i != dateList.end(); i++)
 		{
 			tm beginData, endData;
@@ -107,13 +146,13 @@ bool checkInVaildTime(string vaildTime, bool checkCurData)
 			if (-1 == i->find('-'))	//µ¥ÈÕÆÚ
 			{
 				sscanf_s(i->c_str(), "%d/%d/%d", &tempYear, &tempMon, &tempDay);
-				if (checkCurData && (local.tm_year + 1900 == tempYear) && (local.tm_mon + 1 == tempMon) && (local.tm_mday == tempDay))
+				if (checkCurData && (local->tm_year + 1900 == tempYear) && (local->tm_mon + 1 == tempMon) && (local->tm_mday == tempDay))
 					return true;//find the data
 			}
 			else
 			{
 				sscanf_s(i->c_str(), "%d/%d/%d-%d/%d/%d", &tempYear, &tempMon, &tempDay, &tempYear2, &tempMon2, &tempDay2);
-				
+
 				beginData.tm_year = tempYear - 1900;
 				beginData.tm_mon = tempMon - 1;
 				beginData.tm_mday = tempDay;
@@ -128,24 +167,34 @@ bool checkInVaildTime(string vaildTime, bool checkCurData)
 					else
 						return false;
 				}
-					
+
 			}
 		}
 	}
-	catch (exception *e)
+	catch (exception* e)
 	{
 		return false;
 	}
 	return true;
+
 }
 
 
 bool parseResourceIni(LPCTSTR filePath, vector<ResourceParse> &ret)
 {
+#ifdef RVC_OS_WIN
 	if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(filePath))
 	{
 		return FALSE;
 	}
+#else
+	struct stat buf;
+	if (stat(filePath, &buf))
+	{
+		return FALSE;
+	}
+#endif
+
 	ret.clear();
 	int mediaNum = ReadInterger(SECTION_GENERAL, GENERAL_MEDIANUM, filePath, 0);
 
@@ -153,7 +202,11 @@ bool parseResourceIni(LPCTSTR filePath, vector<ResourceParse> &ret)
 	{
 		//get section name
 		TCHAR sectionMedia[30] = STR_NULL;
+#ifdef RVC_OS_WIN
 		_stprintf_s(sectionMedia, _T("%s%d"), SECTION_MEDIA, i);
+#else
+		sprintf_s(sectionMedia, 30, "%s%d", SECTION_MEDIA, i);
+#endif // RVC_OS_WIN
 
 		ResourceParse curResource;
 		//»ñÈ¡typeÊôÐÔ¼°Ð£Ñé

+ 6 - 1
Module/mod_localmediaplay/AdvertManage/resourceIniParse.h

@@ -1,7 +1,13 @@
 #include <iostream>
 #include <vector>
 #include <fstream>
+
+#ifdef RVC_OS_WIN
 #include <windows.h>
+#endif // RVC_OS_WIN
+
+
+
 using namespace std;
 
 #define HEADINI_NAME		"HeadquartersLocalMediaPlay.ini"
@@ -36,7 +42,6 @@ typedef struct _resourceParse
 	string playTime;
 	int priority;
 	string resourcePath;
-
 }ResourceParse;
 
 

+ 8 - 1
Module/mod_localmediaplay/CMakeLists.txt

@@ -15,9 +15,11 @@ set(${MODULE_PREFIX}_SRCS
 	mod_localmediaplay.cpp
 	)
 
+
 set(MOD_VERSION_STRING "0.0.1-dev1")
 add_module_libraries(${MODULE_PREFIX} ${MODULE_NAME} ${MOD_VERSION_STRING})
 
+
 target_include_directories(${MODULE_NAME} PRIVATE
 )
 
@@ -28,7 +30,12 @@ target_link_directories(${MODULE_NAME} PRIVATE
 
 
 # 添加实体需要依赖的其他共享库(包括系统库)
-set(${MODULE_PREFIX}_LIBS  ${MODULE_BASE_LIBS} ${RVCCOMM_LIB} libwmpplayer libimgplayer ShLwApi)
+if(WIN32)
+set(${MODULE_PREFIX}_LIBS  ${MODULE_BASE_LIBS} ${RVCCOMM_LIB} ShLwApi Shell32 libimgplayer libwmpplayer)
+else(WIN32)
+set(${MODULE_PREFIX}_LIBS  ${MODULE_BASE_LIBS} ${RVCCOMM_LIB} libmediaplayer libpictureplayer)
+endif(WIN32)
+
 target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
 
 deploy_module(${MODULE_PREFIX} ${MODULE_NAME})

+ 476 - 97
Module/mod_localmediaplay/mod_localmediaplay.cpp

@@ -1,6 +1,7 @@
 #include "stdafx.h"
 #include "mod_localmediaplay.h"
 #include "LocalMediaPlay_msg_g.h"
+#include "fileutil.h"
 
 #define DEFAULT_SLEEP_TIME		(60 * 1000)
 #define DEFAULT_ADVERT_TYPE		'A'
@@ -8,14 +9,15 @@ bool IS_DEBUG = false;
 int scanTime = 600;
 int removeOldTime = 1800;
 
-DWORD WINAPI clearOutdata(LPVOID lpv)
-{
-	CLocalMediaPlayEntity* curEntity = (CLocalMediaPlayEntity*)lpv;
-	Sleep(removeOldTime * 1000);
-	curEntity->m_mediaManage.clearOutdataResource();
-	return 0;
-}
+//DWORD WINAPI clearOutdata(LPVOID lpv)
+//{
+//	CLocalMediaPlayEntity* curEntity = (CLocalMediaPlayEntity*)lpv;
+//	Sleep(removeOldTime * 1000);
+//	curEntity->m_mediaManage.clearOutdataResource();
+//	return 0;
+//}
 
+#ifdef RVC_OS_WIN
 DWORD WINAPI qryMedia(LPVOID lpv)
 {
 	CLocalMediaPlayEntity* curEntity = (CLocalMediaPlayEntity*)lpv;
@@ -28,58 +30,44 @@ DWORD WINAPI qryMedia(LPVOID lpv)
 		Sleep(3600 * 1000);
 	}
 
-	/*
-	while(true)
+	return 0;
+}
+
+#else
+void* queryMedia(void* param)
+{
+	CLocalMediaPlayEntity* curEntity = (CLocalMediaPlayEntity*)param;
+	Sleep(3000);
+
+	while (true)
 	{
-		if (!curEntity->SecureClientConnect())
-		{
-			Dbg("fail to connect to RvcResourceSyncEntity");
-			Sleep(DEFAULT_SLEEP_TIME);
-			continue;
-		}
-		//connect success
-		//curEntity->m_connection->QryVaildAdvertList();
-		CSystemStaticInfo si;
-		curEntity->GetFunction()->GetSystemStaticInfo(si);
-
-		vector<ResourceListRet> vaidResourceList;
-		if (Error_Succeed == curEntity->m_connection->QryVaildAdvertList(si.strTerminalID.GetData(), DEFAULT_ADVERT_TYPE, vaidResourceList))
-		{//获取有效的资源列表vaidResourceList
-			vector<string> curList;
-			for (vector<ResourceListRet>::iterator i = vaidResourceList.begin(); i != vaidResourceList.end(); i++)
-				curList.push_back(i->ResourceName);
-			curEntity->m_mediaManage.InitResourseList(curList);
-			//CloseHandle(CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&clearOutdata, curEntity, 0, NULL));	//获取成功的情况下进行清理过期资源
-			if (IS_DEBUG)
-			{
-				curEntity->StartAudio("CardIn.mp3");
-				//curEntity->StartVideo(1, 0, 0, 480, 360);
-// 				Sleep(10000);
-// 				curEntity->StopVideo(1);
-			}
-			Sleep(scanTime * 1000);
-		}
-		else
-			Sleep(DEFAULT_SLEEP_TIME);		//失败的情况下,等待短时间,重新尝试连接和获取
-		curEntity->SecureClientRelease();
-		
+		curEntity->m_mediaManage.InitResourceListByLocal();
+		Sleep(3600 * 1000);
 	}
-	*/
-	return 0;
 }
+#endif // RVC_OS_WIN
 
 
 CLocalMediaPlayEntity::CLocalMediaPlayEntity() 
-	: m_id_seq(0), m_pAudioPlayer(NULL), m_scanThread(NULL), m_connection(NULL)
+	: m_id_seq(0),  m_connection(NULL)
 {
 	//stopForDebug();
-	
 	m_defaultVolum = 50;
+
+#ifdef RVC_OS_WIN
+	m_scanThread=NULL;
+	m_pAudioPlayer = NULL;
 	memset(m_pVideoPlayer, 0, sizeof(m_pVideoPlayer));
 	memset(m_pImagePlayer, 0, sizeof(m_pImagePlayer));
+#else
+	//add by clp 20201103
+	m_scanThreadId = 0;
+	m_pMediaAudioPlayer = NULL;
+	memset(m_pMediaPlayer, 0, sizeof(m_pMediaPlayer));
+	memset(m_pPicturePlayer, 0, sizeof(m_pPicturePlayer));
+#endif // RVC_OS_WIN
+
 	ZeroMemory(&m_mediaParam, sizeof(MediaPlayParam));
-	
-	
 }
 
 CLocalMediaPlayEntity::~CLocalMediaPlayEntity()
@@ -87,12 +75,18 @@ CLocalMediaPlayEntity::~CLocalMediaPlayEntity()
 	SecureClientRelease();
 
 	DWORD exitCode = 0;
+#ifdef RVC_OS_WIN
 	if (m_scanThread)
 	{
 		TerminateThread(m_scanThread, exitCode);
 		m_scanThread = NULL;
 	}
-	
+#else
+	if (0 == pthread_kill(m_scanThreadId, 0))
+	{
+		pthread_cancel(m_scanThreadId);
+	}
+#endif // RVC_OS_WIN
 }
 
 void CLocalMediaPlayEntity::SecureClientRelease()
@@ -134,30 +128,17 @@ CServerSessionBase* CLocalMediaPlayEntity::OnNewSession(const char* pszRemoteEnt
 }
 
 
-
-BOOL CLocalMediaPlayEntity::LoadPlayConfig(CWmpPlayConfig &config, int CfgInx)
-{
-	if (CfgInx >= MAX_PLAY_CHANNELS || CfgInx < 0)
-	{
-		Dbg("Invalid CfgInx while LoadConfig!");
-		return FALSE;
-	}
-
-
-	if (config.eMode == LOCALAUDIO && CfgInx < m_defaultAudio.size())
-		memcpy(&config, &(m_defaultAudio[CfgInx]), sizeof(CWmpPlayConfig));
-	else if (config.eMode == LOCALVIDEO && CfgInx < m_defaultVideo.size())
-		memcpy(&config, &(m_defaultVideo[CfgInx]), sizeof(CWmpPlayConfig));
-	else
-		return FALSE;
-	return TRUE;
-}
-
 void CLocalMediaPlayEntity::loadDefaultMedia()
 {
+#ifdef RVC_OS_WIN
 	m_defaultImg.clear();
 	m_defaultAudio.clear();
 	m_defaultVideo.clear();
+#else
+	m_defaultPic.clear();
+	m_Audios.clear();
+	m_Videos.clear();
+#endif // RVC_OS_WIN
 
 	// 获取本地媒体根目录
 	CSimpleStringA strRootPath;
@@ -192,14 +173,19 @@ void CLocalMediaPlayEntity::loadDefaultMedia()
 				{
 					removeOldTime = 1800;
 				}
-				
 			}
 			else if (sectionList[i].IsStartWith("Image"))
 			{
-				// 加载图片配置
+			// 加载图片配置
+#ifdef RVC_OS_WIN
 				CImgPlayConfig curImg;
 				ZeroMemory(&curImg, sizeof(CImgPlayConfig));
-				CSimpleStringA imgPath = strRootPath + "\\Image\\";
+#else
+				CPicPlayConfig curImg;
+				ZeroMemory(&curImg, sizeof(CPicPlayConfig));
+#endif // RVC_OS_WIN
+
+				CSimpleStringA imgPath = strRootPath + SPLIT_SLASH_STR +"Image" + SPLIT_SLASH_STR;
 				strncpy(curImg.strRootPath, (LPCSTR)imgPath, sizeof(curImg.strRootPath));
 				Dbg("curSection: %s, config.strRootPath: %s", sectionList[i].GetData(), curImg.strRootPath);
 
@@ -231,17 +217,26 @@ void CLocalMediaPlayEntity::loadDefaultMedia()
 				}
 				delete[] Tmp; Tmp = NULL;
 
+#ifdef RVC_OS_WIN
 				m_defaultImg.push_back(curImg);
+#else
+				m_defaultPic.push_back(curImg);
+#endif // RVC_OS_WIN
 			}
 			else if (sectionList[i].IsStartWith("Video"))
 			{
+#ifdef RVC_OS_WIN
 				CWmpPlayConfig curVideo;
 				ZeroMemory(&curVideo, sizeof(CWmpPlayConfig));
-				CSimpleStringA videoPath = strRootPath + "\\Video\\";
+#else
+				CMediaPlayConfig curVideo;
+				ZeroMemory(&curVideo, sizeof(CMediaPlayConfig));
+#endif // RVC_OS_WIN
+
+				CSimpleStringA videoPath = strRootPath + SPLIT_SLASH_STR +"Video" + SPLIT_SLASH_STR;
 				strcpy(curVideo.strRootPath, (LPCSTR)videoPath);
 				Dbg("config.strRootPath: %s", curVideo.strRootPath);
 
-
 				CSimpleStringA strRunTime_S, strRunTime_E;
 				// 加载通用配置
 				table.AddEntryString("General", "VideoRunTime_Start", strRunTime_S, "");
@@ -290,18 +285,27 @@ void CLocalMediaPlayEntity::loadDefaultMedia()
 				for (int i = 0; i != FileCount; ++i)
 					strcpy(curVideo.strFileNames[i], Result[i]);
 				delete[] Tmp; Tmp = NULL;
+
+#ifdef RVC_OS_WIN
 				m_defaultVideo.push_back(curVideo);
+#else
+				m_Videos.push_back(curVideo);
+#endif // RVC_OS_WIN
 			}
 			else if (sectionList[i].IsStartWith("Audio"))
 			{
+#ifdef RVC_OS_WIN
 				CWmpPlayConfig curAudio;
 				ZeroMemory(&curAudio, sizeof(CWmpPlayConfig));
-				CSimpleStringA audioPath = strRootPath + "\\Audio\\";
+#else
+				CMediaPlayConfig curAudio;
+				ZeroMemory(&curAudio, sizeof(CMediaPlayConfig));
+#endif // RVC_OS_WIN
+
+				CSimpleStringA audioPath = strRootPath + SPLIT_SLASH_STR +"Audio" + SPLIT_SLASH_STR;
 				strcpy(curAudio.strRootPath, (LPCSTR)audioPath);
 				Dbg("config.strRootPath: %s", curAudio.strRootPath);
 
-				
-				
 				table.AddEntryInt(sectionList[i], "PlayCount", curAudio.nPlayCnt, 0);
 				table.AddEntryInt(sectionList[i], "PlayInterval", curAudio.nPlayInterval, 0);
 
@@ -317,14 +321,43 @@ void CLocalMediaPlayEntity::loadDefaultMedia()
 				else
 					Dbg("Fail to LoadWmpConfig!");
 
+#ifdef RVC_OS_WIN
 				m_defaultAudio.push_back(curAudio);
+#else
+				m_Audios.push_back(curAudio);
+#endif // RVC_OS_WIN
+
 			}
 		}
+	}
+}
 
+#ifdef RVC_OS_WIN
+
+BOOL CLocalMediaPlayEntity::LoadPlayConfig(CWmpPlayConfig& config, int CfgInx)
+{
+	if (CfgInx >= MAX_PLAY_CHANNELS || CfgInx < 0)
+	{
+		Dbg("Invalid CfgInx while LoadConfig!");
+		return FALSE;
+	}
+
+	if (config.eMode == LOCALAUDIO && CfgInx < m_defaultAudio.size())
+	{
+		memcpy(&config, &(m_defaultAudio[CfgInx]), sizeof(CWmpPlayConfig));
 	}
+	else if (config.eMode == LOCALVIDEO && CfgInx < m_defaultVideo.size())
+	{
+		memcpy(&config, &(m_defaultVideo[CfgInx]), sizeof(CWmpPlayConfig));
+	}
+	else {
+		return FALSE;
+	}
+
+	return TRUE;
 }
 
-BOOL CLocalMediaPlayEntity::LoadPlayConfig(CImgPlayConfig &config, int CfgInx)
+BOOL CLocalMediaPlayEntity::LoadPlayConfig(CImgPlayConfig& config, int CfgInx)
 {
 	if (CfgInx >= m_defaultImg.size() || CfgInx < 0)
 	{
@@ -337,7 +370,7 @@ BOOL CLocalMediaPlayEntity::LoadPlayConfig(CImgPlayConfig &config, int CfgInx)
 	return TRUE;
 }
 
-void CLocalMediaPlayEntity::WmpDebug(const char *fmt, ...)
+void CLocalMediaPlayEntity::WmpDebug(const char* fmt, ...)
 {
 	va_list arg;
 	va_start(arg, fmt);
@@ -345,7 +378,7 @@ void CLocalMediaPlayEntity::WmpDebug(const char *fmt, ...)
 	va_end(arg);
 }
 
-void CLocalMediaPlayEntity::ImgDebug(const char *fmt, ...)
+void CLocalMediaPlayEntity::ImgDebug(const char* fmt, ...)
 {
 	va_list arg;
 	va_start(arg, fmt);
@@ -353,6 +386,64 @@ void CLocalMediaPlayEntity::ImgDebug(const char *fmt, ...)
 	va_end(arg);
 }
 
+#else
+
+int CLocalMediaPlayEntity::LoadPlayConfig(CMediaPlayConfig& config, int CfgInx)
+{
+	if (CfgInx >= MAX_PLAY_CHANNELS || CfgInx < 0)
+	{
+		Dbg("Invalid CfgInx while LoadConfig!");
+		return -1;
+	}
+
+	if (config.eMode == LOCALAUDIO && CfgInx < m_Audios.size())
+	{
+		memcpy(&config, &(m_Audios[CfgInx]), sizeof(CMediaPlayConfig));
+	}
+	else if (config.eMode == LOCALVIDEO && CfgInx < m_Videos.size())
+	{
+		memcpy(&config, &(m_Videos[CfgInx]), sizeof(CMediaPlayConfig));
+	}
+	else {
+		return -1;
+	}
+
+	return 0;
+}
+
+int CLocalMediaPlayEntity::LoadPlayConfig(CPicPlayConfig& config, int CfgInx)
+{
+	if (CfgInx >= m_defaultPic.size() || CfgInx < 0)
+	{
+		Dbg("Invalid CfgInx while LoadConfig!");
+		return -1;
+	}
+
+	memcpy(&config, &(m_defaultPic[CfgInx]), sizeof(CPicPlayConfig));
+
+	return 0;
+}
+
+
+void CLocalMediaPlayEntity::Debug(const char* fmt, ...)
+{
+	va_list arg;
+	va_start(arg, fmt);
+	vDbg(fmt, arg);
+	va_end(arg);
+}
+
+void CLocalMediaPlayEntity::PicDebug(const char* fmt, ...)
+{
+	va_list arg;
+	va_start(arg, fmt);
+	vDbg(fmt, arg);
+	va_end(arg);
+}
+
+#endif // RVC_OS_WIN
+
+
 void CLocalMediaPlayEntity::OnLog(const CAutoArray<CUUID> &SubIDs, const CUUID nLogID, const LogTypeEnum eLogType, const SeverityLevelEnum eLevel,
 	const DWORD dwSysError, const DWORD dwUserCode, const DWORD dwEntityInstanceID, const WORD wEntityDevelID,
 	const CAutoArray<DWORD> &Param, const char *pszEntityName, const char *pszModuleName, const char *pszMessage)
@@ -367,6 +458,8 @@ void CLocalMediaPlayEntity::OnLog(const CAutoArray<CUUID> &SubIDs, const CUUID n
 
 void CLocalMediaPlayEntity::OnPreStart(CAutoArray<CSimpleStringA> strArgs, CSmartPointer<ITransactionContext> pTransactionContext)
 {
+	LOG_FUNCTION();
+
 	ErrorCodeEnum Error = __OnStart(Error_Succeed);
 
 	pTransactionContext->SendAnswer(Error);
@@ -375,12 +468,27 @@ void CLocalMediaPlayEntity::OnPreStart(CAutoArray<CSimpleStringA> strArgs, CSmar
 ErrorCodeEnum CLocalMediaPlayEntity::__OnStart(ErrorCodeEnum preOperationError)
 {
 	LOG_FUNCTION();
+
+#ifdef RVC_OS_WIN
+	//toolkit_setenv("SDL_AUDIODRIVER", "winmm");
+#endif // RVC_OS_WIN
+
 	for (int i = 0; i != MAX_PLAY_CHANNELS; ++i)
 	{
+#ifdef RVC_OS_WIN
 		m_pVideoPlayer[i] = new Clibwmpplayer(this);
 		m_pImagePlayer[i] = new Clibimgplayer(this);
+#else
+		m_pMediaPlayer[i] = new Clibmediaplayer(this);
+		m_pPicturePlayer[i] = new Clibpictureplayer(this);
+#endif 
 	}
+
+#ifdef RVC_OS_WIN
 	m_pAudioPlayer = new Clibwmpplayer(this);
+#else
+	m_pMediaAudioPlayer = new Clibmediaplayer(this);
+#endif // RVC_OS_WIN
 
 	if (!IsRunConfigExist())
 	{
@@ -388,18 +496,28 @@ ErrorCodeEnum CLocalMediaPlayEntity::__OnStart(ErrorCodeEnum preOperationError)
 		SetLocalAudioVolume(50);
 	}
 
-
 	//setMediaPath();
 	loadDefaultMedia();
 
-
 	// 订阅IEBrowser重启事件
 	GetFunction()->SubscribeLog(m_SubIDIEIdle, this, Log_Event, Severity_None, Error_IgnoreAll, LOG_EVT_SELFCHECK_IEBROWSER_IDLE, NULL, false);
 	// 实例化播放对象
 
-	m_scanThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&qryMedia, this, 0, NULL);
+#ifdef RVC_OS_WIN
+	m_scanThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)& qryMedia, this, 0, NULL);
 	if (NULL != m_scanThread)
-		Dbg("create qryMeia thread success, %d", m_scanThread);
+		Dbg("create qryMeia thread success, %d.", m_scanThread);
+#else
+	int err = pthread_create(&m_scanThreadId, NULL, queryMedia, this);
+	if (0 == err){
+		Dbg("create queryMedia thread success, %d.", m_scanThreadId);
+	}
+	else
+	{
+		Dbg("create queryMedia thread failed.");
+	}
+#endif // RVC_OS_WIN
+
 	return Error_Succeed;
 }
 
@@ -414,41 +532,80 @@ ErrorCodeEnum CLocalMediaPlayEntity::__OnClose(ErrorCodeEnum preOperationError)
 	LOG_FUNCTION();
 	for (int i = 0; i != MAX_PLAY_CHANNELS; ++i)
 	{
+#ifdef RVC_OS_WIN
 		delete m_pVideoPlayer[i]; m_pVideoPlayer[i] = NULL;
 		delete m_pImagePlayer[i]; m_pImagePlayer[i] = NULL;
+#else
+		delete m_pMediaPlayer[i];  m_pMediaPlayer[i] = NULL;
+		delete m_pPicturePlayer[i]; m_pPicturePlayer[i] = NULL;
+#endif // RVC_OS_WIN
 	}
+
+#ifdef RVC_OS_WIN
 	delete m_pAudioPlayer; m_pAudioPlayer = NULL;
+#else
+	delete m_pMediaAudioPlayer; m_pMediaAudioPlayer = NULL;
+#endif // RVC_OS_WIN
+	
 	// 退订返回主页事件
 	GetFunction()->UnsubscribeLog(m_SubIDIEIdle);
 	// 释放播放对象
 	return Error_Succeed;
 }
 
+#ifdef RVC_OS_WIN
 DWORD WINAPI CheckAudioThread(LPVOID param)
 {
 	CLocalMediaPlayEntity *entity = (CLocalMediaPlayEntity*)param;
 	Dbg("Begin CheckAudioThread");
 
+
 	HANDLE playThread = NULL;
 	if (!entity->m_pAudioPlayer->checkIsPlay(playThread))
 	{
 		Dbg("Create play audio Media Thread Failed!");
 		return 0;
 	}
-
 	WaitForSingleObject(playThread, INFINITE);
+	entity->m_pAudioPlayer->Close();
+
 	AudioPlayRet ret;
 	ret.AudioNames = entity->m_lastPlayAudio.c_str();
+	ret.ret = true;
+	Dbg("stop play audio %s success", ret.AudioNames);
+	SpSendBroadcast(entity->GetFunction(), eMsg_AudioPlayRet, eMsgSig_AudioPlayRet, ret);
 
-	entity->m_pAudioPlayer->Close();
+	return 0;
+}
+
+#else
+
+void* CheckAudioPlayingThreadFunc(void* param)
+{
+	CLocalMediaPlayEntity* entity = (CLocalMediaPlayEntity*)param;
+	Dbg("Begin CheckAudioPlayingThreadFunc");
+
+	pthread_t playThreadId = 0;
+	if (!entity->m_pMediaAudioPlayer->checkIsPlay(&playThreadId))
+	{
+		Dbg("Create play audio Media Thread Failed!");
+		return 0;
+	}
+	pthread_join(playThreadId, NULL);
+
+	AudioPlayRet ret;
+	ret.AudioNames = entity->m_lastPlayAudio.c_str();
 	ret.ret = true;
 	Dbg("stop play audio %s success", ret.AudioNames);
 	SpSendBroadcast(entity->GetFunction(), eMsg_AudioPlayRet, eMsgSig_AudioPlayRet, ret);
-		
 
 	return 0;
 }
 
+#endif // RVC_OS_WIN
+
+
+#ifdef RVC_OS_WIN
 DWORD WINAPI MediaPlayThread(LPVOID param)
 {
 	CLocalMediaPlayEntity *entity = (CLocalMediaPlayEntity*)param;
@@ -484,7 +641,7 @@ DWORD WINAPI MediaPlayThread(LPVOID param)
 			SYSTEMTIME st;
 			GetLocalTime(&st);
 			TCHAR strNow[TIME_LEN];
-			sprintf(strNow, "%02d:%02d:%02d", st.wHour,st.wMinute, st.wSecond);
+			sprintf(strNow, "%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
 			if (strcmp(strNow, config.strVideoRunTime_S) < 0 || strcmp(strNow, config.strVideoRunTime_E) >= 0)
 			{
 				Sleep(10000);
@@ -536,7 +693,7 @@ DWORD WINAPI MediaPlayThread(LPVOID param)
 				strncpy_s(config.strFileNames[0], i->videoNames.c_str(), 256);
 				strncpy_s(config.strVideoRunTime_S, "09:00:00", sizeof(config.strVideoRunTime_S));
 				strncpy_s(config.strVideoRunTime_E, "17:30:00", sizeof(config.strVideoRunTime_E));
-				
+
 				entity->m_pVideoPlayer[entity->m_mediaParam.nCfgInx]->PlayMedia(config);
 				if (!entity->m_pVideoPlayer[entity->m_mediaParam.nCfgInx]->checkIsPlay(playThread))
 				{
@@ -573,6 +730,7 @@ DWORD WINAPI MediaPlayThread(LPVOID param)
 					Dbg("Create play Image Media Thread Failed!");
 					return 0;
 				}
+
 				WaitForSingleObject(playThread, i->playInterval);
 				if (entity->m_pImagePlayer[entity->m_mediaParam.nCfgInx]->checkIsStop())
 				{//停止播放
@@ -584,10 +742,165 @@ DWORD WINAPI MediaPlayThread(LPVOID param)
 			}
 		}
 	}
-
-	
 	return 0;
 }
+#else
+int StartMediaPlay(void* param)
+{
+	int iRet = -1;
+	if (NULL == param)
+	{
+		return iRet;
+	}
+
+	CLocalMediaPlayEntity* entity = (CLocalMediaPlayEntity*)param;
+	while (TRUE)
+	{
+		vector<ResourceParse> curParse;
+		entity->m_mediaManage.GetPlayListByLocal(curParse);
+		if (0 == curParse.size())
+		{
+			int64_t playThreadId  = 0;
+			CMediaPlayConfig config;
+			ZeroMemory(&config, sizeof(CMediaPlayConfig));
+			memcpy(&config, &(entity->m_Videos[entity->m_mediaParam.nCfgInx]), sizeof(CMediaPlayConfig));
+			config.nPlayCnt = 1;	//can be change 
+			config.eMode = LOCALVIDEO;
+			config.nWndX = entity->m_mediaParam.nWndX;
+			config.nWndY = entity->m_mediaParam.nWndY;
+			config.nWndWidth = entity->m_mediaParam.nWndWidth;
+			config.nWndHeight = entity->m_mediaParam.nWndHeight;
+			if (IS_DEBUG)
+			{
+				config.bFullScreen = false;
+				config.bPrimMonitor = true;
+			}
+			// add by ly@2018/07/30
+			if (!entity->GetLocalVideoVolume(0, config.nVolume))
+			{
+				config.nVolume = entity->m_defaultVolum;
+			}
+			Dbg("config.nVolume=%d while play local video.", config.nVolume);
+			// 判断当前时间是否允许播放
+			struct tm* ptm = NULL;
+			time_t t = time(NULL);
+			ptm = localtime(&t);
+			TCHAR strNow[TIME_LEN] = {0};
+			sprintf(strNow, "%02d:%02d:%02d", ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
+			if (strcmp(strNow, config.strVideoRunTime_S) < 0 || strcmp(strNow, config.strVideoRunTime_E) >= 0)
+			{
+				Sleep(10000);
+				continue;
+			}
+			entity->m_pMediaPlayer[entity->m_mediaParam.nCfgInx]->PlayMedia(config);
+			if (!entity->m_pMediaPlayer[entity->m_mediaParam.nCfgInx]->checkIsPlay(&playThreadId))
+			{
+				Dbg("Create play Video Media Thread Failed!");
+				return iRet;
+			}
+			iRet = 0;
+			pthread_join(playThreadId, NULL);
+			if (entity->m_pMediaPlayer[entity->m_mediaParam.nCfgInx]->checkIsStop())
+			{//停止播放
+				Dbg("stop play video");
+				return iRet;
+			}
+		}
+
+		for (vector<ResourceParse>::iterator i = curParse.begin(); i != curParse.end(); i++)
+		{
+			int64_t playThreadId = 0;
+			Dbg("begin play extend %c:%s, %s", i->type, i->resourcePath.c_str(), i->videoNames.c_str());
+			if ('V' == i->type)
+			{//video
+				CMediaPlayConfig config;
+				ZeroMemory(&config, sizeof(CMediaPlayConfig));
+				config.bFullScreen = IS_DEBUG ? false : i->fullScreen;
+				config.bPrimMonitor = IS_DEBUG ? true : i->primMonitor;
+				config.bSimpleMode = i->simpleMode;
+				config.eMode = LOCALVIDEO;
+				config.nFileCnt = 1;
+				config.nPlayCnt = 1;
+				config.nPlayInterval = i->playInterval;
+				// add by ly@2018/07/30
+				//config.nVolume = IS_DEBUG ? 0 : entity->m_defaultVolum;
+				if (!entity->GetLocalVideoVolume(0, config.nVolume))
+				{
+					config.nVolume = entity->m_defaultVolum;
+				}
+				Dbg("config.nVolume=%d while play local video.", config.nVolume);
+				config.nWndX = entity->m_mediaParam.nWndX;
+				config.nWndY = entity->m_mediaParam.nWndY;
+				config.nWndWidth = entity->m_mediaParam.nWndWidth;
+				config.nWndHeight = entity->m_mediaParam.nWndHeight;
+				strncpy(config.strRootPath, i->resourcePath.c_str(), sizeof(config.strRootPath));
+				strncpy(config.strFileNames[0], i->videoNames.c_str(), 256);
+				strncpy(config.strVideoRunTime_S, "09:00:00", sizeof(config.strVideoRunTime_S));
+				strncpy(config.strVideoRunTime_E, "17:30:00", sizeof(config.strVideoRunTime_E));
+
+				entity->m_pMediaPlayer[entity->m_mediaParam.nCfgInx]->PlayMedia(config);
+				if (!entity->m_pMediaPlayer[entity->m_mediaParam.nCfgInx]->checkIsPlay(&playThreadId))
+				{
+					Dbg("Create play Video Media Thread Failed!");
+					return 0;
+				}
+				pthread_join(playThreadId, NULL);
+				if (entity->m_pMediaPlayer[entity->m_mediaParam.nCfgInx]->checkIsStop())
+				{//停止播放
+					Dbg("stop play video");
+					return 0;
+				}
+			}
+			else if ('P' == i->type)
+			{//play Image
+				CPicPlayConfig config;
+				ZeroMemory(&config, sizeof(CPicPlayConfig));
+				config.bFullScreen = IS_DEBUG ? false : i->fullScreen;
+				config.bPrimMonitor = IS_DEBUG ? true : i->primMonitor;
+				config.nFileCnt = 1;
+				config.nPlayCnt = 1;
+				config.nPlayInterval = i->playInterval;
+				config.nWndX = entity->m_mediaParam.nWndX;
+				config.nWndY = entity->m_mediaParam.nWndY;
+				config.nWndWidth = entity->m_mediaParam.nWndWidth;
+				config.nWndHeight = entity->m_mediaParam.nWndHeight;
+				strncpy(config.strRootPath, i->resourcePath.c_str(), sizeof(config.strRootPath));
+				strncpy(config.strFileNames[0], i->videoNames.c_str(), 256);
+				entity->m_pPicturePlayer[entity->m_mediaParam.nCfgInx]->PlayMedia(config);
+				if (!entity->m_pPicturePlayer[entity->m_mediaParam.nCfgInx]->checkIsPlay(&playThreadId))
+				{
+					Dbg("Create play Image Media Thread Failed!");
+					return -1;
+				}
+
+				//WaitForSingleObject(playThread, i->playInterval);
+				struct timespec ts;
+				{
+					int rc = clock_gettime(CLOCK_REALTIME, &ts);
+					if (rc)
+						return -1;
+				}
+				ts.tv_nsec += (i->playInterval * 1000000);
+				pthread_timedjoin_np(playThreadId, NULL, &ts);
+
+				if (entity->m_pPicturePlayer[entity->m_mediaParam.nCfgInx]->checkIsStop())
+				{//停止播放
+					Dbg("stop play Image");
+					return 0;
+				}
+				else
+				{
+					entity->m_pPicturePlayer[entity->m_mediaParam.nCfgInx]->Close();
+				}
+			}
+		}
+	}
+
+	return iRet;
+}
+
+#endif // RVC_OS_WIN
+
 
 
 void CLocalMediaPlayEntity::StartVideo(int nCfgInx, int nWndX, int nWndY, int nWndWidth, int nWndHeight)
@@ -604,20 +917,26 @@ void CLocalMediaPlayEntity::StartVideo(int nCfgInx, int nWndX, int nWndY, int nW
 	m_mediaParam.nWndWidth = nWndWidth;
 	m_mediaParam.nWndHeight = nWndHeight;
 
+#ifdef RVC_OS_WIN	// RVC_OS_WIN
 	if (NULL != m_playThread)
 	{
 		WaitForSingleObject(m_playThread, 5000);
 		m_playThread = NULL;
 	}
-
 	if (NULL == m_playThread)
-		m_playThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&MediaPlayThread, this, 0, NULL);	
+		m_playThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)& MediaPlayThread, this, 0, NULL);
+#else
+
+	StartMediaPlay(this);
+
+
+#endif 
 }
 
 void CLocalMediaPlayEntity::StartAudio(const char *pAudioNames)
 {
+#ifdef RVC_OS_WIN
 	static HANDLE audioPlayThread = NULL;
-
 	if (audioPlayThread)//关闭正在播放的音频
 	{
 		DWORD exitCode = 0;
@@ -629,19 +948,34 @@ void CLocalMediaPlayEntity::StartAudio(const char *pAudioNames)
 		audioPlayThread = NULL;
 	}
 
-	
-
+	Dbg("Begin StartAudio, and Audio Info is %s.", pAudioNames);
 	m_pAudioPlayer->PlayLocalAudio(pAudioNames);
-	Dbg("Succeed to StartAudio!, Audio Info:%s", pAudioNames);
+	Dbg("Succeed to StartAudio, and Audio Info is %s.", pAudioNames);
 
 	auto audioRet = GetLocalAudioVolume();
 	m_pAudioPlayer->SetVolume(audioRet.second);
 	Dbg("Succeed to set audio volume %d!", audioRet.second);
 
 	m_lastPlayAudio = pAudioNames;
-	audioPlayThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&CheckAudioThread, this, 0, NULL);
-	
+	audioPlayThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)& CheckAudioThread, this, 0, NULL);
+#else
+	static pthread_t audioPlayThreadId = 0;
+	if (0 == pthread_kill(audioPlayThreadId, 0)) {
+
+	}
+
+	Dbg("Begin StartAudio, and Audio Info is %s.", pAudioNames);
+	m_pMediaAudioPlayer->PlayLocalAudio(pAudioNames);
+	Dbg("Succeed to StartAudio, and Audio Info is %s.", pAudioNames);
+
+	auto audioRet = GetLocalAudioVolume();
+	m_pMediaAudioPlayer->SetVolume(audioRet.second);
+	Dbg("Succeed to set audio volume %d!", audioRet.second);
+	m_lastPlayAudio = pAudioNames;
+
 
+
+#endif // RVC_OS_WIN
 }
 
 void CLocalMediaPlayEntity::StartImage(int nCfgInx, int nWndX, int nWndY, int nWndWidth, int nWndHeight)
@@ -651,10 +985,17 @@ void CLocalMediaPlayEntity::StartImage(int nCfgInx, int nWndX, int nWndY, int nW
 		Dbg("Invalid CfgInx while StartImage!");
 		return;
 	}
+
+#ifdef RVC_OS_WIN
 	m_pImagePlayer[nCfgInx]->Play(nCfgInx, nWndX, nWndY, nWndWidth, nWndHeight);
+#else
+	m_pPicturePlayer[nCfgInx]->Play(nCfgInx, nWndX, nWndY, nWndWidth, nWndHeight);
+#endif // RVC_OS_WIN
+
 	Dbg("Succeed to StartImage!");
 }
 
+
 void CLocalMediaPlayEntity::StopVideo(int nCfgInx)
 {
 	if (nCfgInx >= MAX_PLAY_CHANNELS || nCfgInx < 0)
@@ -662,6 +1003,8 @@ void CLocalMediaPlayEntity::StopVideo(int nCfgInx)
 		Dbg("Invalid CfgInx while StopVideo!");
 		return;
 	}
+
+#ifdef RVC_OS_WIN
 	m_pVideoPlayer[nCfgInx]->Close();
 	m_pImagePlayer[nCfgInx]->Close();
 	if (m_playThread)
@@ -669,12 +1012,22 @@ void CLocalMediaPlayEntity::StopVideo(int nCfgInx)
 		TerminateThread(m_playThread, -1);
 		m_playThread = NULL;
 	}
+#else
+	m_pMediaPlayer[nCfgInx]->Close();
+	m_pPicturePlayer[nCfgInx]->Close();
+
+#endif // RVC_OS_WIN
+
 	Dbg("Succeed to StopVideo!");
 }
 
 void CLocalMediaPlayEntity::StopAudio()
 {
+#ifdef RVC_OS_WIN
 	m_pAudioPlayer->Close();
+#else
+	m_pMediaAudioPlayer->Close();
+#endif
 	Dbg("Succeed to StopAudio!");
 }
 
@@ -685,7 +1038,13 @@ void CLocalMediaPlayEntity::StopImage(int nCfgInx)
 		Dbg("Invalid CfgInx while StopImage!");
 		return;
 	}
+
+#ifdef RVC_OS_WIN
 	m_pImagePlayer[nCfgInx]->Close();
+#else
+	m_pPicturePlayer[nCfgInx]->Close();
+#endif // RVC_OS_WIN
+
 	Dbg("Succeed to StopImage!");
 }
 
@@ -743,6 +1102,7 @@ std::pair<bool, int> CLocalMediaPlayEntity::GetLocalAudioVolume()
 	Dbg("fail to get local audio volume!");
 	return std::make_pair(false, nVolume);
 }
+
 bool CLocalMediaPlayEntity::SetLocalAudioVolume(int nVolume)
 {
 	Dbg("set local video audio req.");
@@ -758,7 +1118,11 @@ bool CLocalMediaPlayEntity::SetLocalAudioVolume(int nVolume)
 		if (Error == Error_Succeed)
 		{
 			Dbg("succeed to set local audio volume with nVolume:%d!", nVolume);
+#ifdef RVC_OS_WIN
 			m_pAudioPlayer->SetVolume(nVolume);
+#else
+			m_pMediaAudioPlayer->SetVolume(nVolume);
+#endif // RVC_OS_WIN
 			return true;
 		}
 	}
@@ -784,7 +1148,11 @@ bool CLocalMediaPlayEntity::SetLocalVideoVolume(int nCfgInx, int nVolume)
 			Dbg("succeed to set local video volume with nCfgInx:%d, nVolume:%d!", nCfgInx, nVolume);
 			for (int i = 0; i < MAX_PLAY_CHANNELS; ++i)
 			{
+#ifdef RVC_OS_WIN
 				m_pVideoPlayer[i]->SetVolume(nVolume);
+#else
+				m_pMediaPlayer[i]->SetVolume(nVolume);
+#endif
 			}
 			return true;
 		}
@@ -805,11 +1173,22 @@ bool CLocalMediaPlayEntity::IsRunConfigExist()
 		return false;
 	}
 
+#ifdef RVC_OS_WIN
 	CSimpleStringA strRuninfoFile;
 	strRuninfoFile = CSimpleStringA::Format("%s\\runcfg\\%s.ini", (LPCTSTR)strPath, GetEntityName());
 	WIN32_FIND_DATA  findData;
 	if (FindFirstFileA((LPCTSTR)strRuninfoFile, &findData) != INVALID_HANDLE_VALUE) return true;
 	return false;
+#else
+	CSimpleStringA strRuninfoFile;
+	strRuninfoFile = CSimpleStringA::Format("%s" SPLIT_SLASH_STR "runcfg" SPLIT_SLASH_STR "%s.ini", (LPCTSTR)strPath, GetEntityName());
+	if (ExistsFileA(strRuninfoFile)) {
+		return true;
+	}
+	else{
+		return false;
+	}
+#endif // RVC_OS_WIN
 }
 
 void CLocalMediaPlayEntity::OnSelfTest(EntityTestEnum eTestType, CSmartPointer<ITransactionContext> pTransactionContext)

+ 76 - 19
Module/mod_localmediaplay/mod_localmediaplay.h

@@ -1,16 +1,27 @@
 #include "SpBase.h"
 #include "SpIni.h"
+
+#ifdef RVC_OS_WIN
 #include <Shlwapi.h>
-#pragma comment(lib, "ShLwApi.Lib")
+#include "../../Other/libwmpplayer/libwmpplayer.h"
+#include "../../Other/libimgplayer/libimgplayer.h"
+#else
+#include <pthread.h>
+#include <signal.h>
+#include "../../Other/libmediaplayer/libmediaplayer.h"
+#include "../../Other/libpictureplayer/libpictureplayer.h"
+#endif // RVC_OS_WIN
+
 
-#include "..\\..\\Other\\libwmpplayer\\libwmpplayer.h"
-#include "..\\..\\Other\\libimgplayer\\libimgplayer.h"
 #include "LocalMediaPlay_server_g.h"
 #include "AdvertBase/AdvertSync.h"
 #include "AdvertManage/mediaManage.h"
 #include <iostream>
 
 
+//#include "SpTest.h"
+#include "modVer.h"
+
 
 using namespace LocalMediaPlay;
 
@@ -25,12 +36,16 @@ typedef struct {
 	int nWndHeight;
 }MediaPlayParam;
 
-DWORD WINAPI qryMedia(LPVOID lpv);
-DWORD WINAPI MediaPlayThread(LPVOID param);
-DWORD WINAPI clearOutdata(LPVOID lpv);
-DWORD WINAPI CheckAudioThread(LPVOID param);
+//DWORD WINAPI qryMedia(LPVOID lpv);
+//DWORD WINAPI MediaPlayThread(LPVOID param);
+//DWORD WINAPI clearOutdata(LPVOID lpv);
+//DWORD WINAPI CheckAudioThread(LPVOID param);
 
+#ifdef RVC_OS_WIN
 class CLocalMediaPlayEntity : public CWmpHostApi, public CImgHostApi, public CEntityBase, public ILogListener
+#else
+class CLocalMediaPlayEntity : public CMediaHostApi, public CPicHostApi, public CEntityBase, public ILogListener
+#endif // RVC_OS_WIN
 {
 
 public:
@@ -41,15 +56,24 @@ public:
 
 	virtual bool IsService() const { return true; }
 
-	virtual CServerSessionBase *OnNewSession(const char* pszRemoteEntityName, const char * pszClass);
-
-	virtual BOOL LoadPlayConfig(CWmpPlayConfig &config, int CfgInx);
-
-	virtual BOOL LoadPlayConfig(CImgPlayConfig &config, int CfgInx);
+	const char* GetEntityVersion() const override
+	{
+		return MODULE_VERSION_FULL;
+	}
 
-	virtual void WmpDebug(const char *fmt, ...);
+	virtual CServerSessionBase *OnNewSession(const char* pszRemoteEntityName, const char * pszClass);
 
-	virtual void ImgDebug(const char *fmt, ...);
+#ifdef RVC_OS_WIN
+	virtual BOOL LoadPlayConfig(CWmpPlayConfig& config, int CfgInx);
+	virtual BOOL LoadPlayConfig(CImgPlayConfig& config, int CfgInx);
+	virtual void WmpDebug(const char* fmt, ...);
+	virtual void ImgDebug(const char* fmt, ...);
+#else
+	virtual int LoadPlayConfig(CMediaPlayConfig& config, int CfgInx);
+	virtual int LoadPlayConfig(CPicPlayConfig& config, int CfgInx);
+	virtual void Debug(const char* fmt, ...);
+	virtual void PicDebug(const char* fmt, ...);
+#endif // _WIN32
 
 	virtual void OnLog(const CAutoArray<CUUID> &SubIDs, const CUUID nLogID, const LogTypeEnum eLogType, const SeverityLevelEnum eLevel,
 		const DWORD dwSysError, const DWORD dwUserCode, const DWORD dwEntityInstanceID, const WORD wEntityDevelID,
@@ -83,6 +107,15 @@ public:
 	bool SetLocalAudioVolume(int nVolume);
 	bool IsRunConfigExist();
 
+
+protected:
+	// 音频播放对象指针
+#ifdef RVC_OS_WIN
+	Clibwmpplayer* m_pAudioPlayer;
+#else
+	Clibmediaplayer* m_pMediaAudioPlayer;
+#endif // RVC_OS_WIN
+
 private:
 
 	virtual void OnSelfTest(EntityTestEnum eTestType, CSmartPointer<ITransactionContext> pTransactionContext);
@@ -90,10 +123,19 @@ private:
 	// 按分隔符分隔字符串
 	void CStringSplit(char *str, char **result, const char *del);
 
-	friend DWORD WINAPI clearOutdata(LPVOID lpv);
-	friend DWORD WINAPI qryMedia(LPVOID lpv);
+
+	//friend DWORD WINAPI clearOutdata(LPVOID lpv);
+	
+#ifdef RVC_OS_WIN
 	friend DWORD WINAPI MediaPlayThread(LPVOID param);
+	friend DWORD WINAPI qryMedia(LPVOID lpv);
 	friend DWORD WINAPI CheckAudioThread(LPVOID param);
+#else
+	friend void* queryMedia(void* param);
+	friend void* CheckAudioPlayingThreadFunc(void* param);
+	friend int StartMediaPlay(void* param);
+#endif // RVC_OS_WIN
+
 	void setMediaPath();
 	void SecureClientRelease();
 	bool SecureClientConnect();
@@ -103,22 +145,37 @@ private:
 private:
 	int m_id_seq;
 	CUUID m_SubIDIEIdle;
+
+#ifdef RVC_OS_WIN
 	// 视频播放对象指针数组
 	Clibwmpplayer* m_pVideoPlayer[MAX_PLAY_CHANNELS];
-	// 音频播放对象指针
-	Clibwmpplayer* m_pAudioPlayer;
 	// 图片播放对象指针数组
 	Clibimgplayer* m_pImagePlayer[MAX_PLAY_CHANNELS];
+#else
+	// 媒体播放对象指针数组
+	Clibmediaplayer* m_pMediaPlayer[MAX_PLAY_CHANNELS];
+	// 图片播放对象指针数组    add by clp   20201103
+	Clibpictureplayer* m_pPicturePlayer[MAX_PLAY_CHANNELS];
+#endif // RVC_OS_WIN
+
 	//连接获取最新广告资源
 	CAdvertSyncConnection *m_connection;
 	mediaManage m_mediaManage;
 	MediaPlayParam m_mediaParam;
 	int m_defaultVolum;
-    HANDLE m_scanThread, m_playThread;
 	std::string m_lastPlayAudio;
+
+#ifdef RVC_OS_WIN
+	HANDLE m_scanThread, m_playThread;
 	vector<CWmpPlayConfig> m_defaultVideo;
 	vector<CWmpPlayConfig> m_defaultAudio;
 	vector<CImgPlayConfig> m_defaultImg;
+#else
+	pthread_t m_scanThreadId;
+	vector<CMediaPlayConfig> m_Videos;
+	vector<CMediaPlayConfig> m_Audios;
+	vector<CPicPlayConfig> m_defaultPic;
+#endif // RVC_OS_WIN
 };
 
 

+ 8 - 3
Module/mod_localmediaplay/stdafx.h

@@ -15,17 +15,22 @@
 #endif 
 #endif
 
-#include <WinSock2.h>
 #ifndef WIN32_LEAN_AND_MEAN
 #define WIN32_LEAN_AND_MEAN
 #endif
-#include <Mmsystem.h>
-#include <windows.h>
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <assert.h>
+
+#ifdef _WIN32
+#include <WinSock2.h>
+#include <Mmsystem.h>
+#include <windows.h>
 #include <crtdbg.h>
 #include <process.h>
+#endif // _WIN32
+
 
 #pragma warning( disable: 4127 )
 

+ 45 - 0
Other/libimgplayer/CMakeLists.txt

@@ -0,0 +1,45 @@
+set(MODULE_NAME "libimgplayer")
+set(MODULE_PREFIX "LIB_IMGPLAYER_FUNC")
+
+set(${MODULE_PREFIX}_SRCS
+	resource.h
+	stdafx.h
+	stdafx.cpp
+	targetver.h
+	libimgplayer.rc
+	CImgPlayerDlg.h
+	CImgPlayerDlg.cpp
+	ClibimgplayerApp.h
+	ClibimgplayerApp.cpp
+	libimgplayer.h
+	libimgplayer.cpp
+)
+
+add_library(${MODULE_NAME} SHARED ${${MODULE_PREFIX}_SRCS})
+
+target_include_directories(${MODULE_NAME} PRIVATE
+	${RVC_COMMON_INCLUDE_DIR}
+	)
+
+
+if(MSVC)
+	set(CMAKE_MFC_FLAG 2)
+	ADD_DEFINITIONS(-D_AFXDLL)
+	target_compile_definitions(${MODULE_NAME} PUBLIC "_USRDLL")
+endif(MSVC)
+
+
+
+if(MSVC)
+	install(TARGETS ${MODULE_NAME} 
+    RUNTIME DESTINATION "${RVC_RUNTIME_PATH}" COMPONENT libraries
+    ARCHIVE DESTINATION "${RVC_LIBRARY_PATH}" COMPONENT develops EXCLUDE_FROM_ALL
+    LIBRARY DESTINATION "${RVC_LIBRARY_PATH}" COMPONENT libraries
+    )
+else()
+install(TARGETS ${MODULE_NAME} 
+    RUNTIME DESTINATION "${RVC_RUNTIME_PATH}"
+    ARCHIVE DESTINATION "${RVC_LIBRARY_PATH}"
+    LIBRARY DESTINATION "${RVC_RUNTIME_PATH}"
+    COMPONENT libraries)
+endif(MSVC)

+ 3 - 0
Other/libimgplayer/libimgplayer.h

@@ -6,7 +6,10 @@
 #define LIBIMGPLAYER_API __declspec(dllimport)
 #endif
 
+#ifndef MAX_FILECOUNT
 #define MAX_FILECOUNT 32
+#endif // !MAX_FILECOUNT
+
 
 struct CImgPlayConfig
 {	

+ 85 - 0
Other/libmediaplayer/CMakeLists.txt

@@ -0,0 +1,85 @@
+set(MODULE_NAME "libmediaplayer")
+set(MODULE_PREFIX "LIB_MEDIAPLAYER_FUNC")
+
+if(RVC_DEBUG_MODE)
+    set(SPBASE_LIB spbased)
+else()
+    set(SPBASE_LIB spbase)
+endif(RVC_DEBUG_MODE)
+
+set(${MODULE_PREFIX}_SRCS
+	libmediaplayer.h
+	libmediaplayer.cpp
+	player.h
+	player.cpp
+	audio.h
+	audio.cpp
+	demux.h
+	demux.cpp
+	frame.h
+	frame.cpp
+	packet.h
+	packet.cpp
+	video.h
+	video.cpp
+)
+
+add_library(${MODULE_NAME} SHARED ${${MODULE_PREFIX}_SRCS})
+
+if(WIN32)
+conan_cmake_run(REQUIRES ffmpeg/4.1@LR04.02_ThirdParty/testing
+BASIC_SETUP CMAKE_TARGETS
+BUILD missing)
+else(WIN32)
+conan_cmake_run(REQUIRES ffmpeg/4.1@LR04.02_ThirdParty/testing
+BASIC_SETUP CMAKE_TARGETS
+BUILD missing)
+endif(WIN32)
+
+
+if(WIN32)
+conan_cmake_run(REQUIRES SDL2/2.0.9@LR04.02_ThirdParty/testing
+BASIC_SETUP CMAKE_TARGETS
+BUILD missing)
+else(WIN32)
+conan_cmake_run(REQUIRES SDL2/2.0.9@LR04.02_ThirdParty/testing
+BASIC_SETUP CMAKE_TARGETS
+BUILD missing)
+endif(WIN32)
+
+target_include_directories(${MODULE_NAME} PRIVATE
+	${RVC_COMMON_INCLUDE_DIR}
+	${CONAN_INCLUDE_DIRS_FFMPEG}
+	${CONAN_INCLUDE_DIRS_SDL2}
+	${CONAN_RVCFRAMEWORK_ROOT}/include
+	)
+
+
+target_link_directories(${MODULE_NAME} PRIVATE
+	${CONAN_LIB_DIRS_FFMPEG}
+	${CONAN_LIB_DIRS_SDL2}
+	${CONAN_LIB_DIRS_RVCFRAMEWORK}
+	)
+
+
+target_link_libraries(${MODULE_NAME} PRIVATE ${${MODULE_PREFIX}_LIBS} PRIVATE
+	${CONAN_LIBS_FFMPEG}
+	${CONAN_LIBS_SDL2}
+	${SPBASE_LIB}
+	)  
+
+target_compile_definitions(${MODULE_NAME} PUBLIC "LIBMEDIAPLAYER_EXPORTS")
+
+if(MSVC)
+	install(TARGETS ${MODULE_NAME} 
+    RUNTIME DESTINATION "${RVC_RUNTIME_PATH}" COMPONENT libraries
+    ARCHIVE DESTINATION "${RVC_LIBRARY_PATH}" COMPONENT develops EXCLUDE_FROM_ALL
+    LIBRARY DESTINATION "${RVC_LIBRARY_PATH}" COMPONENT libraries
+    )
+else()
+install(TARGETS ${MODULE_NAME} 
+    RUNTIME DESTINATION "${RVC_RUNTIME_PATH}"
+    ARCHIVE DESTINATION "${RVC_LIBRARY_PATH}"
+    LIBRARY DESTINATION "${RVC_RUNTIME_PATH}"
+    COMPONENT libraries)
+endif(MSVC)

+ 475 - 0
Other/libmediaplayer/audio.cpp

@@ -0,0 +1,475 @@
+#include "player.h"
+#include "packet.h"
+#include "frame.h"
+
+static void sdl_audio_callback(void *opaque, Uint8 *stream, int len);
+
+// 从packet_queue中取一个packet,解码生成frame
+static int audio_decode_frame(AVCodecContext *p_codec_ctx, packet_queue_t *p_pkt_queue, AVFrame *frame, play_logfun rvclog)
+{
+    int ret;
+
+    while (1)
+    {
+        AVPacket pkt;
+
+        while (1)
+        {
+            //if (d->queue->abort_request)
+            //    return -1;
+
+            // 3.2 一个音频packet含一至多个音频frame,每次avcodec_receive_frame()返回一个frame,此函数返回。
+            // 下次进来此函数,继续获取一个frame,直到avcodec_receive_frame()返回AVERROR(EAGAIN),
+            // 表示解码器需要填入新的音频packet
+            ret = avcodec_receive_frame(p_codec_ctx, frame);
+            if (ret >= 0)
+            {
+                // 时基转换,从d->avctx->pkt_timebase时基转换到1/frame->sample_rate时基
+				AVRational tb{ 1, frame->sample_rate };
+                if (frame->pts != AV_NOPTS_VALUE)
+                {
+                    frame->pts = av_rescale_q(frame->pts, p_codec_ctx->pkt_timebase, tb);
+                }
+                else
+                {
+                    //av_log(NULL, AV_LOG_WARNING, "frame->pts no.\n");
+					//rvclog("frame->pts no.");
+                }
+
+                return 1;
+            }
+            else if (ret == AVERROR_EOF)
+            {
+                //av_log(NULL, AV_LOG_INFO, "audio avcodec_receive_frame(): the decoder has been flushed.\n");
+				//rvclog("audio avcodec_receive_frame(): the decoder has been flushed.");
+                avcodec_flush_buffers(p_codec_ctx);
+                return 0;
+            }
+            else if (ret == AVERROR(EAGAIN))
+            {
+                //av_log(NULL, AV_LOG_INFO, "audio avcodec_receive_frame(): input is not accepted in the current state.\n");
+				//rvclog("audio avcodec_receive_frame(): input is not accepted in the current state.");
+				break;
+            }
+            else
+            {
+                //av_log(NULL, AV_LOG_ERROR, "audio avcodec_receive_frame(): other errors.\n");
+				//rvclog( "audio avcodec_receive_frame(): other errors.");
+				continue;
+            }
+        }
+
+        // 1. 取出一个packet。使用pkt对应的serial赋值给d->pkt_serial
+        if (packet_queue_get(p_pkt_queue, &pkt, true) < 0)
+        {
+            return -1;
+        }
+
+        // packet_queue中第一个总是flush_pkt。每次seek操作会插入flush_pkt,更新serial,开启新的播放序列
+        if (pkt.data == NULL)
+        {
+            // 复位解码器内部状态/刷新内部缓冲区。当seek操作或切换流时应调用此函数。
+            avcodec_flush_buffers(p_codec_ctx);
+        }
+        else
+        {
+            // 2. 将packet发送给解码器
+            //    发送packet的顺序是按dts递增的顺序,如IPBBPBB
+            //    pkt.pos变量可以标识当前packet在视频文件中的地址偏移
+            if (avcodec_send_packet(p_codec_ctx, &pkt) == AVERROR(EAGAIN))
+            {
+                //av_log(NULL, AV_LOG_ERROR, "receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
+				//rvclog("receive_frame and send_packet both returned EAGAIN, which is an API violation.");
+            }
+
+            av_packet_unref(&pkt);
+        }
+    }
+}
+
+// 音频解码线程:从音频packet_queue中取数据,解码后放入音频frame_queue
+static int audio_decode_thread(void *arg)
+{
+    player_stat_t *is = (player_stat_t *)arg;
+    AVFrame *p_frame = av_frame_alloc();
+    frame_t *af;
+
+    int got_frame = 0;
+    AVRational tb;
+    int ret = 0;
+
+    if (p_frame == NULL)
+    {
+        return AVERROR(ENOMEM);
+    }
+
+	//is->rvc_log("audio_decode_thread is->abort_request == %d.", is->abort_request);
+    while (0 == is->abort_request)
+    {
+        got_frame = audio_decode_frame(is->p_acodec_ctx, &is->audio_pkt_queue, p_frame, is->rvc_log);
+        if (got_frame < 0)
+        {
+            goto the_end;
+        }
+
+        if (got_frame)
+        {
+            tb = { 1, p_frame->sample_rate };
+
+            if (!(af = frame_queue_peek_writable(&is->audio_frm_queue)))
+                goto the_end;
+
+            af->pts = (p_frame->pts == AV_NOPTS_VALUE) ? NAN : p_frame->pts * av_q2d(tb);
+            af->pos = p_frame->pkt_pos;
+            //-af->serial = is->auddec.pkt_serial;
+            // 当前帧包含的(单个声道)采样数/采样率就是当前帧的播放时长
+			AVRational tbdata{ p_frame->nb_samples, p_frame->sample_rate };
+            //af->duration = av_q2d((AVRational) { p_frame->nb_samples, p_frame->sample_rate });
+			af->duration = av_q2d(tbdata);
+            // 将frame数据拷入af->frame,af->frame指向音频frame队列尾部
+            av_frame_move_ref(af->frame, p_frame);
+            // 更新音频frame队列大小及写指针
+            frame_queue_push(&is->audio_frm_queue);
+        }
+    }
+
+the_end:
+    av_frame_free(&p_frame);
+    return ret;
+}
+
+int open_audio_stream(player_stat_t *is)
+{
+    AVCodecContext *p_codec_ctx;
+    AVCodecParameters *p_codec_par = NULL;
+    AVCodec* p_codec = NULL;
+    int ret;
+
+    // 1. 为音频流构建解码器AVCodecContext
+
+    // 1.1 获取解码器参数AVCodecParameters
+    p_codec_par = is->p_audio_stream->codecpar;
+    // 1.2 获取解码器
+    p_codec = avcodec_find_decoder(p_codec_par->codec_id);
+    if (p_codec == NULL)
+    {
+        //av_log(NULL, AV_LOG_ERROR, "Cann't find codec!\n");
+		//is->rvc_log("Cann't find codec!");
+		return -1;
+    }
+
+    // 1.3 构建解码器AVCodecContext
+    // 1.3.1 p_codec_ctx初始化:分配结构体,使用p_codec初始化相应成员为默认值
+    p_codec_ctx = avcodec_alloc_context3(p_codec);
+    if (p_codec_ctx == NULL)
+    {
+        //av_log(NULL, AV_LOG_ERROR, "avcodec_alloc_context3() failed\n");
+		//is->rvc_log("avcodec_alloc_context3() failed.");
+		return -1;
+    }
+    // 1.3.2 p_codec_ctx初始化:p_codec_par ==> p_codec_ctx,初始化相应成员
+    ret = avcodec_parameters_to_context(p_codec_ctx, p_codec_par);
+    if (ret < 0)
+    {
+        //av_log(NULL, AV_LOG_ERROR, "avcodec_parameters_to_context() failed %d\n", ret);
+		//is->rvc_log("avcodec_parameters_to_context() failed %d.", ret);
+        return -1;
+    }
+    // 1.3.3 p_codec_ctx初始化:使用p_codec初始化p_codec_ctx,初始化完成
+    ret = avcodec_open2(p_codec_ctx, p_codec, NULL);
+    if (ret < 0)
+    {
+        //av_log(NULL, AV_LOG_ERROR, "avcodec_open2() failed %d\n", ret);
+		//is->rvc_log("avcodec_open2() failed %d.", ret);
+        return -1;
+    }
+
+    p_codec_ctx->pkt_timebase = is->p_audio_stream->time_base;
+    is->p_acodec_ctx = p_codec_ctx;
+
+    // 2. 创建音频解码线程
+    SDL_CreateThread(audio_decode_thread, "audio decode thread", is);
+
+    return 0;
+}
+
+static int audio_resample(player_stat_t *is, int64_t audio_callback_time)
+{
+    int data_size, resampled_data_size;
+    int64_t dec_channel_layout;
+    av_unused double audio_clock0;
+    int wanted_nb_samples;
+    frame_t *af;
+
+#if defined(_WIN32)
+    while (frame_queue_nb_remaining(&is->audio_frm_queue) == 0)
+    {
+        if ((av_gettime_relative() - audio_callback_time) > 1000000LL * is->audio_hw_buf_size / is->audio_param_tgt.bytes_per_sec / 2)
+            return -1;
+        av_usleep(1000);
+    }
+#endif
+
+    // 若队列头部可读,则由af指向可读帧
+    if (!(af = frame_queue_peek_readable(&is->audio_frm_queue)))
+        return -1;
+    frame_queue_next(&is->audio_frm_queue);
+
+    // 根据frame中指定的音频参数获取缓冲区的大小
+    data_size = av_samples_get_buffer_size(NULL, af->frame->channels,   // 本行两参数:linesize,声道数
+        af->frame->nb_samples,       // 本行一参数:本帧中包含的单个声道中的样本数
+        (AVSampleFormat)af->frame->format, 1);       // 本行两参数:采样格式,不对齐
+
+// 获取声道布局
+    dec_channel_layout =
+        (af->frame->channel_layout && af->frame->channels == av_get_channel_layout_nb_channels(af->frame->channel_layout)) ?
+        af->frame->channel_layout : av_get_default_channel_layout(af->frame->channels);
+    wanted_nb_samples = af->frame->nb_samples;
+
+    // is->audio_param_tgt是SDL可接受的音频帧数,是audio_open()中取得的参数
+    // 在audio_open()函数中又有“is->audio_src = is->audio_param_tgt”
+    // 此处表示:如果frame中的音频参数 == is->audio_src == is->audio_param_tgt,那音频重采样的过程就免了(因此时is->swr_ctr是NULL)
+    //      否则使用frame(源)和is->audio_param_tgt(目标)中的音频参数来设置is->swr_ctx,并使用frame中的音频参数来赋值is->audio_src
+    if (af->frame->format != is->audio_param_src.fmt ||
+        dec_channel_layout != is->audio_param_src.channel_layout ||
+        af->frame->sample_rate != is->audio_param_src.freq)
+    {
+        swr_free(&is->audio_swr_ctx);
+        // 使用frame(源)和is->audio_param_tgt(目标)中的音频参数来设置is->audio_swr_ctx
+        is->audio_swr_ctx = swr_alloc_set_opts(NULL,
+            is->audio_param_tgt.channel_layout, is->audio_param_tgt.fmt, is->audio_param_tgt.freq,
+            dec_channel_layout, (AVSampleFormat)af->frame->format, af->frame->sample_rate,
+            0, NULL);
+        if (!is->audio_swr_ctx || swr_init(is->audio_swr_ctx) < 0)
+        {
+            //av_log(NULL, AV_LOG_ERROR,
+            //    "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n",
+            //    af->frame->sample_rate, av_get_sample_fmt_name((AVSampleFormat)af->frame->format), af->frame->channels,
+            //    is->audio_param_tgt.freq, av_get_sample_fmt_name(is->audio_param_tgt.fmt), is->audio_param_tgt.channels);
+			is->rvc_log("Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n",
+				af->frame->sample_rate, av_get_sample_fmt_name((AVSampleFormat)af->frame->format), af->frame->channels,
+				is->audio_param_tgt.freq, av_get_sample_fmt_name(is->audio_param_tgt.fmt), is->audio_param_tgt.channels);
+            swr_free(&is->audio_swr_ctx);
+            return -1;
+        }
+        // 使用frame中的参数更新is->audio_src,第一次更新后后面基本不用执行此if分支了,因为一个音频流中各frame通用参数一样
+        is->audio_param_src.channel_layout = dec_channel_layout;
+        is->audio_param_src.channels = af->frame->channels;
+        is->audio_param_src.freq = af->frame->sample_rate;
+        is->audio_param_src.fmt = (AVSampleFormat)af->frame->format;
+    }
+	
+    if (is->audio_swr_ctx)
+    {
+        // 重采样输入参数1:输入音频样本数是af->frame->nb_samples
+        // 重采样输入参数2:输入音频缓冲区
+        const uint8_t **in = (const uint8_t **)af->frame->extended_data;
+        // 重采样输出参数1:输出音频缓冲区尺寸
+        // 重采样输出参数2:输出音频缓冲区
+        uint8_t **out = &is->audio_frm_rwr;
+        // 重采样输出参数:输出音频样本数(多加了256个样本)
+        int out_count = (int64_t)wanted_nb_samples * is->audio_param_tgt.freq / af->frame->sample_rate + 256;
+        // 重采样输出参数:输出音频缓冲区尺寸(以字节为单位)
+        int out_size = av_samples_get_buffer_size(NULL, is->audio_param_tgt.channels, out_count, is->audio_param_tgt.fmt, 0);
+        int len2;
+        if (out_size < 0)
+        {
+            //av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size() failed\n");
+			//is->rvc_log("av_samples_get_buffer_size() failed.");
+            return -1;
+        }
+        av_fast_malloc(&is->audio_frm_rwr, &is->audio_frm_rwr_size, out_size);
+        if (!is->audio_frm_rwr)
+            return AVERROR(ENOMEM);
+        // 音频重采样:返回值是重采样后得到的音频数据中单个声道的样本数
+        len2 = swr_convert(is->audio_swr_ctx, out, out_count, in, af->frame->nb_samples);
+        if (len2 < 0)
+        {
+            //av_log(NULL, AV_LOG_ERROR, "swr_convert() failed\n");
+			//is->rvc_log("swr_convert() failed.");
+            return -1;
+        }
+        if (len2 == out_count)
+        {
+            //av_log(NULL, AV_LOG_WARNING, "audio buffer is probably too small\n");
+			//is->rvc_log("audio buffer is probably too small.");
+			if (swr_init(is->audio_swr_ctx) < 0)
+                swr_free(&is->audio_swr_ctx);
+        }
+        is->p_audio_frm = is->audio_frm_rwr;
+        // 重采样返回的一帧音频数据大小(以字节为单位)
+        resampled_data_size = len2 * is->audio_param_tgt.channels * av_get_bytes_per_sample(is->audio_param_tgt.fmt);
+    }
+    else
+    {
+        // 未经重采样,则将指针指向frame中的音频数据
+        is->p_audio_frm = af->frame->data[0];
+        resampled_data_size = data_size;
+    }
+
+    audio_clock0 = is->audio_clock;
+    /* update the audio clock with the pts */
+    if (!isnan(af->pts))
+    {
+        is->audio_clock = af->pts + (double)af->frame->nb_samples / af->frame->sample_rate;
+    }
+    else
+    {
+        is->audio_clock = NAN;
+    }
+    is->audio_clock_serial = af->serial;
+#ifdef DEBUG
+    {
+        static double last_clock;
+        //printf("audio: delay=%0.3f clock=%0.3f clock0=%0.3f\n",
+        //    is->audio_clock - last_clock,
+        //    is->audio_clock, audio_clock0);
+		//is->rvc_log("audio: delay=%0.3f clock=%0.3f clock0=%0.3f\n",
+		//	is->audio_clock - last_clock,
+		//	is->audio_clock, audio_clock0);
+        last_clock = is->audio_clock;
+    }
+#endif
+    return resampled_data_size;
+}
+
+static int open_audio_playing(void *arg)
+{
+    player_stat_t *is = (player_stat_t *)arg;
+    SDL_AudioSpec wanted_spec;
+    SDL_AudioSpec actual_spec;
+
+    // 2. 打开音频设备并创建音频处理线程
+    // 2.1 打开音频设备,获取SDL设备支持的音频参数actual_spec(期望的参数是wanted_spec,实际得到actual_spec)
+    // 1) SDL提供两种使音频设备取得音频数据方法:
+    //    a. push,SDL以特定的频率调用回调函数,在回调函数中取得音频数据
+    //    b. pull,用户程序以特定的频率调用SDL_QueueAudio(),向音频设备提供数据。此种情况wanted_spec.callback=NULL
+    // 2) 音频设备打开后播放静音,不启动回调,调用SDL_PauseAudio(0)后启动回调,开始正常播放音频
+    wanted_spec.freq = is->p_acodec_ctx->sample_rate;   // 采样率
+    wanted_spec.format = AUDIO_S16SYS;                  // S表带符号,16是采样深度,SYS表采用系统字节序
+    wanted_spec.channels = is->p_acodec_ctx->channels;  // 声音通道数
+    wanted_spec.silence = 0;                            // 静音值
+    // wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;     // SDL声音缓冲区尺寸,单位是单声道采样点尺寸x通道数
+    // SDL声音缓冲区尺寸,单位是单声道采样点尺寸x声道数
+    wanted_spec.samples = FFMAX(SDL_AUDIO_MIN_BUFFER_SIZE, 2 << av_log2(wanted_spec.freq / SDL_AUDIO_MAX_CALLBACKS_PER_SEC));
+	wanted_spec.callback = sdl_audio_callback;          // 回调函数,若为NULL,则应使用SDL_QueueAudio()机制
+    wanted_spec.userdata = is;                          // 提供给回调函数的参数
+    if (SDL_OpenAudio(&wanted_spec, &actual_spec) < 0)
+    {
+        //av_log(NULL, AV_LOG_ERROR, "SDL_OpenAudio() failed: %s\n", SDL_GetError());
+		//is->rvc_log("SDL_OpenAudio() failed: %s.", SDL_GetError());
+		return -1;
+    }
+
+    // 2.2 根据SDL音频参数构建音频重采样参数
+    // wanted_spec是期望的参数,actual_spec是实际的参数,wanted_spec和auctual_spec都是SDL中的参数。
+    // 此处audio_param是FFmpeg中的参数,此参数应保证是SDL播放支持的参数,后面重采样要用到此参数
+    // 音频帧解码后得到的frame中的音频格式未必被SDL支持,比如frame可能是planar格式,但SDL2.0并不支持planar格式,
+    // 若将解码后的frame直接送入SDL音频缓冲区,声音将无法正常播放。所以需要先将frame重采样(转换格式)为SDL支持的模式,
+    // 然后送再写入SDL音频缓冲区
+    is->audio_param_tgt.fmt = AV_SAMPLE_FMT_S16;
+    is->audio_param_tgt.freq = actual_spec.freq;
+    is->audio_param_tgt.channel_layout = av_get_default_channel_layout(actual_spec.channels);
+    is->audio_param_tgt.channels = actual_spec.channels;
+    is->audio_param_tgt.frame_size = av_samples_get_buffer_size(NULL, actual_spec.channels, 1, is->audio_param_tgt.fmt, 1);
+    is->audio_param_tgt.bytes_per_sec = av_samples_get_buffer_size(NULL, actual_spec.channels, actual_spec.freq, is->audio_param_tgt.fmt, 1);
+    if (is->audio_param_tgt.bytes_per_sec <= 0 || is->audio_param_tgt.frame_size <= 0)
+    {
+        //av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size failed\n");
+		//is->rvc_log("av_samples_get_buffer_size failed.");
+		return -1;
+    }
+    is->audio_param_src = is->audio_param_tgt;
+    is->audio_hw_buf_size = actual_spec.size;   // SDL音频缓冲区大小
+    is->audio_frm_size = 0;
+    is->audio_cp_index = 0;
+
+    // 3. 暂停/继续音频回调处理。参数1表暂停,0表继续。
+    //     打开音频设备后默认未启动回调处理,通过调用SDL_PauseAudio(0)来启动回调处理。
+    //     这样就可以在打开音频设备后先为回调函数安全初始化数据,一切就绪后再启动音频回调。
+    //     在暂停期间,会将静音值往音频设备写。
+    SDL_PauseAudio(0);
+}
+
+// 音频处理回调函数。读队列获取音频包,解码,播放
+// 此函数被SDL按需调用,此函数不在用户主线程中,因此数据需要保护
+// \param[in]  opaque 用户在注册回调函数时指定的参数
+// \param[out] stream 音频数据缓冲区地址,将解码后的音频数据填入此缓冲区
+// \param[out] len    音频数据缓冲区大小,单位字节
+// 回调函数返回后,stream指向的音频缓冲区将变为无效
+// 双声道采样点的顺序为LRLRLR
+static void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
+{
+    player_stat_t *is = (player_stat_t *)opaque;
+    int audio_size, len1;
+
+    int64_t audio_callback_time = av_gettime_relative();
+	//av_log(NULL, AV_LOG_INFO, "sdl_audio_callback audo len is %d.\n", len);
+	//is->rvc_log("sdl_audio_callback audo len is %d.", len);
+    while (len > 0 && 0 == is->abort_request) // 输入参数len等于is->audio_hw_buf_size,是audio_open()中申请到的SDL音频缓冲区大小
+    {
+        if (is->audio_cp_index >= (int)is->audio_frm_size)
+        {
+            // 1. 从音频frame队列中取出一个frame,转换为音频设备支持的格式,返回值是重采样音频帧的大小
+            audio_size = audio_resample(is, audio_callback_time);
+			//av_log(NULL, AV_LOG_INFO, "audio_resample audio_size is %d.\n", audio_size);
+			//is->rvc_log("audio_resample audio_size is %d.", audio_size);
+			if (audio_size < 0)
+            {
+                /* if error, just output silence */
+                is->p_audio_frm = NULL;
+                is->audio_frm_size = SDL_AUDIO_MIN_BUFFER_SIZE / is->audio_param_tgt.frame_size * is->audio_param_tgt.frame_size;
+            }
+            else
+            {
+                is->audio_frm_size = audio_size;
+            }
+            is->audio_cp_index = 0;
+        }
+        // 引入is->audio_cp_index的作用:防止一帧音频数据大小超过SDL音频缓冲区大小,这样一帧数据需要经过多次拷贝
+        // 用is->audio_cp_index标识重采样帧中已拷入SDL音频缓冲区的数据位置索引,len1表示本次拷贝的数据量
+        len1 = is->audio_frm_size - is->audio_cp_index;
+        if (len1 > len)
+        {
+            len1 = len;
+        }
+        // 2. 将转换后的音频数据拷贝到音频缓冲区stream中,之后的播放就是音频设备驱动程序的工作了
+        if (is->p_audio_frm != NULL)
+        {
+            //memcpy(stream, (uint8_t *)is->p_audio_frm + is->audio_cp_index, len1);
+			SDL_memset(stream, 0, len1);
+			SDL_MixAudio(stream, (uint8_t*)is->p_audio_frm + is->audio_cp_index, len1, /*SDL_MIX_MAXVOLUME*/is->uVolume);
+        }
+        else
+        {
+            //memset(stream, 0, len1);
+			SDL_memset(stream, 0, len1);
+        }
+
+        len -= len1;
+        stream += len1;
+        is->audio_cp_index += len1;
+    }
+    // is->audio_write_buf_size是本帧中尚未拷入SDL音频缓冲区的数据量
+    is->audio_write_buf_size = is->audio_frm_size - is->audio_cp_index;
+	//is->rvc_log("audio_write_buf_size == %d.", is->audio_write_buf_size);
+    /* Let's assume the audio driver that is used by SDL has two periods. */
+    // 3. 更新时钟
+    if (!isnan(is->audio_clock))
+    {
+        // 更新音频时钟,更新时刻:每次往声卡缓冲区拷入数据后
+        // 前面audio_decode_frame中更新的is->audio_clock是以音频帧为单位,所以此处第二个参数要减去未拷贝数据量占用的时间
+        set_clock_at(&is->audio_clk,
+            is->audio_clock - (double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / is->audio_param_tgt.bytes_per_sec,
+            is->audio_clock_serial,
+            audio_callback_time / 1000000.0);
+    }
+}
+
+int open_audio(player_stat_t *is)
+{
+    open_audio_stream(is);
+    open_audio_playing(is);
+
+    return 0;
+}

+ 8 - 0
Other/libmediaplayer/audio.h

@@ -0,0 +1,8 @@
+#ifndef __AUDIO_H__
+#define __AUDIO_H__
+
+#include "player.h"
+
+int open_audio(player_stat_t *is);
+
+#endif

+ 205 - 0
Other/libmediaplayer/demux.cpp

@@ -0,0 +1,205 @@
+#include "demux.h"
+#include "packet.h"
+
+static int decode_interrupt_cb(void *ctx)
+{
+    player_stat_t *is = (player_stat_t*)ctx;
+    return is->abort_request;
+}
+
+static int demux_init(player_stat_t *is)
+{
+    AVFormatContext *p_fmt_ctx = NULL;
+    int err, i, ret;
+    int a_idx;
+    int v_idx;
+
+    p_fmt_ctx = avformat_alloc_context();
+    if (!p_fmt_ctx)
+    {
+        //is->rvc_log("Could not allocate context.");
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    // 中断回调机制。为底层I/O层提供一个处理接口,比如中止IO操作。
+    p_fmt_ctx->interrupt_callback.callback = decode_interrupt_cb;
+    p_fmt_ctx->interrupt_callback.opaque = is;
+
+    // 1. 构建AVFormatContext
+    // 1.1 打开视频文件:读取文件头,将文件格式信息存储在"fmt context"中
+    err = avformat_open_input(&p_fmt_ctx, is->filename, NULL, NULL);
+    if (err < 0)
+    {
+		//is->rvc_log("avformat_open_input() failed %d.", err);
+        ret = -1;
+        goto fail;
+    }
+    is->p_fmt_ctx = p_fmt_ctx;
+
+    // 1.2 搜索流信息:读取一段视频文件数据,尝试解码,将取到的流信息填入p_fmt_ctx->streams
+    //     ic->streams是一个指针数组,数组大小是pFormatCtx->nb_streams
+    err = avformat_find_stream_info(p_fmt_ctx, NULL);
+    if (err < 0)
+    {
+		//is->rvc_log("avformat_find_stream_info() failed %d.", err);
+        ret = -1;
+        goto fail;
+    }
+
+    // 2. 查找第一个音频流/视频流
+    a_idx = -1;
+    v_idx = -1;
+    for (i=0; i<(int)p_fmt_ctx->nb_streams; i++)
+    {
+        if ((p_fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) &&
+            (a_idx == -1))
+        {
+            a_idx = i;
+			//is->rvc_log("Find a audio stream, index %d.", a_idx);
+        }
+        if ((p_fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) &&
+            (v_idx == -1))
+        {
+            v_idx = i;
+			//is->rvc_log("Find a video stream, index %d.", v_idx);
+        }
+        if (a_idx != -1 && v_idx != -1)
+        {
+            break;
+        }
+    }
+    if (a_idx == -1 && v_idx == -1)
+    {
+		//is->rvc_log("Cann't find any audio/video stream.");
+        ret = -1;
+ fail:
+        if (p_fmt_ctx != NULL)
+        {
+            avformat_close_input(&p_fmt_ctx);
+        }
+        return ret;
+    }
+
+    is->audio_idx = a_idx;
+    is->video_idx = v_idx;
+    is->p_audio_stream = p_fmt_ctx->streams[a_idx];
+    is->p_video_stream = p_fmt_ctx->streams[v_idx];
+
+    return 0;
+}
+
+int demux_deinit()
+{
+    return 0;
+}
+
+static int stream_has_enough_packets(AVStream *st, int stream_id, packet_queue_t *queue)
+{
+    return stream_id < 0 ||
+           queue->abort_request ||
+           (st->disposition & AV_DISPOSITION_ATTACHED_PIC) ||
+           queue->nb_packets > MIN_FRAMES && (!queue->duration || av_q2d(st->time_base) * queue->duration > 1.0);
+}
+
+/* this thread gets the stream from the disk or the network */
+static int demux_thread(void *arg)
+{
+    player_stat_t *is = (player_stat_t *)arg;
+    AVFormatContext *p_fmt_ctx = is->p_fmt_ctx;
+    int ret = 0;
+    AVPacket pkt1, *pkt = &pkt1;
+
+    SDL_mutex *wait_mutex = SDL_CreateMutex();
+
+	//is->rvc_log("demux_thread running, is->abort_request == %d.", is->abort_request);
+
+    // 4. 解复用处理
+    while (0 == is->abort_request)
+    {
+		//is->rvc_log("demux_thread is->abort_request is %d.", is->abort_request);
+  //      if (is->abort_request)
+  //      {
+  //          break;
+  //      }
+
+		if (0 != ret)
+		{
+			SDL_Event event;
+			event.type = FF_QUIT_EVENT;
+			event.user.data1 = is;
+			SDL_PushEvent(&event);
+		}
+        
+        /* if the queue are full, no need to read more */
+        if (is->audio_pkt_queue.size + is->video_pkt_queue.size > MAX_QUEUE_SIZE ||
+            (stream_has_enough_packets(is->p_audio_stream, is->audio_idx, &is->audio_pkt_queue) &&
+             stream_has_enough_packets(is->p_video_stream, is->video_idx, &is->video_pkt_queue)))
+        {
+            /* wait 10 ms */
+            SDL_LockMutex(wait_mutex);
+            SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);
+            SDL_UnlockMutex(wait_mutex);
+            continue;
+        }
+
+        // 4.1 从输入文件中读取一个packet
+        ret = av_read_frame(is->p_fmt_ctx, pkt);
+        if (ret < 0)
+        {
+            if ((ret == AVERROR_EOF))// || avio_feof(ic->pb)) && !is->eof)
+            {
+                // 输入文件已读完,则往packet队列中发送NULL packet,以冲洗(flush)解码器,否则解码器中缓存的帧取不出来
+                if (is->video_idx >= 0)
+                {
+                    packet_queue_put_nullpacket(&is->video_pkt_queue, is->video_idx, is->rvc_log);
+                }
+                if (is->audio_idx >= 0)
+                {
+                    packet_queue_put_nullpacket(&is->audio_pkt_queue, is->audio_idx, is->rvc_log);
+                }
+				is->rvc_log("av_read_frame ret is AVERROR_EOF.");
+            }
+
+            SDL_LockMutex(wait_mutex);
+            SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);
+            SDL_UnlockMutex(wait_mutex);
+            continue;
+        }
+        
+        // 4.3 根据当前packet类型(音频、视频、字幕),将其存入对应的packet队列
+        if (pkt->stream_index == is->audio_idx)
+        {
+            packet_queue_put(&is->audio_pkt_queue, pkt, is->rvc_log);
+        }
+        else if (pkt->stream_index == is->video_idx)
+        {
+            packet_queue_put(&is->video_pkt_queue, pkt, is->rvc_log);
+        }
+        else
+        {
+            av_packet_unref(pkt);
+        }
+    }
+
+    SDL_DestroyMutex(wait_mutex);
+    return 0;
+}
+
+int open_demux(player_stat_t *is)
+{
+    if (demux_init(is) != 0)
+    {
+		//is->rvc_log("demux_init() failed.");
+        return -1;
+    }
+
+    is->read_tid = SDL_CreateThread(demux_thread, "demux_thread", is);
+    if (is->read_tid == NULL)
+    {
+		//is->rvc_log("SDL_CreateThread() failed: %s.", SDL_GetError());
+        return -1;
+    }
+
+    return 0;
+}

+ 8 - 0
Other/libmediaplayer/demux.h

@@ -0,0 +1,8 @@
+#ifndef __DEMUX_H__
+#define __DEMUX_H__
+
+#include "player.h"
+
+int open_demux(player_stat_t *is);
+
+#endif

+ 142 - 0
Other/libmediaplayer/frame.cpp

@@ -0,0 +1,142 @@
+#include "frame.h"
+#include "player.h"
+
+void frame_queue_unref_item(frame_t *vp)
+{
+    av_frame_unref(vp->frame);
+}
+
+int frame_queue_init(frame_queue_t *f, packet_queue_t *pktq, int max_size, int keep_last)
+{
+    int i;
+    memset(f, 0, sizeof(frame_queue_t));
+    if (!(f->mutex = SDL_CreateMutex())) {
+        av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
+        return AVERROR(ENOMEM);
+    }
+    if (!(f->cond = SDL_CreateCond())) {
+        av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
+        return AVERROR(ENOMEM);
+    }
+    f->pktq = pktq;
+    f->max_size = FFMIN(max_size, FRAME_QUEUE_SIZE);
+    f->keep_last = !!keep_last;
+    for (i = 0; i < f->max_size; i++)
+        if (!(f->queue[i].frame = av_frame_alloc()))
+            return AVERROR(ENOMEM);
+    return 0;
+}
+
+void frame_queue_destory(frame_queue_t *f)
+{
+    int i;
+    for (i = 0; i < f->max_size; i++) {
+        frame_t *vp = &f->queue[i];
+        frame_queue_unref_item(vp);
+        av_frame_free(&vp->frame);
+    }
+    SDL_DestroyMutex(f->mutex);
+    SDL_DestroyCond(f->cond);
+}
+
+void frame_queue_signal(frame_queue_t *f)
+{
+    SDL_LockMutex(f->mutex);
+    SDL_CondSignal(f->cond);
+    SDL_UnlockMutex(f->mutex);
+}
+
+frame_t *frame_queue_peek(frame_queue_t *f)
+{
+    return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
+}
+
+frame_t *frame_queue_peek_next(frame_queue_t *f)
+{
+    return &f->queue[(f->rindex + f->rindex_shown + 1) % f->max_size];
+}
+
+// 取出此帧进行播放,只读取不删除,不删除是因为此帧需要缓存下来供下一次使用。播放后,此帧变为上一帧
+frame_t *frame_queue_peek_last(frame_queue_t *f)
+{
+    return &f->queue[f->rindex];
+}
+
+// 向队列尾部申请一个可写的帧空间,若无空间可写,则等待
+frame_t *frame_queue_peek_writable(frame_queue_t *f)
+{
+    /* wait until we have space to put a new frame */
+    SDL_LockMutex(f->mutex);
+    while (f->size >= f->max_size &&
+           !f->pktq->abort_request) {
+        SDL_CondWait(f->cond, f->mutex);
+    }
+    SDL_UnlockMutex(f->mutex);
+
+    if (f->pktq->abort_request)
+        return NULL;
+
+    return &f->queue[f->windex];
+}
+
+// 从队列头部读取一帧,只读取不删除,若无帧可读则等待
+frame_t *frame_queue_peek_readable(frame_queue_t *f)
+{
+    /* wait until we have a readable a new frame */
+    SDL_LockMutex(f->mutex);
+    while (f->size - f->rindex_shown <= 0 &&
+           !f->pktq->abort_request) {
+        SDL_CondWait(f->cond, f->mutex);
+    }
+    SDL_UnlockMutex(f->mutex);
+
+    if (f->pktq->abort_request)
+        return NULL;
+
+    return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
+}
+
+// 向队列尾部压入一帧,只更新计数与写指针,因此调用此函数前应将帧数据写入队列相应位置
+void frame_queue_push(frame_queue_t *f)
+{
+    if (++f->windex == f->max_size)
+        f->windex = 0;
+    SDL_LockMutex(f->mutex);
+    f->size++;
+    SDL_CondSignal(f->cond);
+    SDL_UnlockMutex(f->mutex);
+}
+
+// 读指针(rindex)指向的帧已显示,删除此帧,注意不读取直接删除。读指针加1
+void frame_queue_next(frame_queue_t *f)
+{
+    if (f->keep_last && !f->rindex_shown) {
+        f->rindex_shown = 1;
+        return;
+    }
+    frame_queue_unref_item(&f->queue[f->rindex]);
+    if (++f->rindex == f->max_size)
+        f->rindex = 0;
+    SDL_LockMutex(f->mutex);
+    f->size--;
+    SDL_CondSignal(f->cond);
+    SDL_UnlockMutex(f->mutex);
+}
+
+// frame_queue中未显示的帧数
+/* return the number of undisplayed frames in the queue */
+int frame_queue_nb_remaining(frame_queue_t *f)
+{
+    return f->size - f->rindex_shown;
+}
+
+/* return last shown position */
+int64_t frame_queue_last_pos(frame_queue_t *f)
+{
+    frame_t *fp = &f->queue[f->rindex];
+    if (f->rindex_shown && fp->serial == f->pktq->serial)
+        return fp->pos;
+    else
+        return -1;
+}
+

+ 20 - 0
Other/libmediaplayer/frame.h

@@ -0,0 +1,20 @@
+#ifndef __FRAME_H__
+#define __FRAME_H__
+
+#include "player.h"
+
+void frame_queue_unref_item(frame_t *vp);
+int frame_queue_init(frame_queue_t *f, packet_queue_t *pktq, int max_size, int keep_last);
+void frame_queue_destory(frame_queue_t *f);
+void frame_queue_signal(frame_queue_t *f);
+frame_t *frame_queue_peek(frame_queue_t *f);
+frame_t *frame_queue_peek_next(frame_queue_t *f);
+frame_t *frame_queue_peek_last(frame_queue_t *f);
+frame_t *frame_queue_peek_writable(frame_queue_t *f);
+frame_t *frame_queue_peek_readable(frame_queue_t *f);
+void frame_queue_push(frame_queue_t *f);
+void frame_queue_next(frame_queue_t *f);
+int frame_queue_nb_remaining(frame_queue_t *f);
+int64_t frame_queue_last_pos(frame_queue_t *f);
+
+#endif

+ 199 - 0
Other/libmediaplayer/libmediaplayer.cpp

@@ -0,0 +1,199 @@
+#include "libmediaplayer.h"
+#include "SpBase.h"
+
+#include "player.h"
+
+
+void __dbg(const char* fmt, ...)
+{
+	Dbg(fmt);
+}
+
+
+class libmediaplayer_impl
+{
+private:
+	CMediaHostApi* m_pHostApi;
+	CMediaPlayer* m_Player;
+
+public:
+
+	libmediaplayer_impl(CMediaHostApi* pHostApi) {
+		m_pHostApi = pHostApi;
+		m_Player = new CMediaPlayer();
+	}
+
+	~libmediaplayer_impl()
+	{
+		m_pHostApi = NULL;
+		delete m_Player;
+		m_Player = NULL;
+	}
+
+	bool isStop() { return false; }
+
+	static void __cb_play_finished(void* user_data)
+	{
+		Dbg("callback play finished.");
+		libmediaplayer_impl* pthis = static_cast<libmediaplayer_impl*>(user_data);
+		if (NULL != pthis)
+		{
+			pthis->PlayMediaFinished();
+		}
+	}
+
+	void StartPlayVideo(const char* pVideoDir, const char* pNamePrefix = NULL, int nVideoCount = 1)
+	{
+		LOG_FUNCTION();
+		play_media_callback_t cb;
+		cb.cb_play_media_finished = &__cb_play_finished;
+		cb.user_data = this;
+
+		rvc_media_player_param_t t_param;
+		t_param.p_input_file = (char*)pNamePrefix;
+		t_param.cb = &cb;
+		t_param.picon_path = "D:/run/version/0.0.2.0/bin/rvc_media_player_64px.bmp";
+		t_param.eType = eVideo_Type;
+		t_param.playlog = __dbg;
+
+		//player_start_play_media(&t_param);
+		if (0 == m_Player->Init(&t_param))
+		{
+			m_Player->StartMediaPlay();
+		}
+	}
+
+	void StartPlayLocalAudio(const char* pAudioNames)
+	{
+		LOG_FUNCTION();
+		play_media_callback_t cb;
+		cb.cb_play_media_finished = &__cb_play_finished;
+		cb.user_data = this;
+
+		rvc_media_player_param_t t_param;
+		t_param.p_input_file = (char*)pAudioNames;
+		t_param.cb = &cb;
+		t_param.picon_path = "D:/run/version/0.0.2.0/bin/rvc_media_player_64px.bmp";
+		t_param.eType = eVideo_Type;
+		t_param.playlog = __dbg;
+
+		//player_start_play_media(&t_param);
+		if (0 == m_Player->Init(&t_param))
+		{
+			m_Player->StartMediaPlay();
+		}
+	}
+
+	void StartPlayLocalVideo(int nCfgInx, int nWndX, int nWndY, int nWndWidth, int nWndHeight)
+	{
+
+	}
+
+	void Close()
+	{
+		
+	}
+
+	bool checkIsPlay(void* curThreadId)
+	{
+		bool bret = false;
+		int64_t iThreadId = m_Player->GetMediaPlayingThreadId();
+		if (0 != iThreadId)
+		{
+			bret = true;
+			curThreadId = &iThreadId;
+		}
+
+		return bret;
+	}
+
+	bool checkIsStop()
+	{
+		return true;
+	}
+
+	//VOID PlayMedia(CWmpPlayConfig& config);
+
+	bool StopPlay()
+	{
+		m_Player->StopMediaPlay();
+		return true;
+	}
+
+	void SetVolume(int nVolume)
+	{
+		if (m_Player->GetPlayingFlag())
+		{
+			m_Player->SetVolume(nVolume);
+		}
+	}
+
+	void StartPlaySalesRecordVideo(int nWndX, int nWndY, int nWndWidth, int nWndHeight, const char* pVideoDir, const char* pNamePrefix = NULL, int nVideoCount = 1)
+	{
+
+	}
+
+	void PlayMediaFinished() 
+	{
+		m_pHostApi->Debug("PlayMediaFinished!");
+	}
+
+};
+
+
+Clibmediaplayer::Clibmediaplayer(CMediaHostApi* pHostApi)
+{
+	m_pImpl = new libmediaplayer_impl(pHostApi);
+	return;
+}
+
+Clibmediaplayer::~Clibmediaplayer()
+{
+	delete m_pImpl;
+	m_pImpl = NULL;
+}
+
+void Clibmediaplayer::PlayVideo(const char* pVideoDir, const char* pNamePrefix, int nVideoCount)
+{
+	m_pImpl->StartPlayVideo(pVideoDir, pNamePrefix, nVideoCount);
+}
+
+void Clibmediaplayer::PlayLocalAudio(const char* pAudioNames)
+{
+	m_pImpl->StartPlayLocalAudio(pAudioNames);
+}
+
+void Clibmediaplayer::PlayLocalVideo(int nCfgInx, int nWndX, int nWndY, int nWndWidth, int nWndHeight)
+{
+	m_pImpl->StartPlayLocalVideo(nCfgInx, nWndX, nWndY, nWndWidth, nWndHeight);
+}
+
+bool Clibmediaplayer::checkIsPlay(void* curThreadId)
+{
+	return m_pImpl->checkIsPlay(curThreadId);
+}
+
+bool Clibmediaplayer::checkIsStop()
+{
+	return m_pImpl->isStop();
+}
+
+VOID Clibmediaplayer::PlayMedia(CMediaPlayConfig& config)
+{
+	//m_pImpl->StartPlayMedia(config);
+}
+
+void Clibmediaplayer::Close()
+{
+	m_pImpl->StopPlay();
+}
+
+void Clibmediaplayer::SetVolume(int nVolume)
+{
+	m_pImpl->SetVolume(nVolume);
+}
+
+void Clibmediaplayer::PlaySalesRecordVideo(int nWndX, int nWndY, int nWndWidth, int nWndHeight, const char* pVideoDir, const char* pNamePrefix, int nVideoCount)
+{
+	m_pImpl->StartPlaySalesRecordVideo(nWndX, nWndY, nWndWidth, nWndHeight, pVideoDir, pNamePrefix, nVideoCount);
+}

+ 111 - 0
Other/libmediaplayer/libmediaplayer.h

@@ -0,0 +1,111 @@
+#pragma once
+#include <stdio.h>
+
+#ifdef _WIN32
+#ifdef LIBMEDIAPLAYER_EXPORTS
+#define LIBMEDIAPLAYER_API __declspec(dllexport)
+#else
+#define LIBMEDIAPLAYER_API __declspec(dllimport)
+#endif
+# elif ( defined(__GNUC__) &&  __GNUC__ >= 4 )
+#define LIBMEDIAPLAYER_API __attribute__((visibility("default")))
+#else // _WIN32
+#define LIBMEDIAPLAYER_API
+#endif // _WIN32
+
+
+#ifdef _WIN32
+#ifndef RVC_NO_VTABLE
+#define RVC_NO_VTABLE __declspec(novtable)
+#endif // !RVC_NO_VTABLE
+
+#else
+#ifndef RVC_NO_VTABLE
+#define RVC_NO_VTABLE 
+#endif // !RVC_NO_VTABLE
+#endif // _WIN32
+
+
+
+#ifndef TIME_LEN
+#define TIME_LEN      9
+#endif // !TIME_LEN
+
+#ifndef MAX_FILECOUNT
+#define MAX_FILECOUNT 32
+#endif // !MAX_FILECOUNT
+
+#ifndef MAX_PATH
+#define MAX_PATH 260
+#endif // !MAX_PATH
+
+
+enum PlayModeEnum
+{
+	UNKNOWN = -1,                               // 未知
+	SINGLE,                                     // 单一
+	SALESRECORD,                                // 客户经理
+	LOCALAUDIO,                                 // 本地音频
+	LOCALVIDEO,                                 // 本地视频
+	THRIDSALESRECORD							// 3.0客户经理双录
+};
+
+struct CMediaPlayConfig
+{
+	char strVideoRunTime_S[TIME_LEN];			// 每天允许视频播放的起始时间
+	char strVideoRunTime_E[TIME_LEN];			// 每天允许视频播放的终止时间
+
+	bool bFullScreen;                           // 是否全屏
+	bool bPrimMonitor;                          // 是否主屏显示
+	bool bSimpleMode;                           // 是否简洁模式
+	PlayModeEnum eMode;                         // 播放模式
+	int nWndX;                                  // 窗口X坐标
+	int nWndY;                                  // 窗口Y坐标
+	int nWndWidth;                              // 窗口宽度
+	int nWndHeight;                             // 窗口高度
+	int nFileCnt;		                        // 文件个数
+	int nPlayCnt;		                        // 播放次数
+	int nPlayInterval;		                    // 播放时间间隔
+	char strNamePrefix[MAX_PATH];               // 客户经理录像名前缀
+	char strRootPath[MAX_PATH];		            // 根目录
+	char strFileNames[MAX_FILECOUNT][MAX_PATH]; // 文件名数组
+
+	int nVolume;								// 音量
+};
+
+
+struct RVC_NO_VTABLE CMediaHostApi
+{
+	virtual int LoadPlayConfig(CMediaPlayConfig& config, int CfgInx) = 0;
+	virtual void Debug(const char* fmt, ...) = 0;
+};
+
+class libmediaplayer_impl; // 桥接
+
+class LIBMEDIAPLAYER_API Clibmediaplayer
+{
+public:
+	Clibmediaplayer(CMediaHostApi* pHostApi);
+	~Clibmediaplayer();
+
+	void PlayVideo(const char* pVideoDir, const char* pNamePrefix = NULL, int nVideoCount = 1);
+
+	void PlayLocalAudio(const char* pAudioNames);
+
+	void PlayLocalVideo(int nCfgInx, int nWndX, int nWndY, int nWndWidth, int nWndHeight);
+
+	void Close();
+
+	bool checkIsPlay(void* curThread);
+
+	bool checkIsStop();
+
+	void PlayMedia(CMediaPlayConfig& config);
+
+	void SetVolume(int nVolume);
+
+	void PlaySalesRecordVideo(int nWndX, int nWndY, int nWndWidth, int nWndHeight, const char* pVideoDir, const char* pNamePrefix = NULL, int nVideoCount = 1);
+
+private:
+	libmediaplayer_impl* m_pImpl;
+};

+ 146 - 0
Other/libmediaplayer/packet.cpp

@@ -0,0 +1,146 @@
+#include "packet.h"
+
+int packet_queue_init(packet_queue_t *q, play_logfun playlog)
+{
+    memset(q, 0, sizeof(packet_queue_t));
+    q->mutex = SDL_CreateMutex();
+    if (!q->mutex)
+    {
+		playlog("SDL_CreateMutex(): %s.", SDL_GetError());
+        return AVERROR(ENOMEM);
+    }
+    q->cond = SDL_CreateCond();
+    if (!q->cond)
+    {
+		playlog("SDL_CreateCond(): %s.", SDL_GetError());
+        return AVERROR(ENOMEM);
+    }
+    q->abort_request = 0;
+    return 0;
+}
+
+
+// 写队列尾部。pkt是一包还未解码的音频数据
+int packet_queue_put(packet_queue_t *q, AVPacket *pkt, play_logfun playlog)
+{
+    AVPacketList *pkt_list;
+    
+    if (av_packet_make_refcounted(pkt) < 0)
+    {
+		playlog("[pkt] is not refrence counted.");
+        return -1;
+    }
+    pkt_list = (AVPacketList*)av_malloc(sizeof(AVPacketList));
+    if (!pkt_list)
+    {
+        return -1;
+    }
+    
+    pkt_list->pkt = *pkt;
+    pkt_list->next = NULL;
+
+    SDL_LockMutex(q->mutex);
+
+    if (!q->last_pkt)   // 队列为空
+    {
+        q->first_pkt = pkt_list;
+    }
+    else
+    {
+        q->last_pkt->next = pkt_list;
+    }
+    q->last_pkt = pkt_list;
+    q->nb_packets++;
+    q->size += pkt_list->pkt.size;
+    // 发个条件变量的信号:重启等待q->cond条件变量的一个线程
+    SDL_CondSignal(q->cond);
+
+    SDL_UnlockMutex(q->mutex);
+    return 0;
+}
+
+// 读队列头部。
+int packet_queue_get(packet_queue_t *q, AVPacket *pkt, int block)
+{
+    AVPacketList *p_pkt_node;
+    int ret;
+
+    SDL_LockMutex(q->mutex);
+
+    while (1)
+    {
+        p_pkt_node = q->first_pkt;
+        if (p_pkt_node)             // 队列非空,取一个出来
+        {
+			//printf("packet queue is not empty, get one.");
+            q->first_pkt = p_pkt_node->next;
+            if (!q->first_pkt)
+            {
+                q->last_pkt = NULL;
+            }
+            q->nb_packets--;
+            q->size -= p_pkt_node->pkt.size;
+            *pkt = p_pkt_node->pkt;
+            av_free(p_pkt_node);
+            ret = 1;
+            break;
+        }
+        else if (!block)            // 队列空且阻塞标志无效,则立即退出
+        {
+			printf("packet queue is empty, and block flag is invalid, break.");
+            ret = 0;
+            break;
+        }
+        else                        // 队列空且阻塞标志有效,则等待
+        {
+			printf("packet queue is empty, and block flag is valid, continue wait.");
+            SDL_CondWait(q->cond, q->mutex);
+        }
+    }
+    SDL_UnlockMutex(q->mutex);
+    return ret;
+}
+
+int packet_queue_put_nullpacket(packet_queue_t *q, int stream_index, play_logfun playlog)
+{
+    AVPacket pkt1, *pkt = &pkt1;
+    av_init_packet(pkt);
+    pkt->data = NULL;
+    pkt->size = 0;
+    pkt->stream_index = stream_index;
+    return packet_queue_put(q, pkt, playlog);
+}
+
+void packet_queue_flush(packet_queue_t *q)
+{
+    AVPacketList *pkt, *pkt1;
+
+    SDL_LockMutex(q->mutex);
+    for (pkt = q->first_pkt; pkt; pkt = pkt1) {
+        pkt1 = pkt->next;
+        //av_packet_unref(&pkt->pkt);
+        //av_freep(&pkt);
+    }
+    q->last_pkt = NULL;
+    q->first_pkt = NULL;
+    q->nb_packets = 0;
+    q->size = 0;
+    q->duration = 0;
+    SDL_UnlockMutex(q->mutex);
+}
+
+void packet_queue_destroy(packet_queue_t *q)
+{
+    packet_queue_flush(q);
+    SDL_DestroyMutex(q->mutex);
+    SDL_DestroyCond(q->cond);
+}
+
+void packet_queue_abort(packet_queue_t *q)
+{
+    SDL_LockMutex(q->mutex);
+    q->abort_request = 1;
+    SDL_CondSignal(q->cond);
+    SDL_UnlockMutex(q->mutex);
+}
+

+ 13 - 0
Other/libmediaplayer/packet.h

@@ -0,0 +1,13 @@
+#ifndef __PACKET_H__
+#define __PACKET_H__
+
+#include "player.h"
+
+int packet_queue_init(packet_queue_t *q, play_logfun playlog);
+int packet_queue_put(packet_queue_t *q, AVPacket *pkt, play_logfun playlog);
+int packet_queue_get(packet_queue_t *q, AVPacket *pkt, int block);
+int packet_queue_put_nullpacket(packet_queue_t *q, int stream_index, play_logfun playlog);
+void packet_queue_destroy(packet_queue_t *q);
+void packet_queue_abort(packet_queue_t *q);
+
+#endif

+ 477 - 0
Other/libmediaplayer/player.cpp

@@ -0,0 +1,477 @@
+/*******************************************************************************
+ * player.c
+ *
+ * details:
+ *   A simple ffmpeg player.
+ *
+ * refrence:
+ *   ffplay.c in FFmpeg 4.1 project.
+ *******************************************************************************/
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <assert.h>
+
+#include "player.h"
+#include "frame.h"
+#include "packet.h"
+#include "demux.h"
+#include "video.h"
+#include "audio.h"
+
+static player_stat_t *player_init(rvc_media_player_param_t* pmedia_player);
+static int player_deinit(player_stat_t *is);
+
+
+// 返回值:返回上一帧的pts更新值(上一帧pts+流逝的时间)
+double get_clock(play_clock_t *c)
+{
+    if (*c->queue_serial != c->serial)
+    {
+        return NAN;
+    }
+    if (c->paused)
+    {
+        return c->pts;
+    }
+    else
+    {
+        double time = av_gettime_relative() / 1000000.0;
+        double ret = c->pts_drift + time;   // 展开得: c->pts + (time - c->last_updated)
+        return ret;
+    }
+}
+
+
+void set_clock_at(play_clock_t *c, double pts, int serial, double time)
+{
+    c->pts = pts;
+    c->last_updated = time;
+    c->pts_drift = c->pts - time;
+    c->serial = serial;
+}
+
+
+void set_clock(play_clock_t *c, double pts, int serial)
+{
+    double time = av_gettime_relative() / 1000000.0;
+    set_clock_at(c, pts, serial, time);
+}
+
+
+static void set_clock_speed(play_clock_t *c, double speed)
+{
+    set_clock(c, get_clock(c), c->serial);
+    c->speed = speed;
+}
+
+
+void init_clock(play_clock_t *c, int *queue_serial)
+{
+    c->speed = 1.0;
+    c->paused = 0;
+    c->queue_serial = queue_serial;
+    set_clock(c, NAN, -1);
+}
+
+
+static void sync_play_clock_to_slave(play_clock_t *c, play_clock_t *slave)
+{
+    double clock = get_clock(c);
+    double slave_clock = get_clock(slave);
+    if (!isnan(slave_clock) && (isnan(clock) || fabs(clock - slave_clock) > AV_NOSYNC_THRESHOLD))
+        set_clock(c, slave_clock, slave->serial);
+}
+
+
+static void do_exit(player_stat_t *is)
+{
+    if (is)
+    {
+		is->rvc_log("do_exit");
+        player_deinit(is);
+    }
+
+    if (is->sdl_video.renderer)
+        SDL_DestroyRenderer(is->sdl_video.renderer);
+    if (is->sdl_video.window)
+        SDL_DestroyWindow(is->sdl_video.window);
+    
+    avformat_network_deinit();
+
+	if (is->prvc_cb)
+	{
+		is->rvc_log("cb_play_media_finished callback.");
+		is->prvc_cb->cb_play_media_finished(is->prvc_cb->user_data);
+	}
+
+    SDL_Quit();
+
+    exit(0);
+}
+
+
+static player_stat_t *player_init(rvc_media_player_param_t* pmedia_player)
+{
+    player_stat_t *is = (player_stat_t*)av_mallocz(sizeof(player_stat_t));
+
+    if (!is || !pmedia_player)
+    {
+        return NULL;
+    }
+
+    is->filename = av_strdup(pmedia_player->p_input_file);
+	is->rvc_log = pmedia_player->playlog;
+	is->prvc_cb = pmedia_player->cb;
+	is->piconpath = av_strdup(pmedia_player->picon_path);
+	is->eMType = pmedia_player->eType;
+    if (is->filename == NULL || NULL == is->rvc_log || NULL == is->prvc_cb || NULL == is->piconpath)
+    {
+        goto fail;
+    }
+
+    /* start video display */
+    if (frame_queue_init(&is->video_frm_queue, &is->video_pkt_queue, VIDEO_PICTURE_QUEUE_SIZE, 1) < 0 ||
+        frame_queue_init(&is->audio_frm_queue, &is->audio_pkt_queue, SAMPLE_QUEUE_SIZE, 1) < 0)
+    {
+        goto fail;
+    }
+
+	/* init packet queue */
+    if (packet_queue_init(&is->video_pkt_queue, pmedia_player->playlog) < 0 ||
+        packet_queue_init(&is->audio_pkt_queue, pmedia_player->playlog) < 0)
+    {
+        goto fail;
+    }
+
+	/* put end pkt into packet queue */
+    AVPacket flush_pkt;
+    flush_pkt.data = NULL;
+    packet_queue_put(&is->video_pkt_queue, &flush_pkt, pmedia_player->playlog);
+    packet_queue_put(&is->audio_pkt_queue, &flush_pkt, pmedia_player->playlog);
+
+    if (!(is->continue_read_thread = SDL_CreateCond()))
+    {
+        //av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
+		is->rvc_log("SDL_CreateCond(): %s.", SDL_GetError());
+fail:
+        player_deinit(is);
+        goto fail;
+    }
+
+    init_clock(&is->video_clk, &is->video_pkt_queue.serial);
+    init_clock(&is->audio_clk, &is->audio_pkt_queue.serial);
+
+    is->abort_request = 0;
+
+    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
+    {
+        //av_log(NULL, AV_LOG_FATAL, "Could not initialize SDL - %s\n", SDL_GetError());
+        //av_log(NULL, AV_LOG_FATAL, "(Did you set the DISPLAY variable?)\n");
+		is->rvc_log("Could not initialize SDL - %s\n", SDL_GetError());
+		is->rvc_log("(Did you set the DISPLAY variable?)");
+        exit(1);
+    }
+
+    return is;
+}
+
+
+static int player_deinit(player_stat_t *is)
+{
+    /* XXX: use a special url_shutdown call to abort parse cleanly */
+    is->abort_request = 1;
+
+    SDL_WaitThread(is->read_tid, NULL);
+	is->rvc_log("is->read_tid finished.");
+    /* close each stream */
+    if (is->audio_idx >= 0)
+    {
+        //stream_component_close(is, is->p_audio_stream);
+    }
+    if (is->video_idx >= 0)
+    {
+        //stream_component_close(is, is->p_video_stream);
+    }
+
+    avformat_close_input(&is->p_fmt_ctx);
+
+    packet_queue_abort(&is->video_pkt_queue);
+    packet_queue_abort(&is->audio_pkt_queue);
+    packet_queue_destroy(&is->video_pkt_queue);
+    packet_queue_destroy(&is->audio_pkt_queue);
+
+    /* free all pictures */
+    frame_queue_destory(&is->video_frm_queue);
+    frame_queue_destory(&is->audio_frm_queue);
+
+    SDL_DestroyCond(is->continue_read_thread);
+    sws_freeContext(is->img_convert_ctx);
+    av_free(is->filename);
+	av_free(is->piconpath);
+    if (is->sdl_video.texture)
+    {
+        SDL_DestroyTexture(is->sdl_video.texture);
+    }
+	is->rvc_log("av_free is.");
+	is->rvc_log = NULL;
+
+    av_free(is);
+
+    return 0;
+}
+
+
+/* pause or resume the video */
+static void stream_toggle_pause(player_stat_t *is)
+{
+    if (is->paused)
+    {
+        // 这里表示当前是暂停状态,将切换到继续播放状态。在继续播放之前,先将暂停期间流逝的时间加到frame_timer中
+        is->frame_timer += av_gettime_relative() / 1000000.0 - is->video_clk.last_updated;
+        set_clock(&is->video_clk, get_clock(&is->video_clk), is->video_clk.serial);
+    }
+    is->paused = is->audio_clk.paused = is->video_clk.paused = !is->paused;
+}
+
+
+static void toggle_pause(player_stat_t *is)
+{
+    stream_toggle_pause(is);
+    is->step = 0;
+}
+
+
+CMediaPlayer::CMediaPlayer()
+{
+	m_player_stat = (player_stat_t*)av_mallocz(sizeof(player_stat_t));
+	m_uvolume = SDL_MIX_MAXVOLUME;
+	m_bplaying = false;
+
+}
+
+CMediaPlayer::~CMediaPlayer()
+{
+	player_deinit(m_player_stat);
+	m_player_stat = NULL;
+}
+
+
+int CMediaPlayer::Init(rvc_media_player_param_t* pMedia_Player)
+{
+	int iRet = -1;
+	if (NULL == pMedia_Player || NULL == m_player_stat)
+	{
+		return iRet;
+	}
+
+	m_player_stat->filename = av_strdup(pMedia_Player->p_input_file);
+	m_player_stat->rvc_log = pMedia_Player->playlog;
+	m_player_stat->prvc_cb = pMedia_Player->cb;
+	m_player_stat->piconpath = av_strdup(pMedia_Player->picon_path);
+	m_player_stat->eMType = pMedia_Player->eType;
+	m_player_stat->uVolume = m_uvolume;
+	if (m_player_stat->filename == NULL || NULL == m_player_stat->rvc_log || NULL == m_player_stat->prvc_cb || NULL == m_player_stat->piconpath)
+	{
+		player_deinit(m_player_stat);
+		m_player_stat = NULL;
+		return iRet;
+	}
+
+	/* start video display */
+	if (frame_queue_init(&m_player_stat->video_frm_queue, &m_player_stat->video_pkt_queue, VIDEO_PICTURE_QUEUE_SIZE, 1) < 0 ||
+		frame_queue_init(&m_player_stat->audio_frm_queue, &m_player_stat->audio_pkt_queue, SAMPLE_QUEUE_SIZE, 1) < 0)
+	{
+		player_deinit(m_player_stat);
+		m_player_stat = NULL;
+		return iRet;
+	}
+
+	/* init packet queue */
+	if (packet_queue_init(&m_player_stat->video_pkt_queue, pMedia_Player->playlog) < 0 ||
+		packet_queue_init(&m_player_stat->audio_pkt_queue, pMedia_Player->playlog) < 0)
+	{
+		player_deinit(m_player_stat);
+		m_player_stat = NULL;
+		return iRet;
+	}
+
+	/* put end pkt into packet queue */
+	AVPacket flush_pkt;
+	flush_pkt.data = NULL;
+	packet_queue_put(&m_player_stat->video_pkt_queue, &flush_pkt, pMedia_Player->playlog);
+	packet_queue_put(&m_player_stat->audio_pkt_queue, &flush_pkt, pMedia_Player->playlog);
+
+	if (!(m_player_stat->continue_read_thread = SDL_CreateCond()))
+	{
+		m_player_stat->rvc_log("SDL_CreateCond(): %s.", SDL_GetError());
+		player_deinit(m_player_stat);
+		m_player_stat = NULL;
+		return iRet;
+	}
+
+	init_clock(&m_player_stat->video_clk, &m_player_stat->video_pkt_queue.serial);
+	init_clock(&m_player_stat->audio_clk, &m_player_stat->audio_pkt_queue.serial);
+
+	m_player_stat->abort_request = 0;
+
+	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
+	{
+		m_player_stat->rvc_log("Could not initialize SDL - %s\n", SDL_GetError());
+		m_player_stat->rvc_log("(Did you set the DISPLAY variable?)");
+	}
+	else {
+		iRet = 0;
+	}
+
+	return iRet;
+}
+
+
+int CMediaPlayer::SetVolume(uint8_t uVolume)
+{
+	int iRet = -1;
+	
+	m_uvolume = uVolume;
+	if (NULL != m_player_stat)
+	{
+		if (GetPlayingFlag())
+		{
+			m_player_stat->uVolume = m_uvolume;
+			iRet = 0;
+		}
+		else 
+		{
+			m_player_stat->rvc_log("set audio volume failed for not in playing status.");
+		}
+	}
+
+	return iRet;
+}
+
+
+bool CMediaPlayer::GetPlayingFlag()
+{
+	return m_bplaying;
+}
+
+
+void CMediaPlayer::StartMediaPlay()
+{
+	if (0 == open_demux(m_player_stat)){
+		m_player_stat->rvc_log("open_demux function call success.");
+	}
+	else{
+		m_player_stat->rvc_log("open_demux function call failed.");
+	}
+
+	if (eVideo_Type == m_player_stat->eMType) {
+		open_video(m_player_stat);
+	}
+	open_audio(m_player_stat);
+
+	m_bplaying = true;
+
+	SDL_Event event;
+	while (1)
+	{
+		SDL_PumpEvents();
+		// SDL event队列为空,则在while循环中播放视频帧。否则从队列头部取一个event,退出当前函数,在上级函数中处理event
+		while (!SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT))
+		{
+			av_usleep(100000);
+			SDL_PumpEvents();
+		}
+
+		switch (event.type) {
+		case SDL_KEYDOWN:
+			if (event.key.keysym.sym == SDLK_ESCAPE)
+			{
+				ExitMediaPlayingThread();
+				break;
+			}
+
+			switch (event.key.keysym.sym) {
+			case SDLK_SPACE:        // 空格键:暂停
+				toggle_pause(m_player_stat);
+				break;
+			case SDL_WINDOWEVENT:
+				break;
+			default:
+				break;
+			}
+			break;
+
+		case SDL_QUIT:
+		case FF_QUIT_EVENT:
+			ExitMediaPlayingThread();
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+
+int CMediaPlayer::StopMediaPlay()
+{
+	int iRet = -1;
+
+	SDL_QuitRequested();
+
+	iRet = 0;
+
+	return iRet;
+}
+
+
+int CMediaPlayer::ExitMediaPlayingThread()
+{
+	int iRet = -1;
+
+	if (NULL == m_player_stat)
+	{
+		return iRet;
+	}
+
+	m_player_stat->rvc_log("ExitMediaPlayingThread called.");
+
+	if (m_player_stat->sdl_video.renderer)
+		SDL_DestroyRenderer(m_player_stat->sdl_video.renderer);
+	if (m_player_stat->sdl_video.window)
+		SDL_DestroyWindow(m_player_stat->sdl_video.window);
+
+	avformat_network_deinit();
+
+	if (m_player_stat->prvc_cb)
+	{
+		m_player_stat->rvc_log("cb_play_media_finished callback.");
+		m_player_stat->prvc_cb->cb_play_media_finished(m_player_stat->prvc_cb->user_data);
+	}
+
+	SDL_Quit();
+
+	player_deinit(m_player_stat);
+	m_player_stat = NULL;
+
+	m_bplaying = false;
+
+	iRet = 0;
+
+	return iRet;
+}
+
+
+int64_t CMediaPlayer::GetMediaPlayingThreadId()
+{
+	int64_t iRet = 0;
+	if (NULL != m_player_stat)
+	{
+		if (NULL != m_player_stat->read_tid)
+		{
+			iRet = SDL_GestureID(m_player_stat->read_tid);
+		}
+	}
+
+	return iRet;
+}

+ 240 - 0
Other/libmediaplayer/player.h

@@ -0,0 +1,240 @@
+#ifndef __PLAYER_H__
+#define __PLAYER_H__
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libswscale/swscale.h>
+#include <libswresample/swresample.h>
+#include <libavutil/frame.h>
+#include <libavutil/time.h>
+#include <libavutil/imgutils.h>
+#if defined(_WIN32)
+#include <SDL.h>
+#include <SDL_video.h>
+#include <SDL_render.h>
+#include <SDL_rect.h>
+#include <SDL_mutex.h>
+#else
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_video.h>
+#include <SDL2/SDL_render.h>
+#include <SDL2/SDL_rect.h>
+#include <SDL2/SDL_mutex.h>
+#endif
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+
+/* no AV sync correction is done if below the minimum AV sync threshold */
+#define AV_SYNC_THRESHOLD_MIN 0.04
+/* AV sync correction is done if above the maximum AV sync threshold */
+#define AV_SYNC_THRESHOLD_MAX 0.1
+/* If a frame duration is longer than this, it will not be duplicated to compensate AV sync */
+#define AV_SYNC_FRAMEDUP_THRESHOLD 0.1
+/* no AV correction is done if too big error */
+#define AV_NOSYNC_THRESHOLD 10.0
+
+/* polls for possible required screen refresh at least this often, should be less than 1/fps */
+#define REFRESH_RATE 0.01
+
+#define SDL_AUDIO_BUFFER_SIZE 1024
+#define MAX_AUDIO_FRAME_SIZE 192000
+
+#define MAX_QUEUE_SIZE (15 * 1024 * 1024)
+#define MIN_FRAMES 25
+
+/* Minimum SDL audio buffer size, in samples. */
+#define SDL_AUDIO_MIN_BUFFER_SIZE 512
+/* Calculate actual buffer size keeping in mind not cause too frequent audio callbacks */
+#define SDL_AUDIO_MAX_CALLBACKS_PER_SEC 30
+
+#define VIDEO_PICTURE_QUEUE_SIZE 3
+#define SUBPICTURE_QUEUE_SIZE 16
+#define SAMPLE_QUEUE_SIZE 9
+#define FRAME_QUEUE_SIZE FFMAX(SAMPLE_QUEUE_SIZE, FFMAX(VIDEO_PICTURE_QUEUE_SIZE, SUBPICTURE_QUEUE_SIZE))
+
+
+#define FF_QUIT_EVENT    (SDL_USEREVENT + 2)
+
+typedef struct {
+    double pts;                     // 当前帧(待播放)显示时间戳,播放后,当前帧变成上一帧
+    double pts_drift;               // 当前帧显示时间戳与当前系统时钟时间的差值
+    double last_updated;            // 当前时钟(如视频时钟)最后一次更新时间,也可称当前时钟时间
+    double speed;                   // 时钟速度控制,用于控制播放速度
+    int serial;                     // 播放序列,所谓播放序列就是一段连续的播放动作,一个seek操作会启动一段新的播放序列
+    int paused;                     // 暂停标志
+    int *queue_serial;              // 指向packet_serial
+}   play_clock_t;
+
+typedef struct {
+    int freq;
+    int channels;
+    int64_t channel_layout;
+    enum AVSampleFormat fmt;
+    int frame_size;
+    int bytes_per_sec;
+}   audio_param_t;
+
+typedef struct {
+    SDL_Window *window; 
+    SDL_Renderer *renderer;
+    SDL_Texture *texture;
+    SDL_Rect rect;
+}   sdl_video_t;
+
+typedef struct packet_queue_t {
+    AVPacketList *first_pkt, *last_pkt;
+    int nb_packets;                 // 队列中packet的数量
+    int size;                       // 队列所占内存空间大小
+    int64_t duration;               // 队列中所有packet总的播放时长
+    int abort_request;
+    int serial;                     // 播放序列,所谓播放序列就是一段连续的播放动作,一个seek操作会启动一段新的播放序列
+    SDL_mutex *mutex;
+    SDL_cond *cond;
+}   packet_queue_t;
+
+/* Common struct for handling all types of decoded data and allocated render buffers. */
+typedef struct {
+    AVFrame *frame;
+    int serial;
+    double pts;           /* presentation timestamp for the frame */
+    double duration;      /* estimated duration of the frame */
+    int64_t pos;                    // frame对应的packet在输入文件中的地址偏移
+    int width;
+    int height;
+    int format;
+    AVRational sar;
+    int uploaded;
+    int flip_v;
+}   frame_t;
+
+typedef struct {
+    frame_t queue[FRAME_QUEUE_SIZE];
+    int rindex;                     // 读索引。待播放时读取此帧进行播放,播放后此帧成为上一帧
+    int windex;                     // 写索引
+    int size;                       // 总帧数
+    int max_size;                   // 队列可存储最大帧数
+    int keep_last;
+    int rindex_shown;               // 当前是否有帧在显示
+    SDL_mutex *mutex;
+    SDL_cond *cond;
+    packet_queue_t *pktq;           // 指向对应的packet_queue
+}   frame_queue_t;
+
+
+typedef void (*play_logfun)(const char* fmt, ...);
+
+typedef struct play_media_callback_s {
+	void (*cb_play_media_finished)(void* user_data);
+	void* user_data;
+}play_media_callback_t;
+
+typedef enum eMediaType_s {
+	eAudio_Type,
+	eVideo_Type,
+	eImage_Type
+}eMediaType_t;
+
+typedef struct {
+    char *filename;
+    AVFormatContext *p_fmt_ctx;
+    AVStream *p_audio_stream;
+    AVStream *p_video_stream;
+    AVCodecContext *p_acodec_ctx;
+    AVCodecContext *p_vcodec_ctx;
+
+    int audio_idx;
+    int video_idx;
+    sdl_video_t sdl_video;
+
+    play_clock_t audio_clk;                   // 音频时钟
+    play_clock_t video_clk;                   // 视频时钟
+    double frame_timer;
+
+    packet_queue_t audio_pkt_queue;
+    packet_queue_t video_pkt_queue;
+    frame_queue_t audio_frm_queue;
+    frame_queue_t video_frm_queue;
+
+    struct SwsContext *img_convert_ctx;
+    struct SwrContext *audio_swr_ctx;
+    AVFrame *p_frm_yuv;
+
+    audio_param_t audio_param_src;
+    audio_param_t audio_param_tgt;
+    int audio_hw_buf_size;              // SDL音频缓冲区大小(单位字节)
+    uint8_t *p_audio_frm;               // 指向待播放的一帧音频数据,指向的数据区将被拷入SDL音频缓冲区。若经过重采样则指向audio_frm_rwr,否则指向frame中的音频
+    uint8_t *audio_frm_rwr;             // 音频重采样的输出缓冲区
+    unsigned int audio_frm_size;        // 待播放的一帧音频数据(audio_buf指向)的大小
+    unsigned int audio_frm_rwr_size;    // 申请到的音频缓冲区audio_frm_rwr的实际尺寸
+    int audio_cp_index;                 // 当前音频帧中已拷入SDL音频缓冲区的位置索引(指向第一个待拷贝字节)
+    int audio_write_buf_size;           // 当前音频帧中尚未拷入SDL音频缓冲区的数据量,audio_frm_size = audio_cp_index + audio_write_buf_size
+    double audio_clock;
+    int audio_clock_serial;
+    
+    int abort_request;
+    int paused;
+    int step;
+
+    SDL_cond *continue_read_thread;
+    SDL_Thread *read_tid;           // demux解复用线程
+	
+	play_logfun rvc_log;			// 终端日志函数
+	play_media_callback_t* prvc_cb;	// 播放状态回调函数
+	char* piconpath;				// icon图标路径
+	eMediaType_t eMType;			// 媒体类型
+	volatile uint8_t uVolume;		// 音量大小1-128
+}   player_stat_t;
+
+
+typedef struct rvc_media_player_param_s{
+	char* p_input_file;
+	eMediaType_t eType;
+	play_logfun playlog;
+	play_media_callback_t* cb;
+	char* picon_path;
+}rvc_media_player_param_t;
+
+int player_start_play_media(rvc_media_player_param_t* pmedia_player);
+bool player_stop_play_media();
+
+double get_clock(play_clock_t *c);
+void set_clock_at(play_clock_t *c, double pts, int serial, double time);
+void set_clock(play_clock_t *c, double pts, int serial);
+
+
+class CMediaPlayer
+{
+public:
+	CMediaPlayer();
+	~CMediaPlayer();
+
+	int Init(rvc_media_player_param_t* pMedia_Player);
+	int SetVolume(uint8_t uVolume);
+	bool GetPlayingFlag();
+	void StartMediaPlay();
+	int StopMediaPlay();
+	int ExitMediaPlayingThread();
+	int64_t GetMediaPlayingThreadId();
+
+private:
+	player_stat_t* m_player_stat;
+	bool m_bplaying;
+	uint8_t m_uvolume;
+};
+
+
+
+
+#endif

+ 546 - 0
Other/libmediaplayer/video.cpp

@@ -0,0 +1,546 @@
+#include "video.h"
+#include "packet.h"
+#include "frame.h"
+#include "player.h"
+
+static int queue_picture(player_stat_t *is, AVFrame *src_frame, double pts, double duration, int64_t pos)
+{
+    frame_t *vp;
+
+    if (!(vp = frame_queue_peek_writable(&is->video_frm_queue)))
+        return -1;
+
+    vp->sar = src_frame->sample_aspect_ratio;
+    vp->uploaded = 0;
+
+    vp->width = src_frame->width;
+    vp->height = src_frame->height;
+    vp->format = src_frame->format;
+
+    vp->pts = pts;
+    vp->duration = duration;
+    vp->pos = pos;
+    //vp->serial = serial;
+
+    //set_default_window_size(vp->width, vp->height, vp->sar);
+
+    // 将AVFrame拷入队列相应位置
+    av_frame_move_ref(vp->frame, src_frame);
+    // 更新队列计数及写索引
+    frame_queue_push(&is->video_frm_queue);
+    return 0;
+}
+
+
+// 从packet_queue中取一个packet,解码生成frame
+static int video_decode_frame(AVCodecContext *p_codec_ctx, packet_queue_t *p_pkt_queue, AVFrame *frame, play_logfun rvclog)
+{
+    int ret;
+    
+    while (1)
+    {
+        AVPacket pkt;
+
+        while (1)
+        {
+            // 3. 从解码器接收frame
+            // 3.1 一个视频packet含一个视频frame
+            //     解码器缓存一定数量的packet后,才有解码后的frame输出
+            //     frame输出顺序是按pts的顺序,如IBBPBBP
+            //     frame->pkt_pos变量是此frame对应的packet在视频文件中的偏移地址,值同pkt.pos
+            ret = avcodec_receive_frame(p_codec_ctx, frame);
+            if (ret < 0)
+            {
+                if (ret == AVERROR_EOF)
+                {
+                    //av_log(NULL, AV_LOG_INFO, "video avcodec_receive_frame(): the decoder has been fully flushed\n");
+					rvclog("video avcodec_receive_frame(): the decoder has been fully flushed.");
+					avcodec_flush_buffers(p_codec_ctx);
+                    return 0;
+                }
+                else if (ret == AVERROR(EAGAIN))
+                {
+                    //av_log(NULL, AV_LOG_INFO, "video avcodec_receive_frame(): output is not available in this state - "
+                    //        "user must try to send new input\n");
+
+					//rvclog("video avcodec_receive_frame(): output is not available in this state - "
+					//       "user must try to send new input");
+                    break;
+                }
+                else
+                {
+                    //av_log(NULL, AV_LOG_ERROR, "video avcodec_receive_frame(): other errors\n");
+					rvclog("video avcodec_receive_frame(): other errors.");
+					continue;
+                }
+            }
+            else
+            {
+                frame->pts = frame->best_effort_timestamp;
+                //frame->pts = frame->pkt_dts;
+
+                return 1;   // 成功解码得到一个视频帧或一个音频帧,则返回
+            }
+        }
+
+        // 1. 取出一个packet。使用pkt对应的serial赋值给d->pkt_serial
+        if (packet_queue_get(p_pkt_queue, &pkt, true) < 0)
+        {
+            return -1;
+        }
+
+        if (pkt.data == NULL)
+        {
+            // 复位解码器内部状态/刷新内部缓冲区。
+            avcodec_flush_buffers(p_codec_ctx);
+        }
+        else
+        {
+            // 2. 将packet发送给解码器
+            //    发送packet的顺序是按dts递增的顺序,如IPBBPBB
+            //    pkt.pos变量可以标识当前packet在视频文件中的地址偏移
+            if (avcodec_send_packet(p_codec_ctx, &pkt) == AVERROR(EAGAIN))
+            {
+                //av_log(NULL, AV_LOG_ERROR, "receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
+				rvclog("receive_frame and send_packet both returned EAGAIN, which is an API violation.");
+            }
+
+            av_packet_unref(&pkt);
+        }
+    }
+}
+
+// 将视频包解码得到视频帧,然后写入picture队列
+static int video_decode_thread(void *arg)
+{
+    player_stat_t *is = (player_stat_t *)arg;
+    AVFrame *p_frame = av_frame_alloc();
+    double pts;
+    double duration;
+    int ret;
+    int got_picture;
+    AVRational tb = is->p_video_stream->time_base;
+    AVRational frame_rate = av_guess_frame_rate(is->p_fmt_ctx, is->p_video_stream, NULL);
+    
+    if (p_frame == NULL)
+    {
+        //av_log(NULL, AV_LOG_ERROR, "av_frame_alloc() for p_frame failed\n");
+		is->rvc_log("av_frame_alloc() for p_frame failed.");
+		return AVERROR(ENOMEM);
+    }
+
+	is->rvc_log("video_decode_thread is->abort_request == %d.", is->abort_request);
+    while (0 == is->abort_request)
+    {
+        got_picture = video_decode_frame(is->p_vcodec_ctx, &is->video_pkt_queue, p_frame, is->rvc_log);
+        if (got_picture < 0)
+        {
+            goto exit;
+        }
+
+		AVRational tbdata{ frame_rate.den, frame_rate.num };
+		duration = (frame_rate.num && frame_rate.den ? av_q2d(tbdata) : 0);   // 当前帧播放时长
+        //duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational){frame_rate.den, frame_rate.num}) : 0);   // 当前帧播放时长
+        pts = (p_frame->pts == AV_NOPTS_VALUE) ? NAN : p_frame->pts * av_q2d(tb);   // 当前帧显示时间戳
+        ret = queue_picture(is, p_frame, pts, duration, p_frame->pkt_pos);   // 将当前帧压入frame_queue
+        av_frame_unref(p_frame);
+
+        if (ret < 0)
+        {
+            goto exit;
+        }
+    }
+
+exit:
+    av_frame_free(&p_frame);
+
+    return 0;
+}
+
+// 根据视频时钟与同步时钟(如音频时钟)的差值,校正delay值,使视频时钟追赶或等待同步时钟
+// 输入参数delay是上一帧播放时长,即上一帧播放后应延时多长时间后再播放当前帧,通过调节此值来调节当前帧播放快慢
+// 返回值delay是将输入参数delay经校正后得到的值
+static double compute_target_delay(double delay, player_stat_t *is)
+{
+    double sync_threshold, diff = 0;
+
+    /* update delay to follow master synchronisation source */
+
+    /* if video is slave, we try to correct big delays by
+       duplicating or deleting a frame */
+    // 视频时钟与同步时钟(如音频时钟)的差异,时钟值是上一帧pts值(实为:上一帧pts + 上一帧至今流逝的时间差)
+    diff = get_clock(&is->video_clk) - get_clock(&is->audio_clk);
+    // delay是上一帧播放时长:当前帧(待播放的帧)播放时间与上一帧播放时间差理论值
+    // diff是视频时钟与同步时钟的差值
+
+    /* skip or repeat frame. We take into account the
+       delay to compute the threshold. I still don't know
+       if it is the best guess */
+    // 若delay < AV_SYNC_THRESHOLD_MIN,则同步域值为AV_SYNC_THRESHOLD_MIN
+    // 若delay > AV_SYNC_THRESHOLD_MAX,则同步域值为AV_SYNC_THRESHOLD_MAX
+    // 若AV_SYNC_THRESHOLD_MIN < delay < AV_SYNC_THRESHOLD_MAX,则同步域值为delay
+    sync_threshold = FFMAX(AV_SYNC_THRESHOLD_MIN, FFMIN(AV_SYNC_THRESHOLD_MAX, delay));
+    if (!isnan(diff))
+    {
+        if (diff <= -sync_threshold)        // 视频时钟落后于同步时钟,且超过同步域值
+            delay = FFMAX(0, delay + diff); // 当前帧播放时刻落后于同步时钟(delay+diff<0)则delay=0(视频追赶,立即播放),否则delay=delay+diff
+        else if (diff >= sync_threshold && delay > AV_SYNC_FRAMEDUP_THRESHOLD)  // 视频时钟超前于同步时钟,且超过同步域值,但上一帧播放时长超长
+            delay = delay + diff;           // 仅仅校正为delay=delay+diff,主要是AV_SYNC_FRAMEDUP_THRESHOLD参数的作用
+        else if (diff >= sync_threshold)    // 视频时钟超前于同步时钟,且超过同步域值
+            delay = 2 * delay;              // 视频播放要放慢脚步,delay扩大至2倍
+    }
+
+    //av_log(NULL, AV_LOG_TRACE, "video: delay=%0.3f A-V=%f\n", delay, -diff);
+	//is->rvc_log("video: delay=%0.3f A-V=%f\n", delay, -diff);
+    return delay;
+}
+
+static double vp_duration(player_stat_t *is, frame_t *vp, frame_t *nextvp) {
+    if (vp->serial == nextvp->serial)
+    {
+        double duration = nextvp->pts - vp->pts;
+        if (isnan(duration) || duration <= 0)
+            return vp->duration;
+        else
+            return duration;
+    } else {
+        return 0.0;
+    }
+}
+
+static void update_video_pts(player_stat_t *is, double pts, int64_t pos, int serial) {
+    /* update current video pts */
+    set_clock(&is->video_clk, pts, serial);            // 更新vidclock
+    //-sync_clock_to_slave(&is->extclk, &is->vidclk);  // 将extclock同步到vidclock
+}
+
+static void video_display(player_stat_t *is)
+{
+    frame_t *vp;
+
+    vp = frame_queue_peek_last(&is->video_frm_queue);
+
+    // 图像转换:p_frm_raw->data ==> p_frm_yuv->data
+    // 将源图像中一片连续的区域经过处理后更新到目标图像对应区域,处理的图像区域必须逐行连续
+    // plane: 如YUV有Y、U、V三个plane,RGB有R、G、B三个plane
+    // slice: 图像中一片连续的行,必须是连续的,顺序由顶部到底部或由底部到顶部
+    // stride/pitch: 一行图像所占的字节数,Stride=BytesPerPixel*Width+Padding,注意对齐
+    // AVFrame.*data[]: 每个数组元素指向对应plane
+    // AVFrame.linesize[]: 每个数组元素表示对应plane中一行图像所占的字节数
+    sws_scale(is->img_convert_ctx,                      // sws context
+              (const uint8_t *const *)vp->frame->data,// src slice
+              vp->frame->linesize,                    // src stride
+              0,                                      // src slice y
+              is->p_vcodec_ctx->height,               // src slice height
+              is->p_frm_yuv->data,                    // dst planes
+              is->p_frm_yuv->linesize                 // dst strides
+             );
+    
+    // 使用新的YUV像素数据更新SDL_Rect
+    SDL_UpdateYUVTexture(is->sdl_video.texture,         // sdl texture
+                         &is->sdl_video.rect,           // sdl rect
+                         is->p_frm_yuv->data[0],        // y plane
+                         is->p_frm_yuv->linesize[0],    // y pitch
+                         is->p_frm_yuv->data[1],        // u plane
+                         is->p_frm_yuv->linesize[1],    // u pitch
+                         is->p_frm_yuv->data[2],        // v plane
+                         is->p_frm_yuv->linesize[2]     // v pitch
+                        );
+    
+    // 使用特定颜色清空当前渲染目标
+    SDL_RenderClear(is->sdl_video.renderer);
+    // 使用部分图像数据(texture)更新当前渲染目标
+    SDL_RenderCopy(is->sdl_video.renderer,              // sdl renderer
+                   is->sdl_video.texture,               // sdl texture
+                   NULL,                                // src rect, if NULL copy texture
+                   &is->sdl_video.rect                  // dst rect
+                  );
+    
+    // 执行渲染,更新屏幕显示
+    SDL_RenderPresent(is->sdl_video.renderer);
+}
+
+/* called to display each frame */
+static void video_refresh(void *opaque, double *remaining_time)
+{
+    player_stat_t *is = (player_stat_t *)opaque;
+    double time;
+    static bool first_frame = true;
+
+retry:
+    if (frame_queue_nb_remaining(&is->video_frm_queue) == 0)  // 所有帧已显示
+    {    
+        // nothing to do, no picture to display in the queue
+        return;
+    }
+
+    double last_duration, duration, delay;
+    frame_t *vp, *lastvp;
+
+    /* dequeue the picture */
+    lastvp = frame_queue_peek_last(&is->video_frm_queue);     // 上一帧:上次已显示的帧
+    vp = frame_queue_peek(&is->video_frm_queue);              // 当前帧:当前待显示的帧
+
+    // lastvp和vp不是同一播放序列(一个seek会开始一个新播放序列),将frame_timer更新为当前时间
+    if (first_frame)
+    {
+        is->frame_timer = av_gettime_relative() / 1000000.0;
+        first_frame = false;
+    }
+
+    // 暂停处理:不停播放上一帧图像
+    if (is->paused)
+        goto display;
+
+    /* compute nominal last_duration */
+    last_duration = vp_duration(is, lastvp, vp);        // 上一帧播放时长:vp->pts - lastvp->pts
+    delay = compute_target_delay(last_duration, is);    // 根据视频时钟和同步时钟的差值,计算delay值
+
+    time= av_gettime_relative()/1000000.0;
+    // 当前帧播放时刻(is->frame_timer+delay)大于当前时刻(time),表示播放时刻未到
+    if (time < is->frame_timer + delay) {
+        // 播放时刻未到,则更新刷新时间remaining_time为当前时刻到下一播放时刻的时间差
+        *remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);
+        // 播放时刻未到,则不播放,直接返回
+        return;
+    }
+
+    // 更新frame_timer值
+    is->frame_timer += delay;
+    // 校正frame_timer值:若frame_timer落后于当前系统时间太久(超过最大同步域值),则更新为当前系统时间
+    if (delay > 0 && time - is->frame_timer > AV_SYNC_THRESHOLD_MAX)
+    {
+        is->frame_timer = time;
+    }
+
+    SDL_LockMutex(is->video_frm_queue.mutex);
+    if (!isnan(vp->pts))
+    {
+        update_video_pts(is, vp->pts, vp->pos, vp->serial); // 更新视频时钟:时间戳、时钟时间
+    }
+    SDL_UnlockMutex(is->video_frm_queue.mutex);
+
+    // 是否要丢弃未能及时播放的视频帧
+    if (frame_queue_nb_remaining(&is->video_frm_queue) > 1)  // 队列中未显示帧数>1(只有一帧则不考虑丢帧)
+    {         
+        frame_t *nextvp = frame_queue_peek_next(&is->video_frm_queue);  // 下一帧:下一待显示的帧
+        duration = vp_duration(is, vp, nextvp);             // 当前帧vp播放时长 = nextvp->pts - vp->pts
+        // 当前帧vp未能及时播放,即下一帧播放时刻(is->frame_timer+duration)小于当前系统时刻(time)
+        if (time > is->frame_timer + duration)
+        {
+            frame_queue_next(&is->video_frm_queue);   // 删除上一帧已显示帧,即删除lastvp,读指针加1(从lastvp更新到vp)
+            goto retry;
+        }
+    }
+
+    // 删除当前读指针元素,读指针+1。若未丢帧,读指针从lastvp更新到vp;若有丢帧,读指针从vp更新到nextvp
+    frame_queue_next(&is->video_frm_queue);
+
+display:
+    video_display(is);                      // 取出当前帧vp(若有丢帧是nextvp)进行播放
+}
+
+static int video_playing_thread(void *arg)
+{
+    player_stat_t *is = (player_stat_t *)arg;
+    double remaining_time = 0.0;
+
+	is->rvc_log("video_playing_thread is->abort_request == %d.", is->abort_request);
+    while (0 == is->abort_request)
+    {
+        if (remaining_time > 0.0)
+        {
+            av_usleep((unsigned)(remaining_time * 1000000.0));
+        }
+        remaining_time = REFRESH_RATE;
+        // 立即显示当前帧,或延时remaining_time后再显示
+        video_refresh(is, &remaining_time);
+    }
+
+    return 0;
+}
+
+static int open_video_playing(void *arg)
+{
+    player_stat_t *is = (player_stat_t *)arg;
+    int ret;
+    int buf_size;
+    uint8_t* buffer = NULL;
+	SDL_Surface* IconSurface = NULL;
+
+    is->p_frm_yuv = av_frame_alloc();
+    if (is->p_frm_yuv == NULL)
+    {
+        is->rvc_log("av_frame_alloc() for p_frm_raw failed\n");
+        return -1;
+    }
+
+    // 为AVFrame.*data[]手工分配缓冲区,用于存储sws_scale()中目的帧视频数据
+    buf_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, 
+                                        is->p_vcodec_ctx->width, 
+                                        is->p_vcodec_ctx->height, 
+                                        1
+                                        );
+    // buffer将作为p_frm_yuv的视频数据缓冲区
+    buffer = (uint8_t *)av_malloc(buf_size);
+    if (buffer == NULL)
+    {
+		is->rvc_log("av_malloc() for buffer failed\n");
+        return -1;
+    }
+    // 使用给定参数设定p_frm_yuv->data和p_frm_yuv->linesize
+    ret = av_image_fill_arrays(is->p_frm_yuv->data,     // dst data[]
+                               is->p_frm_yuv->linesize, // dst linesize[]
+                               buffer,                  // src buffer
+                               AV_PIX_FMT_YUV420P,      // pixel format
+                               is->p_vcodec_ctx->width, // width
+                               is->p_vcodec_ctx->height,// height
+                               1                        // align
+                               );
+    if (ret < 0)
+    {
+		is->rvc_log("av_image_fill_arrays() failed %d\n", ret);
+        return -1;;
+    }
+
+    // A2. 初始化SWS context,用于后续图像转换
+    //     此处第6个参数使用的是FFmpeg中的像素格式,对比参考注释B3
+    //     FFmpeg中的像素格式AV_PIX_FMT_YUV420P对应SDL中的像素格式SDL_PIXELFORMAT_IYUV
+    //     如果解码后得到图像的不被SDL支持,不进行图像转换的话,SDL是无法正常显示图像的
+    //     如果解码后得到图像的能被SDL支持,则不必进行图像转换
+    //     这里为了编码简便,统一转换为SDL支持的格式AV_PIX_FMT_YUV420P==>SDL_PIXELFORMAT_IYUV
+    is->img_convert_ctx = sws_getContext(is->p_vcodec_ctx->width,   // src width
+                                         is->p_vcodec_ctx->height,  // src height
+                                         is->p_vcodec_ctx->pix_fmt, // src format
+                                         is->p_vcodec_ctx->width,   // dst width
+                                         is->p_vcodec_ctx->height,  // dst height
+                                         AV_PIX_FMT_YUV420P,        // dst format
+                                         SWS_BICUBIC,               // flags
+                                         NULL,                      // src filter
+                                         NULL,                      // dst filter
+                                         NULL                       // param
+                                         );
+    if (is->img_convert_ctx == NULL)
+    {
+		is->rvc_log("sws_getContext() failed\n");
+        return -1;
+    }
+
+    // SDL_Rect赋值
+    is->sdl_video.rect.x = 0;
+    is->sdl_video.rect.y = 0;
+    is->sdl_video.rect.w = is->p_vcodec_ctx->width;
+    is->sdl_video.rect.h = is->p_vcodec_ctx->height;
+
+    // 1. 创建SDL窗口,SDL 2.0支持多窗口
+    //    SDL_Window即运行程序后弹出的视频窗口,同SDL 1.x中的SDL_Surface
+    is->sdl_video.window = SDL_CreateWindow(NULL, 
+                              SDL_WINDOWPOS_UNDEFINED,// 不关心窗口X坐标
+                              SDL_WINDOWPOS_UNDEFINED,// 不关心窗口Y坐标
+                              is->sdl_video.rect.w, 
+                              is->sdl_video.rect.h,
+                              SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE
+                              );
+    if (is->sdl_video.window == NULL)
+    {  
+		is->rvc_log("SDL_CreateWindow() failed: %s.", SDL_GetError());
+        return -1;
+    }
+
+	IconSurface = SDL_LoadBMP(is->piconpath);
+	if (NULL == IconSurface)
+	{
+		is->rvc_log("SDL_LoadBMP(%s) failed: %s\n", is->piconpath, SDL_GetError());
+	}
+	else
+	{
+		SDL_SetWindowIcon(is->sdl_video.window, IconSurface);
+		SDL_FreeSurface(IconSurface);
+	}
+
+    // 2. 创建SDL_Renderer
+    //    SDL_Renderer:渲染器
+    is->sdl_video.renderer = SDL_CreateRenderer(is->sdl_video.window, -1, 0);
+    if (is->sdl_video.renderer == NULL)
+    {  
+		is->rvc_log("SDL_CreateRenderer() failed: %s\n", SDL_GetError());
+        return -1;
+    }
+
+    // 3. 创建SDL_Texture
+    //    一个SDL_Texture对应一帧YUV数据,同SDL 1.x中的SDL_Overlay
+   is->sdl_video.texture = SDL_CreateTexture(is->sdl_video.renderer, 
+                                    SDL_PIXELFORMAT_IYUV, 
+                                    SDL_TEXTUREACCESS_STREAMING,
+                                    is->sdl_video.rect.w,
+                                    is->sdl_video.rect.h
+                                    );
+    if (is->sdl_video.texture == NULL)
+    {  
+		is->rvc_log("SDL_CreateTexture() failed: %s\n", SDL_GetError());
+        return -1;
+    }
+
+    SDL_CreateThread(video_playing_thread, "video playing thread", is);
+
+    return 0;
+}
+
+static int open_video_stream(player_stat_t *is)
+{
+    AVCodecParameters* p_codec_par = NULL;
+    AVCodec* p_codec = NULL;
+    AVCodecContext* p_codec_ctx = NULL;
+    AVStream *p_stream = is->p_video_stream;
+    int ret;
+
+    // 1. 为视频流构建解码器AVCodecContext
+    // 1.1 获取解码器参数AVCodecParameters
+    p_codec_par = p_stream->codecpar;
+
+    // 1.2 获取解码器
+    p_codec = avcodec_find_decoder(p_codec_par->codec_id);
+    if (p_codec == NULL)
+    {
+		is->rvc_log("Cann't find codec!\n");
+        return -1;
+    }
+
+    // 1.3 构建解码器AVCodecContext
+    // 1.3.1 p_codec_ctx初始化:分配结构体,使用p_codec初始化相应成员为默认值
+    p_codec_ctx = avcodec_alloc_context3(p_codec);
+    if (p_codec_ctx == NULL)
+    {
+		is->rvc_log("avcodec_alloc_context3() failed\n");
+        return -1;
+    }
+    // 1.3.2 p_codec_ctx初始化:p_codec_par ==> p_codec_ctx,初始化相应成员
+    ret = avcodec_parameters_to_context(p_codec_ctx, p_codec_par);
+    if (ret < 0)
+    {
+		is->rvc_log("avcodec_parameters_to_context() failed\n");
+        return -1;
+    }
+    // 1.3.3 p_codec_ctx初始化:使用p_codec初始化p_codec_ctx,初始化完成
+    ret = avcodec_open2(p_codec_ctx, p_codec, NULL);
+    if (ret < 0)
+    {
+		is->rvc_log("avcodec_open2() failed %d\n", ret);
+        return -1;
+    }
+
+    is->p_vcodec_ctx = p_codec_ctx;
+    
+    // 2. 创建视频解码线程
+    SDL_CreateThread(video_decode_thread, "video decode thread", is);
+
+    return 0;
+}
+
+int open_video(player_stat_t *is)
+{
+    open_video_stream(is);
+    open_video_playing(is);
+
+    return 0;
+}

+ 9 - 0
Other/libmediaplayer/video.h

@@ -0,0 +1,9 @@
+#ifndef __VIDEO_H__
+#define __VIDEO_H__
+
+#include "player.h"
+
+int open_video(player_stat_t *is);
+
+#endif
+

+ 63 - 0
Other/libpictureplayer/CMakeLists.txt

@@ -0,0 +1,63 @@
+set(MODULE_NAME "libpictureplayer")
+set(MODULE_PREFIX "LIB_PICTUREPLAYER_FUNC")
+
+if(RVC_DEBUG_MODE)
+    set(SPBASE_LIB spbased)
+else()
+    set(SPBASE_LIB spbase)
+endif(RVC_DEBUG_MODE)
+
+set(${MODULE_PREFIX}_SRCS
+	CPicturePlayer.h
+	CPicturePlayer.cpp
+	libpictureplayer.h
+	libpictureplayer.cpp
+)
+
+add_library(${MODULE_NAME} SHARED ${${MODULE_PREFIX}_SRCS})
+
+if(WIN32)
+conan_cmake_run(REQUIRES SDL2/2.0.9@LR04.02_ThirdParty/testing
+BASIC_SETUP CMAKE_TARGETS
+BUILD missing)
+else(WIN32)
+conan_cmake_run(REQUIRES SDL2/2.0.9@LR04.02_ThirdParty/testing
+BASIC_SETUP CMAKE_TARGETS
+BUILD missing)
+endif(WIN32)
+
+target_include_directories(${MODULE_NAME} PRIVATE
+	${RVC_COMMON_INCLUDE_DIR}
+	${CONAN_INCLUDE_DIRS_SDL2}
+	${CONAN_RVCFRAMEWORK_ROOT}/include
+	)
+
+
+target_link_directories(${MODULE_NAME} PRIVATE
+	${CONAN_LIB_DIRS_SDL2}
+	${CONAN_LIB_DIRS_RVCFRAMEWORK}
+	)
+
+
+target_link_libraries(${MODULE_NAME} PRIVATE ${${MODULE_PREFIX}_LIBS}	PRIVATE
+	${CONAN_LIBS_SDL2}
+	${SPBASE_LIB}
+	)  
+
+
+target_compile_definitions(${MODULE_NAME} PUBLIC "LIBPICTUREPLAYER_EXPORTS")
+
+
+if(MSVC)
+	install(TARGETS ${MODULE_NAME} 
+    RUNTIME DESTINATION "${RVC_RUNTIME_PATH}" COMPONENT libraries
+    ARCHIVE DESTINATION "${RVC_LIBRARY_PATH}" COMPONENT develops EXCLUDE_FROM_ALL
+    LIBRARY DESTINATION "${RVC_LIBRARY_PATH}" COMPONENT libraries
+    )
+else()
+install(TARGETS ${MODULE_NAME} 
+    RUNTIME DESTINATION "${RVC_RUNTIME_PATH}"
+    ARCHIVE DESTINATION "${RVC_LIBRARY_PATH}"
+    LIBRARY DESTINATION "${RVC_RUNTIME_PATH}"
+    COMPONENT libraries)
+endif(MSVC)

+ 270 - 0
Other/libpictureplayer/CPicturePlayer.cpp

@@ -0,0 +1,270 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <string.h>
+
+#include "CPicturePlayer.h"
+
+
+static char* rvc_strdup(const char* strdata)
+{
+	char* strbuffer = NULL;
+	if (NULL == strdata)
+	{
+		return strbuffer;
+	}
+	
+	uint8_t ulen = strlen(strdata);
+	if (strbuffer = (char*)malloc(ulen + 1)) 
+	{
+		memset(strbuffer, 0, ulen + 1);
+		memcpy(strbuffer, strdata, ulen);
+	}
+
+	return strbuffer;
+}
+
+
+static void rvc_strfree(char* strdata)
+{
+	if (NULL != strdata)
+	{
+		free(strdata);
+		strdata = NULL;
+	}
+}
+
+
+static int picture_playing_thread(void* arg)
+{
+	int iRet = -1;
+
+	CPicturePlayer* is = (CPicturePlayer*)arg;
+
+	SDL_Surface* surface = SDL_GetWindowSurface(is->m_window);
+	for (size_t i = 0; i < is->m_nplay_cnt; i++)
+	{
+		int index = 0;
+		for (; index < is->m_nfile_cnt; index++)
+		{
+			char strimage_path[MAX_PATH] = { 0 };
+			snprintf(strimage_path, MAX_PATH, "%s%s", is->m_strroot_path, is->m_strfile_names[index]);
+			SDL_Surface* image = SDL_LoadBMP(strimage_path);
+			//SDL_Surface* image = IMG_Load(strjpgarr[i]);
+
+			if (NULL == image)
+			{
+				is->m_rvclog("SDL_LoadBMP %s failed!", strimage_path);
+				continue;
+			}
+
+			SDL_Rect src, dst;
+			dst.x = 0;
+			dst.y = 0;
+
+			src.x = 0;
+			src.y = 0;
+			src.w = 1920;
+			src.h = 1080;
+
+			SDL_BlitSurface(image, &src, surface, &dst);
+			SDL_UpdateWindowSurface(is->m_window);
+
+			SDL_FreeSurface(surface);
+			SDL_FreeSurface(image);
+			SDL_Delay(is->m_nplay_interval);
+		}
+	}
+
+	SDL_QuitRequested();
+
+	return iRet;
+}
+
+CPicturePlayer::CPicturePlayer()
+{
+	m_thid = NULL;
+	m_bplaying = false;
+	m_stricopath = NULL;
+	m_window = NULL;
+	m_rvclog = NULL;
+	m_nfile_cnt = 0;
+	m_nplay_cnt = 0;
+	m_nplay_interval = 5000;
+	memset(m_strroot_path, 0, MAX_PATH);
+	for (size_t i = 0; i < MAX_FILECOUNT; i++)
+	{
+		memset(m_strfile_names[i], 0, MAX_PATH);
+	}
+}
+
+CPicturePlayer::~CPicturePlayer()
+{
+	DeInit();
+}
+
+
+int CPicturePlayer::Init(rvc_picture_player_param_t* tparam)
+{
+	int iRet = -1;
+	if (NULL == tparam) 
+	{
+		return iRet;
+	}
+
+	m_stricopath = rvc_strdup(tparam->stricon_path);
+	m_rvclog = tparam->rvc_log;
+	m_nfile_cnt = tparam->nfile_cnt;
+	m_nplay_cnt = tparam->nplay_cnt;
+	m_nplay_interval = tparam->nplay_interval;
+
+	Uint32 uflag = SDL_WINDOW_SHOWN;
+	if (tparam->bfull_screen) {
+		uflag = SDL_WINDOW_FULLSCREEN;
+	}
+
+	//1.创建播放窗体
+	m_window = SDL_CreateWindow("",
+		tparam->icx,//SDL_WINDOWPOS_UNDEFINED,// 不关心窗口X坐标
+		tparam->icy,//SDL_WINDOWPOS_UNDEFINED,// 不关心窗口Y坐标
+		tparam->uwindow_width,
+		tparam->uwindow_height,
+		uflag
+	);
+
+	if (NULL == m_window)
+	{
+		m_rvclog("SDL_CreateWindow() failed: %s.\n", SDL_GetError());
+		return -1;
+	}
+
+	//2.设置播放器ico
+	SDL_Surface* IconSurface = SDL_LoadBMP(m_stricopath);
+	if (NULL == IconSurface)
+	{
+		m_rvclog("SDL_LoadBMP(%s) failed: %s\n", m_stricopath, SDL_GetError());
+	}
+	else
+	{
+		SDL_SetWindowIcon(m_window, IconSurface);
+		SDL_FreeSurface(IconSurface);
+	}
+
+	iRet = 0;
+
+	return iRet;
+}
+
+
+int CPicturePlayer::DeInit()
+{
+	int iRet = -1;
+	rvc_strfree(m_stricopath);
+	m_stricopath = NULL;
+
+	return iRet;
+}
+
+
+bool CPicturePlayer::StartPicPlay()
+{
+	bool bRet = false;
+	m_thid = SDL_CreateThread(picture_playing_thread, "picture playing thread", this);
+	if (NULL == m_thid)
+	{
+		m_rvclog("SDL_CreateThread() failed: %s.", SDL_GetError());
+		return bRet;
+	}
+
+	m_bplaying = true;
+	bRet = true;
+
+	SDL_Event event;
+	while (1)
+	{
+		SDL_PumpEvents();
+		// SDL event队列为空,则在while循环中播放图像。否则从队列头部取一个event,退出当前函数,在上级函数中处理event
+		while (!SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT))
+		{
+			SDL_PumpEvents();
+		}
+
+		switch (event.type) {
+		case SDL_KEYDOWN:
+			if (event.key.keysym.sym == SDLK_ESCAPE)
+			{
+				ExitPicturePlayingThread();
+				break;
+			}
+
+			switch (event.key.keysym.sym) {
+			case SDLK_SPACE:        // 空格键:暂停
+				break;
+			case SDL_WINDOWEVENT:
+				break;
+			default:
+				break;
+			}
+			break;
+
+		case SDL_QUIT:
+			ExitPicturePlayingThread();
+			break;
+		default:
+			break;
+		}
+	}
+
+	return bRet;
+}
+
+
+int CPicturePlayer::StopPicPlay()
+{
+	int iRet = -1;
+
+	SDL_QuitRequested();
+
+	iRet = 0;
+
+	return iRet;
+}
+
+
+int CPicturePlayer::ExitPicturePlayingThread()
+{
+	int iRet = -1;
+
+	if (NULL != m_window) {
+		SDL_DestroyWindow(m_window);
+	}
+
+	SDL_WaitThread(m_thid, NULL);
+	m_rvclog("m_thid finished.");
+
+	SDL_Quit();
+
+	m_bplaying = false;
+
+	iRet = 0;
+
+	return iRet;
+}
+
+bool CPicturePlayer::GetPicPlayingFlag()
+{
+	return m_bplaying;
+}
+
+
+int64_t CPicturePlayer::GetPicPlayingThreadId()
+{
+	int64_t iRet = 0;
+
+	if (NULL != m_thid)
+	{
+		iRet = SDL_GestureID(m_thid);
+	}
+	
+	return iRet;
+}

+ 91 - 0
Other/libpictureplayer/CPicturePlayer.h

@@ -0,0 +1,91 @@
+#ifndef __CPICTURE_PLAYER_H__
+#define __CPICTURE_PLAYER_H__
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+#if defined(_WIN32)
+#include <SDL.h>
+#include <SDL_video.h>
+#include <SDL_render.h>
+#include <SDL_rect.h>
+#include <SDL_mutex.h>
+//#include <SDL_image.h>
+#else
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_video.h>
+#include <SDL2/SDL_render.h>
+#include <SDL2/SDL_rect.h>
+#include <SDL2/SDL_mutex.h>
+//#include <SDL2/SDL_image.h>
+#endif
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#ifndef MAX_FILECOUNT
+#define MAX_FILECOUNT 32
+#endif // !MAX_FILECOUNT
+
+#ifndef MAX_PATH
+#define MAX_PATH 260
+#endif
+
+
+typedef void (*play_logfun)(const char* fmt, ...);
+
+typedef struct rvc_picture_player_param_s {
+	char* stricon_path;
+	int icx;
+	int icy;
+	uint8_t uwindow_width;
+	uint8_t uwindow_height;
+	play_logfun rvc_log;
+	bool bfull_screen;                           
+	bool bprim_monitor;                          
+	int nfile_cnt;		                        
+	int nplay_cnt;		                        
+	int nplay_interval;		                    
+	char strroot_path[MAX_PATH];		            
+	char strfile_names[MAX_FILECOUNT][MAX_PATH]; 
+}rvc_picture_player_param_t;
+
+
+class CPicturePlayer
+{
+public:
+	CPicturePlayer();
+	~CPicturePlayer();
+
+	int Init(rvc_picture_player_param_t* tparam);
+	int DeInit();
+	bool StartPicPlay();
+	int StopPicPlay();
+	int ExitPicturePlayingThread();
+	bool GetPicPlayingFlag();
+	int64_t GetPicPlayingThreadId();
+
+public:
+	play_logfun m_rvclog;
+	SDL_Window* m_window;
+	int m_nfile_cnt;
+	int m_nplay_cnt;
+	int m_nplay_interval;
+	char m_strroot_path[MAX_PATH];
+	char m_strfile_names[MAX_FILECOUNT][MAX_PATH];
+
+private:
+	bool m_bplaying;
+	char* m_stricopath;
+	SDL_Thread* m_thid;
+};
+
+
+#endif

+ 169 - 0
Other/libpictureplayer/libpictureplayer.cpp

@@ -0,0 +1,169 @@
+#include "libpictureplayer.h"
+#include "CPicturePlayer.h"
+#include "SpBase.h"
+
+
+void __dbg(const char* fmt, ...)
+{
+	Dbg(fmt);
+}
+
+
+class libpictureplayer_impl
+{
+private:
+	CPicPlayConfig m_stPlayConfig;
+	CPicHostApi* m_pHostApi;
+	CPicturePlayer* m_Player;
+	bool m_bIsPlay;
+
+public:
+	libpictureplayer_impl(CPicHostApi* pHostApi) {
+		m_bIsPlay = false;
+		m_pHostApi = pHostApi;
+		memset(&m_stPlayConfig, 0, sizeof(m_stPlayConfig));
+		m_Player = new CPicturePlayer();
+	}
+
+	~libpictureplayer_impl()
+	{
+		m_pHostApi = NULL;
+		delete m_Player;
+		m_Player = NULL;
+		m_bIsPlay = false;
+	}
+
+	bool isStop() 
+	{
+		return !m_bIsPlay;
+	}
+
+	bool CheckIsPlay(void* pthreadid)
+	{
+		bool bret = false;
+		int64_t iThreadId = m_Player->GetPicPlayingThreadId();
+		if (0 != iThreadId)
+		{
+			bret = true;
+			pthreadid = &iThreadId;
+		}
+	}
+
+	bool StartPlayMedia(CPicPlayConfig& config)
+	{
+		if (m_bIsPlay)
+		{
+			return true;
+		}
+
+		memcpy(&m_stPlayConfig, &config, sizeof(CPicPlayConfig));
+
+		rvc_picture_player_param_t tplayer_param;
+		tplayer_param.icx = m_stPlayConfig.nWndX;
+		tplayer_param.icy = m_stPlayConfig.nWndY;
+		tplayer_param.uwindow_width = m_stPlayConfig.nWndWidth;
+		tplayer_param.uwindow_height = m_stPlayConfig.nWndHeight;
+		tplayer_param.stricon_path = "D:/run/version/0.0.2.0/bin/rvc_media_player_64px.bmp";
+		tplayer_param.rvc_log = __dbg;
+
+		if (0 == m_Player->Init(&tplayer_param))
+		{
+			m_pHostApi->PicDebug("Init Picture Player Success.");
+			if (m_Player->StartPicPlay())
+			{
+				m_bIsPlay = true;
+				m_pHostApi->PicDebug("Start Picture Play Success.");
+			}
+		}
+
+		return true;
+
+	}
+
+	bool StartPlay(int nCfgInx, int nWndX, int nWndY, int nWndWidth, int nWndHeight)
+	{
+		m_stPlayConfig.nWndX = nWndX;
+		m_stPlayConfig.nWndY = nWndY;
+		m_stPlayConfig.nWndWidth = nWndWidth;
+		m_stPlayConfig.nWndHeight = nWndHeight;
+		int iRet = m_pHostApi->LoadPlayConfig(m_stPlayConfig, nCfgInx);
+		if (0 != iRet)
+		{
+			m_pHostApi->PicDebug("Load ImgConfiguration failed!");
+			return false;
+		}
+		else
+		{
+			m_pHostApi->PicDebug("Load ImgConfiguration succeeded while play local image!");
+			m_pHostApi->PicDebug("m_stPlayConfig.strRootPath: %s", m_stPlayConfig.strRootPath);
+		}
+
+		rvc_picture_player_param_t tplayer_param;
+		tplayer_param.icx = nWndX;
+		tplayer_param.icy = nWndY;
+		tplayer_param.uwindow_width = nWndWidth;
+		tplayer_param.uwindow_height = nWndHeight;
+		tplayer_param.stricon_path = "D:/run/version/0.0.2.0/bin/rvc_media_player_64px.bmp";
+		tplayer_param.rvc_log = __dbg;
+
+		if (0 == m_Player->Init(&tplayer_param)) 
+		{
+			m_pHostApi->PicDebug("Init Picture Player Success.");
+			if (m_Player->StartPicPlay()) 
+			{
+				m_bIsPlay = true;
+				m_pHostApi->PicDebug("Start Picture Play Success.");
+			}
+		}
+
+		return true;
+	}
+
+	bool StopPlay()
+	{
+		if (NULL != m_Player)
+		{
+			if (m_Player->GetPicPlayingFlag())
+			{
+				m_Player->StopPicPlay();
+				m_bIsPlay = false;
+			}
+		}
+		return true;
+	}
+};
+
+Clibpictureplayer::Clibpictureplayer(CPicHostApi* pHostApi)
+{
+	m_pImpl = new libpictureplayer_impl(pHostApi);
+}
+
+Clibpictureplayer::~Clibpictureplayer()
+{
+	delete m_pImpl;
+	m_pImpl = NULL;
+}
+
+void Clibpictureplayer::Play(int nCfgInx, int nWndX, int nWndY, int nWndWidth, int nWndHeight)
+{
+	m_pImpl->StartPlay(nCfgInx, nWndX, nWndY, nWndWidth, nWndHeight);
+}
+
+void Clibpictureplayer::PlayMedia(CPicPlayConfig& config)
+{
+	m_pImpl->StartPlayMedia(config);
+}
+
+bool Clibpictureplayer::checkIsStop() {
+	return m_pImpl->isStop();
+}
+
+bool Clibpictureplayer::checkIsPlay(void* pthreadid)
+{
+	return m_pImpl->CheckIsPlay(pthreadid);
+}
+
+void Clibpictureplayer::Close()
+{
+	m_pImpl->StopPlay();
+}

+ 77 - 0
Other/libpictureplayer/libpictureplayer.h

@@ -0,0 +1,77 @@
+#pragma once
+#include <stdio.h>
+
+#ifdef _WIN32
+#ifdef LIBPICTUREPLAYER_EXPORTS
+#define LIBPICTUREPLAYER_API __declspec(dllexport)
+#else
+#define LIBPICTUREPLAYER_API __declspec(dllimport)
+#endif
+# elif ( defined(__GNUC__) &&  __GNUC__ >= 4 )
+#define LIBPICTUREPLAYER_API __attribute__((visibility("default")))
+#else // _WIN32
+#define LIBPICTUREPLAYER_API
+#endif // _WIN32
+
+
+#ifdef _WIN32
+#ifndef RVC_NO_VTABLE
+#define RVC_NO_VTABLE __declspec(novtable)
+#endif // !RVC_NO_VTABLE
+
+#else
+#ifndef RVC_NO_VTABLE
+#define RVC_NO_VTABLE 
+#endif // !RVC_NO_VTABLE
+#endif // _WIN32
+
+#ifndef MAX_FILECOUNT
+#define MAX_FILECOUNT 32
+#endif // !MAX_FILECOUNT
+
+#ifndef MAX_PATH
+#define MAX_PATH 260
+#endif
+
+
+struct CPicPlayConfig
+{
+	bool bFullScreen;                           // 是否全屏
+	bool bPrimMonitor;                          // 是否主屏显示
+	int nWndX;                                  // 窗口X坐标
+	int nWndY;                                  // 窗口Y坐标
+	int nWndWidth;                              // 窗口宽度
+	int nWndHeight;                             // 窗口高度
+	int nFileCnt;		                        // 文件个数
+	int nPlayCnt;		                        // 播放次数
+	int nPlayInterval;		                    // 播放时间间隔
+	char strRootPath[MAX_PATH];		            // 根目录
+	char strFileNames[MAX_FILECOUNT][MAX_PATH]; // 文件名数组
+};
+
+
+struct RVC_NO_VTABLE CPicHostApi
+{
+	virtual void PicDebug(const char* fmt, ...) = 0;
+	virtual int LoadPlayConfig(CPicPlayConfig &config, int CfgInx = 0) = 0;
+};
+
+
+class libpictureplayer_impl;
+
+
+class  LIBPICTUREPLAYER_API Clibpictureplayer
+{
+public:
+	Clibpictureplayer(CPicHostApi* pHostApi);
+	~Clibpictureplayer();
+
+	void Play(int nCfgInx, int nWndX, int nWndY, int nWndWidth, int nWndHeight);
+	void PlayMedia(CPicPlayConfig& config);
+	bool checkIsStop();
+	bool checkIsPlay(void* pthreadid);
+	void Close();
+
+private:
+	libpictureplayer_impl* m_pImpl;
+};

+ 102 - 0
Other/libwmpplayer/CMakeLists.txt

@@ -0,0 +1,102 @@
+set(MODULE_NAME "libwmpplayer")
+set(MODULE_PREFIX "LIB_WMPPLAYER_FUNC")
+
+set(${MODULE_PREFIX}_SRCS
+	wmpplayer/src/CWMPCdromCollection.cpp
+	wmpplayer/src/CWMPClosedCaption.cpp
+	wmpplayer/src/CWMPClosedCaption2.cpp
+	wmpplayer/src/CWMPControls.cpp
+	wmpplayer/src/CWMPControls2.cpp
+	wmpplayer/src/CWMPControls3.cpp
+	wmpplayer/src/CWMPDVD.cpp
+	wmpplayer/src/CWMPError.cpp
+	wmpplayer/src/CWMPErrorItem.cpp
+	wmpplayer/src/CWMPErrorItem2.cpp
+	wmpplayer/src/CWMPMedia.cpp
+	wmpplayer/src/CWMPMedia2.cpp
+	wmpplayer/src/CWMPMedia3.cpp
+	wmpplayer/src/CWMPMediaCollection.cpp
+	wmpplayer/src/CWMPMediaCollection2.cpp
+	wmpplayer/src/CWMPMetadataPicture.cpp
+	wmpplayer/src/CWMPMetadataText.cpp
+	wmpplayer/src/CWMPPlayer.cpp
+	wmpplayer/src/CWMPPlayer2.cpp
+	wmpplayer/src/CWMPPlayer3.cpp
+	wmpplayer/src/CWMPPlayer4.cpp
+	wmpplayer/src/CWMPPlaylist.cpp
+	wmpplayer/src/CWMPPlaylistCollection.cpp
+	wmpplayer/src/CWMPSettings.cpp
+	wmpplayer/src/CWMPSettings2.cpp
+	wmpplayer/src/CWMPStringCollection2.cpp
+	wmpplayer/include/CWMPCdromCollection.h
+	wmpplayer/include/CWMPClosedCaption.h
+	wmpplayer/include/CWMPClosedCaption2.h
+	wmpplayer/include/CWMPControls.h
+	wmpplayer/include/CWMPControls2.h
+	wmpplayer/include/CWMPControls3.h
+	wmpplayer/include/CWMPDVD.h
+	wmpplayer/include/CWMPError.h
+	wmpplayer/include/CWMPErrorItem.h
+	wmpplayer/include/CWMPErrorItem2.h
+	wmpplayer/include/CWMPMedia.h
+	wmpplayer/include/CWMPMedia2.h
+	wmpplayer/include/CWMPMedia3.h
+	wmpplayer/include/CWMPMediaCollection.h
+	wmpplayer/include/CWMPMediaCollection2.h
+	wmpplayer/include/CWMPMetadataPicture.h
+	wmpplayer/include/CWMPMetadataText.h
+	wmpplayer/include/CWMPPlayer.h
+	wmpplayer/include/CWMPPlayer2.h
+	wmpplayer/include/CWMPPlayer3.h
+	wmpplayer/include/CWMPPlayer4.h
+	wmpplayer/include/CWMPPlaylist.h
+	wmpplayer/include/CWMPPlaylistCollection.h
+	wmpplayer/include/CWMPSettings.h
+	wmpplayer/include/CWMPSettings2.h
+	wmpplayer/include/CWMPStringCollection2.h
+
+	ClibwmpplayerApp.cpp
+	libwmpplayer.cpp
+	stdafx.cpp
+	libwmpplayer.def
+	res/8.ico
+	res/LibWMPPlayer.rc2
+	ClibwmpplayerApp.h
+	libwmpplayer.h
+    resource.h
+	stdafx.h
+	targetver.h
+	libwmpplayer.rc
+	CPlayerDlg.h
+	CPlayerDlg.cpp
+)
+
+add_library(${MODULE_NAME} SHARED ${${MODULE_PREFIX}_SRCS})
+
+target_include_directories(${MODULE_NAME} PRIVATE
+	${RVC_COMMON_INCLUDE_DIR}
+	"${CONAN_RVCFRAMEWORK_ROOT}/include"
+	)
+
+
+if(MSVC)
+	set(CMAKE_MFC_FLAG 2)
+	ADD_DEFINITIONS(-D_AFXDLL)
+	target_compile_definitions(${MODULE_NAME} PUBLIC "_USRDLL")
+	set_target_properties(${MODULE_NAME} PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS")
+endif(MSVC)
+
+
+if(MSVC)
+	install(TARGETS ${MODULE_NAME} 
+    RUNTIME DESTINATION "${RVC_RUNTIME_PATH}" COMPONENT libraries
+    ARCHIVE DESTINATION "${RVC_LIBRARY_PATH}" COMPONENT develops EXCLUDE_FROM_ALL
+    LIBRARY DESTINATION "${RVC_LIBRARY_PATH}" COMPONENT libraries
+    )
+else()
+install(TARGETS ${MODULE_NAME} 
+    RUNTIME DESTINATION "${RVC_RUNTIME_PATH}"
+    ARCHIVE DESTINATION "${RVC_LIBRARY_PATH}"
+    LIBRARY DESTINATION "${RVC_RUNTIME_PATH}"
+    COMPONENT libraries)
+endif(MSVC)

+ 2 - 1
Other/libwmpplayer/libwmpplayer.h

@@ -41,7 +41,8 @@ struct CWmpPlayConfig
 
 	// add by ly
 	HANDLE h_WMPCreateEvent;
-	HANDLE h_WMPPlayEndEvent;	
+	HANDLE h_WMPPlayEndEvent;
+
 	int nVolume;								// ÒôÁ¿
 };