open source

This commit is contained in:
lvfulong
2020-11-11 16:17:13 +08:00
parent 4d989f3ecb
commit bc4ca748de
2441 changed files with 623057 additions and 2 deletions
@@ -0,0 +1,31 @@
/**
@file JCAudioInterface.h
@brief
@author James
@version 1.0
@date 2014_11_26
*/
#ifndef __JCAudioInterface_H__
#define __JCAudioInterface_H__
#include <stdio.h>
/**
* @brief JCAudioInterface
* 纯接口类
*/
namespace laya
{
class JCAudioInterface
{
public:
virtual void onPlayEnd() = 0;
};
}
#endif //__JCAudioInterface_H__
//-----------------------------END FILE--------------------------------
@@ -0,0 +1,143 @@
/**
@file JCAudioWavPlayer.h
@brief
@author James
@version 1.0
@date 2014_4_22
*/
#ifndef __JCAudioWavPlayer_H__
#define __JCAudioWavPlayer_H__
//包含头文件
#ifdef WIN32
#include <xaudio2.h>
#include <al.h>
#include <alc.h>
#elif __APPLE__
#include <OpenAL/al.h>
#include <OpenAl/alc.h>
#elif ANDROID
#include <AL/al.h>
#include <AL/alc.h>
#endif
#include <stdio.h>
#include <thread>
#include "../../util/JCCommonMethod.h"
#include "JCWaveInfo.h"
#include "JCAudioInterface.h"
#include<unordered_map>
#include <vector>
namespace laya
{
class JCFileResManager;
#define OPENAL_SOURCE_NUM 10
struct OpenALSourceInfo
{
ALuint m_nOpenALSouceID; //OpenAl的ID
ALuint m_nBufferID; //bufferID
ALfloat m_vSourcePos[3]; //源声音的位置
ALfloat m_vSourceVel[3]; //源声音的速度
bool m_bPlaying; //是否为播放状态
JCAudioInterface* m_pAudio; //Audio 为了回调使用
void operator=(const OpenALSourceInfo& other)
{
m_nOpenALSouceID = other.m_nOpenALSouceID;
m_nBufferID = other.m_nBufferID;
m_bPlaying = other.m_bPlaying;
m_pAudio = other.m_pAudio;
for (int i = 0; i < 3; i++)
{
m_vSourcePos[i] = other.m_vSourcePos[i];
m_vSourceVel[i] = other.m_vSourceVel[i];
}
}
};
class JCAudioWavPlayer
{
public:
typedef std::unordered_map<std::string,JCWaveInfo*> MapWaveInfo;
typedef MapWaveInfo::iterator MapWaveInfoIter;
public:
JCAudioWavPlayer(JCFileResManager* pFileResManager);
~JCAudioWavPlayer();
void Release();
public:
OpenALSourceInfo* playAudio( JCAudioInterface* p_pAudio,const std::string& p_sSrc,bool bIsOgg );
void delAudio(JCAudioInterface* p_pAudio);
OpenALSourceInfo* playAudioFromBuffer( JCAudioInterface* p_pAudio,const char* p_pBuffer,unsigned int p_nBufferSize,int p_nRate, int p_nBitsPerSample, int nChannels);
void setVolume(OpenALSourceInfo* pOpenALInfo,float p_nVolume );
void stop(OpenALSourceInfo* pOpenALInfo);
void setAllVolume(float p_nVolume);
void stopAll();
void checkWavePlayEnd();
void pause();
void resume();
public:
/** @brief 添加资源
* @return
*/
JCWaveInfo* AddWaveInfo( const std::string& p_sUrl,unsigned char* p_pBuffer,int p_nSize,const char* p_sFilePath,void* p_pExternalMark,bool p_bIsOgg );
/** @brief 查找资源根据 ulid
* @param[in] ulID
* @return
*/
JCWaveInfo* FindWaveInfo( const std::string& p_sUrl );
/** @brief 清空所有
*/
bool ClearAllWaveInfo( void );
void autoGarbageCollection();
private:
void releaseOpenAL( OpenALSourceInfo* pOpenALInfo );
void createOpenALSource();
OpenALSourceInfo* getOpenALSource();
public:
static int s_nGarbageCollectionTime;
MapWaveInfo m_vWaveInfos; //wave的信息
public:
JCFileResManager* m_pFileResManager;
ALCdevice* m_pDevice; // 打开默认音频设备
ALCcontext* m_pContext; // context
std::vector<OpenALSourceInfo*> m_pOpenALSource; // 播放声音用的
int m_nCurrentIndex; // 记录上次的位置
bool m_bStop;
};
}
#endif //__JCAudioWavPlayer_H__
//-----------------------------END FILE--------------------------------
@@ -0,0 +1,417 @@
/**
@file JCAudioWavPlayer.cpp
@brief
@author James
@version 1.0
@date 2014_4_22
*/
#include "JCAudioWavPlayer.h"
#include "../../util/JCCommonMethod.h"
#include "../../util/Log.h"
#include "JCWaveParser.h"
#include "JCOggParser.h"
#include "../JCFileResManager.h"
//------------------------------------------------------------------------------
namespace laya
{
int JCAudioWavPlayer::s_nGarbageCollectionTime = 30000; //30Ãë
//------------------------------------------------------------------------------
JCAudioWavPlayer::JCAudioWavPlayer(JCFileResManager* pFileResManager)
{
m_pFileResManager = pFileResManager;
m_pOpenALSource.reserve(128);
m_nCurrentIndex = 0;
m_bStop=false;
m_pDevice = alcOpenDevice(NULL);
m_pContext = alcCreateContext( m_pDevice, NULL );
alcMakeContextCurrent( m_pContext );
createOpenALSource();
}
void JCAudioWavPlayer::createOpenALSource()
{
int m_nALCount = m_pOpenALSource.size();
m_pOpenALSource.resize(m_nALCount + OPENAL_SOURCE_NUM);
ALuint pOpenALBuffer[OPENAL_SOURCE_NUM];
alGenBuffers(OPENAL_SOURCE_NUM, pOpenALBuffer);
ALuint pOpenALSouceID[OPENAL_SOURCE_NUM];
alGenSources(OPENAL_SOURCE_NUM, pOpenALSouceID);
for (int i = 0; i < OPENAL_SOURCE_NUM; i++)
{
m_pOpenALSource[m_nALCount + i] = new OpenALSourceInfo;
m_pOpenALSource[m_nALCount + i]->m_nOpenALSouceID = pOpenALSouceID[i];
m_pOpenALSource[m_nALCount + i]->m_nBufferID = 0;
for (int j = 0; j < 3; j++)
{
m_pOpenALSource[m_nALCount + i]->m_vSourcePos[j] = 0;
m_pOpenALSource[m_nALCount + i]->m_vSourceVel[j] = 0;
}
m_pOpenALSource[m_nALCount + i]->m_bPlaying = false;
m_pOpenALSource[m_nALCount + i]->m_pAudio = NULL;
}
LOGI("createOpenALSource current num=%d",m_pOpenALSource.size());
}
//------------------------------------------------------------------------------
JCAudioWavPlayer::~JCAudioWavPlayer()
{
Release();
}
//------------------------------------------------------------------------------
OpenALSourceInfo* JCAudioWavPlayer::getOpenALSource()
{
int m_nALCount = m_pOpenALSource.size();
bool bFind = false;
OpenALSourceInfo* pInfo = NULL;
for( int i = 0; i < m_nALCount; i++ )
{
int n = (m_nCurrentIndex + i) % m_nALCount;
pInfo= m_pOpenALSource[n];
if( pInfo->m_bPlaying == false )
{
m_nCurrentIndex = n + 1;
bFind = true;
break;
}
}
if (bFind==false)
{
int nLastCount = m_nALCount;
createOpenALSource();
pInfo = m_pOpenALSource[nLastCount];
m_nCurrentIndex = nLastCount;
}
return pInfo;
}
//------------------------------------------------------------------------------
void JCAudioWavPlayer::checkWavePlayEnd()
{
int m_nALCount = m_pOpenALSource.size();
ALint nState = 0;
for ( int i = 0; i < m_nALCount; i++)
{
if( m_pOpenALSource[i]->m_bPlaying == true )
{
alGetSourcei(m_pOpenALSource[i]->m_nOpenALSouceID, AL_SOURCE_STATE, &nState );
if ( nState == AL_STOPPED )
{
releaseOpenAL(m_pOpenALSource[i]);
m_pOpenALSource[i]->m_bPlaying = false;
if(m_pOpenALSource[i]->m_pAudio != NULL )
{
m_pOpenALSource[i]->m_pAudio->onPlayEnd();
m_pOpenALSource[i]->m_pAudio = NULL;
}
}
}
}
}
//------------------------------------------------------------------------------
OpenALSourceInfo* JCAudioWavPlayer::playAudio( JCAudioInterface* p_pAudio,const std::string& p_sSrc, bool bIsOgg)
{
JCWaveInfo* pInfo = NULL;
MapWaveInfoIter iter = m_vWaveInfos.find( p_sSrc );
if( iter != m_vWaveInfos.end() )
{
pInfo = iter->second;
}
else
{
JCFileRes* pRes = m_pFileResManager->getRes(p_sSrc.c_str());
JCBuffer kBuffer;
if (pRes && pRes->loadFromCache(kBuffer, false))
{
pInfo = AddWaveInfo(p_sSrc, (unsigned char*)kBuffer.m_pPtr, kBuffer.m_nLen, NULL, p_pAudio, bIsOgg);
}
else
{
LOGE("JCAudioWavPlayer::playAudio load res error");
}
}
if (pInfo != NULL)
{
pInfo->m_nTouchTime = tmGetCurms();
WAVE_FORMAT* pFormat = &(pInfo->m_kFmtBlock.wavFormat);
return playAudioFromBuffer(p_pAudio, (char*)(pInfo->m_pData), pInfo->m_nRealDataSize, pFormat->dwSamplesPerSec, pFormat->wBitsPerSample, pFormat->wChannels);
}
return NULL;
}
void JCAudioWavPlayer::delAudio(JCAudioInterface* p_pAudio)
{
int m_nALCount = m_pOpenALSource.size();
for ( int i = 0; i < m_nALCount; i++){
if( m_pOpenALSource[i]->m_pAudio==p_pAudio){
m_pOpenALSource[i]->m_pAudio = NULL;
}
}
}
//------------------------------------------------------------------------------
void JCAudioWavPlayer::releaseOpenAL( OpenALSourceInfo* pOpenALInfo )
{
#ifdef __APPLE__
// some specific OpenAL implement defects existed on iOS platform
// refer to: https://github.com/cocos2d/cocos2d-x/issues/18597
ALint sourceState;
ALint bufferProcessed = 0;
alGetSourcei(pOpenALInfo->m_nOpenALSouceID, AL_SOURCE_STATE, &sourceState);
if (sourceState == AL_PLAYING)
{
alGetSourcei(pOpenALInfo->m_nOpenALSouceID, AL_BUFFERS_PROCESSED, &bufferProcessed);
while (bufferProcessed < 1/*QUEUEBUFFER_NUM*/)
{
LOGI("JCAudioWavPlayer::releaseOpenAL wait buffer");
std::this_thread::sleep_for(std::chrono::milliseconds(2));
alGetSourcei(pOpenALInfo->m_nOpenALSouceID, AL_BUFFERS_PROCESSED, &bufferProcessed);
}
alSourceUnqueueBuffers(pOpenALInfo->m_nOpenALSouceID, /*QUEUEBUFFER_NUM*/1, &pOpenALInfo->m_nBufferID);
}
#else
alSourceUnqueueBuffers( pOpenALInfo->m_nOpenALSouceID, 1, &(pOpenALInfo->m_nBufferID) );
#endif
alSourcei(pOpenALInfo->m_nOpenALSouceID, AL_BUFFER, 0);
if (pOpenALInfo->m_nBufferID != 0 && alIsBuffer(pOpenALInfo->m_nBufferID))
{
alDeleteBuffers(1, &pOpenALInfo->m_nBufferID);
pOpenALInfo->m_nBufferID = 0;
}
}
//------------------------------------------------------------------------------
OpenALSourceInfo* JCAudioWavPlayer::playAudioFromBuffer( JCAudioInterface* p_pAudio,const char* p_pBuffer,unsigned int p_nBufferSize,
int p_nRate,int nBitsPerSample, int nChannels)
{
OpenALSourceInfo* pOpenALInfo = getOpenALSource();
alSourceStop( pOpenALInfo->m_nOpenALSouceID );
releaseOpenAL( pOpenALInfo );
alSourcef ( pOpenALInfo->m_nOpenALSouceID, AL_PITCH, 1.0 );
alSourcef ( pOpenALInfo->m_nOpenALSouceID, AL_GAIN, 1.0 );
alSourcei ( pOpenALInfo->m_nOpenALSouceID, AL_LOOPING, 0 );
//ÏÈÆÁ±Î
//alSourcefv( pOpenALInfo->m_nOpenALSouceID, AL_VELOCITY, pOpenALInfo->m_vSourceVel );
//alSourcefv( pOpenALInfo->m_nOpenALSouceID, AL_POSITION, pOpenALInfo->m_vSourcePos );
//bind buffer
ALuint nBufferID = 0;
alGenBuffers( 1,&nBufferID );
int nFormat = AL_FORMAT_MONO16;
switch (nChannels)
{
case 1:
switch (nBitsPerSample)
{
case 8:
nFormat = AL_FORMAT_MONO8;
break;
case 16:
nFormat = AL_FORMAT_MONO16;
break;
default:
LOGE("JCAudioWavPlayer::playAudioFromBuffer sound bitsPerSample error,only support 8 or 16");
nFormat = AL_FORMAT_MONO16;
break;
}
break;
case 2:
switch (nBitsPerSample)
{
case 8:
nFormat = AL_FORMAT_STEREO8;
break;
case 16:
nFormat = AL_FORMAT_STEREO16;
break;
default:
LOGE("JCAudioWavPlayer::playAudioFromBuffer sound bitsPerSample error,only support 8 or 16");
nFormat = AL_FORMAT_STEREO16;
break;
}
break;
default:
LOGE("JCAudioWavPlayer::playAudioFromBuffer sound channel error");
switch (nBitsPerSample)
{
case 8:
nFormat = AL_FORMAT_MONO8;
break;
case 16:
nFormat = AL_FORMAT_MONO16;
break;
default:
LOGE("JCAudioWavPlayer::playAudioFromBuffer sound bitsPerSample error,only support 8 or 16");
nFormat = AL_FORMAT_MONO16;
break;
}
break;
}
alBufferData(nBufferID, nFormat, p_pBuffer, p_nBufferSize, p_nRate);
alSourceQueueBuffers( pOpenALInfo->m_nOpenALSouceID, 1, &nBufferID );
pOpenALInfo->m_nBufferID = nBufferID;
//play
alSourcePlay( pOpenALInfo->m_nOpenALSouceID );
//±£ÁôÐÅÏ¢
pOpenALInfo->m_pAudio = p_pAudio;
pOpenALInfo->m_bPlaying = true;
return pOpenALInfo;
}
//------------------------------------------------------------------------------
void JCAudioWavPlayer::stopAll()
{
int m_nALCount = m_pOpenALSource.size();
for (int i = 0; i < m_nALCount; i++)
{
if( m_pOpenALSource[i]->m_bPlaying == true )
{
alSourceStop(m_pOpenALSource[i]->m_nOpenALSouceID );
releaseOpenAL(m_pOpenALSource[i]);
m_pOpenALSource[i]->m_bPlaying = false;
}
}
}
void JCAudioWavPlayer::pause()
{
int m_nALCount = m_pOpenALSource.size();
for (int i = 0; i < m_nALCount; i++)
{
alSourcePause(m_pOpenALSource[i]->m_nOpenALSouceID);
}
}
void JCAudioWavPlayer::resume()
{
int m_nALCount = m_pOpenALSource.size();
for (int i = 0; i < m_nALCount; i++)
{
alSourcePlay(m_pOpenALSource[i]->m_nOpenALSouceID);
}
}
//------------------------------------------------------------------------------
void JCAudioWavPlayer::setAllVolume( float p_nVolume )
{
int m_nALCount = m_pOpenALSource.size();
for ( int i = 0; i < m_nALCount; i++ )
{
alSourcef ( m_pOpenALSource[i]->m_nOpenALSouceID, AL_GAIN, p_nVolume);
}
}
//------------------------------------------------------------------------------
void JCAudioWavPlayer::stop(OpenALSourceInfo* pOpenALInfo)
{
if (pOpenALInfo->m_bPlaying == true)
{
alSourceStop(pOpenALInfo->m_nOpenALSouceID);
releaseOpenAL(pOpenALInfo);
pOpenALInfo->m_bPlaying = false;
pOpenALInfo->m_pAudio = NULL;
}
}
//------------------------------------------------------------------------------
void JCAudioWavPlayer::setVolume(OpenALSourceInfo* pOpenALInfo,float p_nVolume)
{
alSourcef(pOpenALInfo->m_nOpenALSouceID, AL_GAIN, p_nVolume);
}
//------------------------------------------------------------------------------
void JCAudioWavPlayer::Release()
{
for (int i = 0; i < m_pOpenALSource.size(); i++)
{
if (m_pOpenALSource[i]->m_nBufferID != 0 && alIsBuffer(m_pOpenALSource[i]->m_nBufferID))
{
alDeleteBuffers(1, &m_pOpenALSource[i]->m_nBufferID);
m_pOpenALSource[i]->m_nBufferID = 0;
}
if (m_pOpenALSource[i]->m_nOpenALSouceID != 0 && alIsSource(m_pOpenALSource[i]->m_nOpenALSouceID))
{
alDeleteSources(1, &m_pOpenALSource[i]->m_nOpenALSouceID);
m_pOpenALSource[i]->m_nOpenALSouceID = 0;
}
delete m_pOpenALSource[i];
}
if (m_pContext != NULL)
{
alcDestroyContext( m_pContext );
m_pContext=NULL;
}
if ( m_pDevice !=NULL )
{
alcCloseDevice(m_pDevice);
m_pDevice=NULL;
}
m_bStop=true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
JCWaveInfo* JCAudioWavPlayer::AddWaveInfo( const std::string& p_sUrl,unsigned char* p_pBuffer,int p_nSize,const char* p_sFilePath,void* p_pExternalMark,bool p_bIsOgg )
{
JCWaveInfo* pInfo = FindWaveInfo( p_sUrl );
if( pInfo == NULL )
{
if( p_bIsOgg == false )
{
pInfo = JCWaveParser::GetInstance()->GetWaveInfoFromBuffer( p_pBuffer,p_nSize );
}
else
{
pInfo = JCOggParser::GetInstance()->GetWaveInfo( p_sFilePath,p_pBuffer,p_nSize );
}
if( pInfo != NULL )
{
pInfo->m_sUrl = p_sUrl;
pInfo->m_nTouchTime = tmGetCurms();
pInfo->m_pExternalMark = p_pExternalMark;
m_vWaveInfos[ p_sUrl ] = pInfo;
}
else
{
LOGE( "JCAudioWavPlayer::AddWaveInfo wave paser err" );
}
}
return pInfo;
}
void JCAudioWavPlayer::autoGarbageCollection()
{
if (m_vWaveInfos.size() <= 0)return;
double nCurrentTime = tmGetCurms();
for (MapWaveInfoIter iter = m_vWaveInfos.begin(); iter != m_vWaveInfos.end();)
{
JCWaveInfo* pInfo = iter->second;
if (pInfo && (nCurrentTime - pInfo->m_nTouchTime > s_nGarbageCollectionTime ) )
{
delete pInfo;
pInfo = NULL;
iter = m_vWaveInfos.erase(iter);
}
else
{
iter++;
}
}
}
//------------------------------------------------------------------------------
JCWaveInfo* JCAudioWavPlayer::FindWaveInfo( const std::string& p_sUrl )
{
MapWaveInfoIter iter = m_vWaveInfos.find( p_sUrl );
if( iter != m_vWaveInfos.end() )
{
return iter->second;
}
return NULL;
}
//------------------------------------------------------------------------------
bool JCAudioWavPlayer::ClearAllWaveInfo( void )
{
for( MapWaveInfoIter iter = m_vWaveInfos.begin(); iter != m_vWaveInfos.end(); iter++ )
{
JCWaveInfo* pInfo = NULL;
pInfo = iter->second;
if( pInfo != NULL )
{
delete pInfo;
pInfo = NULL;
}
}
m_vWaveInfos.clear();
return true;
}
//------------------------------------------------------------------------------
}
//-----------------------------END FILE--------------------------------
@@ -0,0 +1,50 @@
/**
@file JCMp3Interface.h
@brief
@author James
@version 1.0
@date 2014_11_26
*/
#ifndef __JCMp3Interface_H__
#define __JCMp3Interface_H__
#include "JCAudioInterface.h"
#include <stdio.h>
/**
* @brief JCMp3Interface
* 纯接口类
*/
namespace laya
{
class JCMp3Interface
{
public:
virtual void play( const char* p_sUrl,int p_nTimes,float nCurrentTime,JCAudioInterface* p_pJSAudio )=0;
virtual void delAudio( JCAudioInterface* p_pJSAudio )=0;
virtual void pause()=0;
virtual void stop()=0;
virtual void resume()=0;
virtual void setVolume( float p_nVolume )=0;
virtual void setMute(bool p_bMute)=0;
virtual void onPlayEnd()=0;
virtual void setCurrentTime(double nCurrentTime) = 0;
virtual double getCurrentTime() = 0;
virtual double getDuration() = 0;
};
}
#endif //__JCMp3Interface_H__
//-----------------------------END FILE--------------------------------
@@ -0,0 +1,144 @@
/**
@file JCOggParser.cpp
@brief
@author James
@version 1.0
@date 2014_11_26
*/
//包含头文件
#include "JCOggParser.h"
#include "../../util/Log.h"
#include <vorbis/vorbisfile.h>
#include <ogg/ogg.h>
#include <vector>
#pragma warning (disable: 4996)
namespace laya
{
#define READ_OGG_BUFFER_TEMP_SIZE 44100 //读取ogg的时候,临时缓冲区大小
//------------------------------------------------------------------------------
JCOggParser* JCOggParser::ms_pOggParser = NULL;
//------------------------------------------------------------------------------
JCOggParser::JCOggParser()
{
m_nBufferSize = READ_OGG_BUFFER_TEMP_SIZE;
m_nCurBufferPos = 0;
m_pBuffer = new char[m_nBufferSize];
}
//------------------------------------------------------------------------------
JCOggParser::~JCOggParser()
{
if( m_pBuffer != NULL )
{
delete[] m_pBuffer;
m_pBuffer = NULL;
}
}
//------------------------------------------------------------------------------
JCOggParser* JCOggParser::GetInstance( void )
{
if( ms_pOggParser == NULL )
{
ms_pOggParser = new JCOggParser();
}
return ms_pOggParser;
}
//-----------------------------------------------------------------------------
void JCOggParser::DelInstance( void )
{
}
//-----------------------------------------------------------------------------
bool JCOggParser::copyBuffer( char* p_pBuffer,int p_nSize )
{
if( p_pBuffer == NULL || p_nSize <=0 ) return false;
if( m_nCurBufferPos + p_nSize > m_nBufferSize )
{
int nElargeSize = (p_nSize<=READ_OGG_BUFFER_TEMP_SIZE) ? READ_OGG_BUFFER_TEMP_SIZE : p_nSize;
char* pNewBuffer = new char[m_nBufferSize + nElargeSize];
memcpy( pNewBuffer,m_pBuffer,m_nBufferSize );
m_nBufferSize += nElargeSize;
delete m_pBuffer;
m_pBuffer = pNewBuffer;
}
memcpy( m_pBuffer+m_nCurBufferPos,p_pBuffer,p_nSize );
m_nCurBufferPos += p_nSize;
return true;
}
//------------------------------------------------------------------------------
JCWaveInfo* JCOggParser::GetWaveInfo( const char* p_sFileName,unsigned char* p_sBuffer, int p_nBufferSize )
{
m_nCurBufferPos = 0;
OggVorbis_File pOggStream;
vorbis_info* pVorbisInfo = NULL;
int nResult = -1;
if( p_sBuffer != NULL && p_nBufferSize > 0 )
{
nResult = ov_open_callbacks( NULL, &pOggStream, (const char*)p_sBuffer, p_nBufferSize, OV_CALLBACKS_DEFAULT );
}
else if( p_sFileName != NULL )
{
FILE* fp = NULL;
fp = fopen( p_sFileName, "rb" );
if( fp )
{
nResult = ov_open_callbacks( (void*)fp, &pOggStream, NULL, 0, OV_CALLBACKS_DEFAULT );
}
fclose( fp );
}
if( nResult >= 0 )
{
pVorbisInfo = ov_info( &pOggStream, -1 );
char pBufferData[ READ_OGG_BUFFER_TEMP_SIZE ];//解码时候的缓冲区
int nSize = 0;
int nPortion = 0;
int nResult1 = 0;
while( true )
{
nResult1 = ov_read( &pOggStream, pBufferData, sizeof(pBufferData), 0, 2, 1, &nPortion );
if( nResult1 > 0 )
{
nSize += nResult1;
copyBuffer( pBufferData,nResult1 );
}
else if( nResult1 == 0 )
{
break;
}
else
{
LOGE("JCOggParser::GetWaveInfo error 1");
return NULL;
}
}
if( nSize == 0)
{
LOGE("JCOggParser::GetWaveInfo error 2");
return NULL;
}
JCWaveInfo* pWaveInfo = new JCWaveInfo();
pWaveInfo->m_nRealDataSize = nSize;
pWaveInfo->m_pData = new unsigned char[nSize];
if( nSize == m_nCurBufferPos )
{
memcpy( pWaveInfo->m_pData,m_pBuffer,nSize*sizeof(char));
}
else
{
LOGE("JCOggParser::GetWaveInfo error 3");
return NULL;
}
pWaveInfo->m_kFmtBlock.wavFormat.dwSamplesPerSec = pVorbisInfo->rate;
pWaveInfo->m_kFmtBlock.wavFormat.wChannels = pVorbisInfo->channels;
//TODO 没有找到ogg的这个参数,似乎目前只支持16bit
pWaveInfo->m_kFmtBlock.wavFormat.wBitsPerSample = 16;
ov_clear(&pOggStream);
return pWaveInfo;
}
LOGE("JCOggParser::GetWaveInfo error 0");
return NULL;
}
//------------------------------------------------------------------------------
}
//-----------------------------END FILE--------------------------------
@@ -0,0 +1,67 @@
/**
@file JCOggParser.h
@brief
@author James
@version 1.0
@date 2014_11_26
*/
#ifndef __JCOggParser_H__
#define __JCOggParser_H__
#include "JCWaveInfo.h"
/**
* @brief 读取wav文件 目前只支持
* 单声道
* 16位
* 采样级别 8000
* 音频格式 PCM
*/
namespace laya
{
class JCOggParser
{
public:
/** @brief构造函数
*/
JCOggParser( void );
/** @brief析构函数
*/
~JCOggParser( void );
/*
* getInstace()
*/
static JCOggParser* GetInstance( void );
static void DelInstance( void );
/*
* 从文件中读取wave数据
*/
JCWaveInfo* GetWaveInfo( const char* p_sFileName,unsigned char* p_sBuffer, int p_nBufferSize );
//拷贝buffer
bool copyBuffer( char* p_sBuffer,int p_nSize );
protected:
static JCOggParser* ms_pOggParser; //静态的this指针
char* m_pBuffer; //临时的buffer
int m_nBufferSize; //buffer大小
int m_nCurBufferPos; //当前buffer的位置
};
}
#endif //__JCOggParser_H__
//-----------------------------END FILE--------------------------------
@@ -0,0 +1,208 @@
/**
@file JCWaveInfo.cpp
@brief
@author James
@version 1.0
@date 2012_11_17
*/
#include "JCWaveInfo.h"
#include "../../util/Log.h"
#pragma warning (disable: 4996)
namespace laya
{
//------------------------------------------------------------------------------
JCWaveInfo::JCWaveInfo( void )
{
m_nTouchTime = 0;
m_pExternalMark = NULL;
m_pData = NULL;
m_nCurPos = 0;
}
//------------------------------------------------------------------------------
JCWaveInfo::~JCWaveInfo( void )
{
//赋值为NULL,就可以了,不需要删除
if( m_pExternalMark != NULL )
{
m_pExternalMark = NULL;
}
if( m_pData != NULL )
{
delete[] m_pData;
m_pData = NULL;
}
}
//------------------------------------------------------------------------------
bool JCWaveInfo::LoadData( const char* p_sFileName )
{
FILE *fp = fopen( p_sFileName,"rb" );
if( fp == NULL )
{
LOGE("JCWaveInfo::LoadData Can't open file %s",p_sFileName );
return false;
}
fseek( fp,0,SEEK_END );
int nSize=ftell(fp);
fseek(fp,0L,SEEK_SET);
unsigned char* sBuffer = new unsigned char[nSize];
fread( sBuffer,1,nSize,fp );
fclose(fp);
m_nCurPos = 0;
LoadData( sBuffer,nSize );
if (sBuffer)
{
delete[] sBuffer;
sBuffer = NULL;
}
return true;
}
//------------------------------------------------------------------------------
bool JCWaveInfo::LoadData( unsigned char* p_sBuffer,int p_nBufferSize )
{
if( p_sBuffer == NULL )
{
LOGE("JCWaveInfo::LoadData buffer == null" );
return false;
}
//读取 RIFF 头
{
read( &m_kRiff, sizeof(RIFF_HEADER), p_sBuffer );
if( memcmp( m_kRiff.szRiffID, "RIFF", 4 ) != 0 || memcmp( m_kRiff.szRiffFormat, "WAVE", 4 ) != 0 )
{
LOGE("JCWaveInfo::LoadData No a vaild wave file!\n");
return false;
}
m_nCurPos += sizeof(RIFF_HEADER);
}
//读取格式
{
read( &m_kFmtBlock.szFmtID, 4, p_sBuffer );
m_nCurPos += 4;
read( &m_kFmtBlock.dwFmtSize, 4, p_sBuffer );
m_nCurPos += 4;
if( memcmp(m_kFmtBlock.szFmtID, "fmt ", 4) !=0 )
{
LOGE("JCWaveInfo::LoadData we only support follow format" );
return false;
}
if( m_kFmtBlock.dwFmtSize != 18 && m_kFmtBlock.dwFmtSize != 16 )
{
LOGE("JCWaveInfo::LoadData we only support Format: linear PCM size = %d",m_kFmtBlock.dwFmtSize );
return false;
}
if( m_kFmtBlock.dwFmtSize == 16 )
{
read(&m_kFmtBlock.wavFormat, 16, p_sBuffer );
m_nCurPos += 16;
}
else if( m_kFmtBlock.dwFmtSize == 18 )
{
read(&m_kFmtBlock.wavFormat, 18, p_sBuffer );
m_nCurPos += 18;
}
/*
if( m_kFmtBlock.wavFormat.dwSamplesPerSec != 22050 )
{
LOGE("we only support Samples Rate: 22050 KHz" );
return false;
}
if( m_kFmtBlock.wavFormat.wChannels != 0x1 )
{
LOGE("we only support one channel" );
return false;
}
if( m_kFmtBlock.wavFormat.wBitsPerSample != 16 )
{
LOGE("we only support BitsPerSample: 16" );
return false;
}
if( m_kFmtBlock.wavFormat.wFormatTag != 0x1 )
{
LOGE("we only support wFormatTag != 0x1" );
return false;
}
*/
}
/*
//读取无用信息 改到下面循环查找了
{
//试图读取fact
read(&m_kFact, sizeof(FACT_BLOCK), p_sBuffer);
if (memcmp(m_kFact.szFactID, "fact", 4) == 0)
{
m_nCurPos += sizeof(FACT_BLOCK);
read(&m_vFackBlockBuffer, m_kFact.dwFactSize, p_sBuffer);
m_nCurPos += m_kFact.dwFactSize;
}
//试图读取JUNK
read(&m_kJunkBlock, sizeof(_JUNK_BLOCK), p_sBuffer);
if (memcmp(m_kJunkBlock.szJUNK, "JUNK", 4) == 0)
{
m_nCurPos += sizeof(_JUNK_BLOCK) + m_kJunkBlock.dwJunkSize;
}
//试图读取FLLR
read(&m_kJunkBlock, sizeof(_JUNK_BLOCK), p_sBuffer);
if (memcmp(m_kJunkBlock.szJUNK, "FLLR", 4) == 0)
{
m_nCurPos += sizeof(_JUNK_BLOCK) + m_kJunkBlock.dwJunkSize;
}
}
*/
bool bResult = false;
int nSize = p_nBufferSize - sizeof(DATA_BLOCK);
while( m_nCurPos < nSize )
{
read( &m_kDataBlock, sizeof(DATA_BLOCK), p_sBuffer );
if ( memcmp( m_kDataBlock.szDataID, "data", 4 ) == 0 )
{
m_kDataBlock.dwDataSize /= 2;
m_nCurPos += sizeof(DATA_BLOCK);
bResult = true;
break;
}
else
{
m_nCurPos += sizeof(DATA_BLOCK) + m_kDataBlock.dwDataSize;
}
}
if (bResult == false)
{
LOGE("JCWaveInfo::LoadData parse wav error");
return false;
}
//拿出data数据
m_nRealDataSize = m_kDataBlock.dwDataSize * 2;
m_pData = new U8[m_nRealDataSize];
for( unsigned int i =0; i< m_kDataBlock.dwDataSize; i++ )
{
U8 n1 = (U8) getC( p_sBuffer );
m_nCurPos ++;
U8 n2 = (U8) getC( p_sBuffer );
m_nCurPos ++;
m_pData[i*2+0] = n1;
m_pData[i*2+1] = n2;
}
return true;
}
//------------------------------------------------------------------------------
void JCWaveInfo::read( void* pRet, int p_nSize,unsigned char* p_sBuffer )
{
unsigned char* p = p_sBuffer + m_nCurPos;
memcpy( pRet,(void*)(p),p_nSize );
}
//------------------------------------------------------------------------------
unsigned char JCWaveInfo::getC( unsigned char* p_sBuffer )
{
return p_sBuffer[m_nCurPos];
}
//------------------------------------------------------------------------------
}
//-----------------------------END FILE--------------------------------
@@ -0,0 +1,136 @@
/**
@file JCWaveInfo.h
@brief
@author James
@version 1.0
@date 2014_11_26
*/
#ifndef __JCWaveInfo_H__
#define __JCWaveInfo_H__
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <string>
namespace laya
{
typedef unsigned char U8;
typedef unsigned short U16;
typedef unsigned int U32;
typedef unsigned long U64;
#pragma pack(push,1)
typedef struct _RIFF_HEADER
{
U8 szRiffID[4]; // 'R','I','F','F'
U32 dwRiffSize;
U8 szRiffFormat[4]; // 'W','A','V','E'
}RIFF_HEADER;
typedef struct _WAVE_FORMAT
{
U16 wFormatTag;
U16 wChannels;
U32 dwSamplesPerSec;
U32 dwAvgBytesPerSec;
U16 wBlockAlign;
U16 wBitsPerSample;
U16 pack; //附加信息
}WAVE_FORMAT;
typedef struct _FMT_BLOCK
{
U8 szFmtID[4]; // 'f','m','t',' '
U32 dwFmtSize;
WAVE_FORMAT wavFormat;
}FMT_BLOCK;
typedef struct _FACT_BLOCK
{
U8 szFactID[4]; // 'f','a','c','t'
U32 dwFactSize;
}FACT_BLOCK;
typedef struct _JUNK_BLOCK
{
U8 szJUNK[4];
U32 dwJunkSize;
}JUNK_BLOCK;
typedef struct _DATA_BLOCK
{
U8 szDataID[4]; // 'd','a','t','a'
U32 dwDataSize;
}DATA_BLOCK;
#pragma pack(pop)
/**
* @brief
*/
class JCWaveInfo
{
public:
/** @brief构造函数
*/
JCWaveInfo( void );
/** @brief析构函数
*/
~JCWaveInfo( void );
/** @brief 加载数据
* @param[in] 文件名
* @return true 代表读取成功 false代表失败
*/
bool LoadData( const char* p_sFileName );
/** @brief 加载数据
* @param[in] buffer数据
* @param[in] 长度
* @return true 代表读取成功 false代表失败
*/
bool LoadData( unsigned char* p_sBuffer,int p_nBufferSize );
private:
unsigned char getC( unsigned char* p_sBuffer );
void read( void* pRet, int p_nSize, unsigned char* p_sBuffer );
public:
char m_vFackBlockBuffer[20]; //FackBlockBuffer
RIFF_HEADER m_kRiff; //数据头
FMT_BLOCK m_kFmtBlock; //fmt
DATA_BLOCK m_kDataBlock; //数据
U32 m_nRealDataSize; //数据长度
U8* m_pData; //数据
int m_nCurPos; //当前位置
void* m_pExternalMark; //外部标记,现在目的是为了存储JSAudio的指针,为了能够回调JS,或者想存储你想存储的数据
std::string m_sUrl; //url
double m_nTouchTime; //时间戳
//FACT_BLOCK m_kFact; //fact
//JUNK_BLOCK m_kJunkBlock; //JunkBlock
};
}
#endif //__JCWaveInfo_H__
//-----------------------------END FILE--------------------------------
@@ -0,0 +1,90 @@
/**
@file JCWaveParser.cpp
@brief
@author James
@version 1.0
@date 2014_11_26
*/
#include "JCWaveParser.h"
#pragma warning (disable: 4996)
namespace laya
{
//------------------------------------------------------------------------------
JCWaveParser* JCWaveParser::m_sWaveParser = NULL;
//------------------------------------------------------------------------------
JCWaveParser::JCWaveParser()
{
}
//------------------------------------------------------------------------------
JCWaveParser::~JCWaveParser()
{
}
//------------------------------------------------------------------------------
JCWaveParser* JCWaveParser::GetInstance( void )
{
if( m_sWaveParser == NULL )
{
m_sWaveParser = new JCWaveParser();
}
return m_sWaveParser;
}
//-----------------------------------------------------------------------------
void JCWaveParser::DelInstance( void )
{
}
//------------------------------------------------------------------------------
JCWaveInfo* JCWaveParser::GetWaveInfoFromFile( const char* p_sFileName )
{
JCWaveInfo* pWaveInfo = new JCWaveInfo();
bool bRet = pWaveInfo->LoadData( p_sFileName );
if( bRet )
{
return pWaveInfo;
}
return NULL;
}
//------------------------------------------------------------------------------
JCWaveInfo* JCWaveParser::GetWaveInfoFromBuffer( unsigned char* p_sBuffer, int p_nBufferSize )
{
JCWaveInfo* pWaveInfo = new JCWaveInfo();
bool bRet = pWaveInfo->LoadData( p_sBuffer,p_nBufferSize );
if( bRet )
{
return pWaveInfo;
}
return NULL;
}
//------------------------------------------------------------------------------
void JCWaveParser::TestWave( const char* p_sFileName,const char* p_sOutFileName )
{
JCWaveInfo* pInfo = GetWaveInfoFromFile( p_sFileName );
if( pInfo )
{
FILE* fp = fopen( p_sOutFileName,"a+" );
char sBuf[128];
fputs( "{", fp );
for( unsigned int i = 0 ; i < pInfo->m_nRealDataSize; i++ )
{
memset( sBuf,0,128 );
if( i == pInfo->m_nRealDataSize -1 )
{
sprintf( sBuf,"%d", pInfo->m_pData[i] );
}
else
{
sprintf( sBuf,"%d,", pInfo->m_pData[i] );
}
fputs( sBuf,fp );
}
fputs( "};", fp );
fclose( fp );
}
}
}
//-----------------------------END FILE--------------------------------
@@ -0,0 +1,68 @@
/**
@file JCWaveParser.h
@brief
@author James
@version 1.0
@date 2014_11_26
*/
#ifndef __JCWaveParser_H__
#define __JCWaveParser_H__
#include "JCWaveInfo.h"
/**
* @brief 读取wav文件 目前只支持
* 单声道
* 16位
* 采样级别 8000
* 音频格式 PCM
*/
namespace laya
{
class JCWaveParser
{
public:
/** @brief构造函数
*/
JCWaveParser( void );
/** @brief析构函数
*/
~JCWaveParser( void );
/*
* getInstace()
*/
static JCWaveParser* GetInstance( void );
static void DelInstance( void );
/*
* 从文件中读取wave数据
*/
JCWaveInfo* GetWaveInfoFromFile( const char* p_sFileName );
/*
* 从数据中读取wave
*/
JCWaveInfo* GetWaveInfoFromBuffer( unsigned char* p_sBuffer, int p_nBufferSize );
/**
* @brief 测试用的
*/
void TestWave( const char* p_sFileName,const char* p_sOutFileName );
protected:
static JCWaveParser* m_sWaveParser; //静态的this指针
};
}
#endif //__JCWaveParser_H__
//-----------------------------END FILE--------------------------------
@@ -0,0 +1,796 @@
/**
@file JCFileResManager.cpp
@brief
@author James
@version 1.0
@date 2016_5_11
*/
#include "JCFileResManager.h"
#include "../downloadCache/JCServerFileCache.h"
#include "../downloadCache/JCFileTable.h"
#include "../misc/JCWorkerThread.h"
#include "../util/JCCommonMethod.h"
#include "../util/Log.h"
#include "../util/JCLayaUrl.h"
#include "../fileSystem/JCFileSystem.h"
#include "../downloadMgr/JCCurlWrap.h"
//#include "../misc/conchDebugThread.h"
#include <algorithm>
#include "../downloadMgr/JCDownloadMgr.h"
#include "../util/JCCrypto.h"
#include "../util/JCIThreadCmdMgr.h"
#include "../downloadMgr/JCHttpHeader.h"
namespace laya
{
HandleFileData gHandleDataFunc = nullptr;
char* _conch_mallocData(int len) {
return new char[len];
}
void setFileDataHandler(HandleFileData h) {
gHandleDataFunc = h;
}
char* tttt(const char* data, int& len) {
//test setFileDataHandler
char* pd = _conch_mallocData(len);
memcpy(pd, data, len);
return pd;
}
std::string JCFileRes::s_strUploadChkErrUrl = "";
bool JCFileRes::s_bHasIgnoreChksum = false;
std::vector<std::string> JCFileRes::s_vIgnoreChksumError;
std::mutex JCFileRes::s_ignorechklock;
std::string JCFileRes::s_strExtVersion;
JCFileRes::JCFileRes(JCDownloadMgr* pNetLoader, JCFileResManager* pMgr)
{
m_nLastAction = INIT;
m_nLength = 0;
m_nLocalFileID = 0;
m_bDownloading = false;
m_CallbackRef.reset(new int(1));
m_bIgnoreError = false;
m_bSendToJS_complete = false;
m_pNetLoader = pNetLoader;
m_pMgr = pMgr;
}
JCFileRes::~JCFileRes() {
m_pBuffer.reset((char*)0);
m_CallbackRef.reset();
}
void JCFileRes::normalizeUrl() {
bool bToLower = m_pMgr ? m_pMgr->m_bUrlToLowerCase : false;
if (m_Url.m_nProto == JCUrl::file) {
std::string ret = m_Url.m_strPath + "/" + m_Url.m_vPath[m_Url.m_vPath.size() - 1];
if (bToLower)
UTF8ToLowercase((char*)ret.c_str());
m_strURL = ret;
return ;
}
std::string file = m_Url.m_vPath.size() > 0 ? m_Url.m_vPath[m_Url.m_vPath.size() - 1] : "";
std::string attFile = file.length() > 0 ? ("/" + file) : "/";
std::string& query = m_Url.m_Query;
std::string attQuery = query.length() > 0 ? (query) : "";
std::string ret = m_Url.m_strPath + attFile;
if (bToLower)
UTF8ToLowercase((char*)ret.c_str());
ret += attQuery;
m_strURL = ret;
}
void JCFileRes::load(const char* p_pszURL, JCSharedBuffer* pSyncResult) {
if (!p_pszURL)
return;
std::weak_ptr<int> wptr(m_CallbackRef);
m_Url.parse(p_pszURL);
normalizeUrl();
//bool bToLower = m_pMgr ? m_pMgr->m_bUrlToLowerCase : false;
//m_strURL = normalizePath(p_pszURL, bToLower, nProto);
//判断是否是file://
if (m_Url.m_nProto == JCUrl::file) {
std::weak_ptr<int> wptr(m_CallbackRef);
const char* pPath = m_strURL.c_str() + strlen("file://");
if (pPath[2] == ':') {
pPath += 1;
}
JCBuffer buf;
bool bret = readFileSync(pPath, buf);
if (bret && buf.m_pPtr) {
//如果本地打开成功,则可以立刻设置回调,因为是在一个线程
//这么写可以做到立即回调,相当于同步函数。这样即使没有缓存也可以应付 JsAppCache::loadCachedURL 的要求。
m_pBuffer.reset(new char[buf.m_nLen]);
memcpy(m_pBuffer.get(), buf.m_pPtr, buf.m_nLen);
m_nLength = buf.m_nLen;
//hugao add
checkIsEncrypted(m_pBuffer.get(), m_nLength);
if (gHandleDataFunc) {
int nNewlen = m_nLength;
char* pNewData = gHandleDataFunc(m_pBuffer.get(), nNewlen);
if (pNewData ) {
m_nLength = nNewlen;
m_pBuffer.reset(pNewData);
}
}
setState(ready);
if (pSyncResult) {
pSyncResult->m_pBuffer = m_pBuffer;
pSyncResult->m_nLen = m_nLength;
}
//立即失效。如果再有相同请求,需要重新加载
m_pBuffer.reset((char*)0); //TODO 测试:这个不一定会导致释放
m_nLength = 0;
setState(freed);
}
else {
onDownloadError(1, 0, wptr);
}
return;
}
unsigned int chksum = 0;
//再尝试从缓存中取
JCSharedBuffer buff;
bool needDownload = true;
JCServerFileCache* pSvFileCache = m_pMgr->m_pFileCache;
if (pSvFileCache) {
std::string strQuery = m_Url.m_Query;
std::string& extV = s_strExtVersion;
if (strQuery.length() > 1) {
if ( extV.length()>0 &&
strQuery.length() > extV.length() &&
memcmp(strQuery.c_str() + 1, extV.c_str(), extV.length()) == 0 &&
strQuery.find('&') == std::string::npos) {
m_bExtVersion = true;
//符合版本号的规则。这时候,只计算文件本身,?后面的都不算
std::string fullfile = m_Url.m_strPath + "/" + m_Url.m_vPath[m_Url.m_vPath.size() - 1];
m_nLocalFileID = pSvFileCache->getFileID(fullfile.c_str());
const char* pExtV = strQuery.c_str() + 1 + extV.length();
//不管是不是0都要计算真正的hash值,因为可能资源中没有,需要下载,需要缓存
m_nExtVersionID = JCCachedFileSys::hashRaw(pExtV);
if (*pExtV == '0'&& *(pExtV+1)==0) {
//版本为0的特殊处理
//前提是肯定有dcc
pSvFileCache->getFileInfo(m_nLocalFileID, chksum);
needDownload = !pSvFileCache->load(m_nLocalFileID, chksum, buff,false,true);
}
else {
JCCachedFileSys::typeChkSum tcs = (JCCachedFileSys::typeChkSum)m_nExtVersionID;
needDownload = !pSvFileCache->load(m_nLocalFileID, tcs, buff,true,true);
}
}
else {
//不是外部版本控制,
//20170821 ?? 这个要走缓存么? 已经忘了为什么这么写了。
// m_strURL是完整的带?的,如果缓存中通过某种方法加了这种地址,应该也允许缓存
m_nLocalFileID = pSvFileCache->getFileID(m_strURL.c_str());
JCCachedFileSys::typeChkSum tcs = 0;
needDownload = !pSvFileCache->load(m_nLocalFileID, tcs, buff, false,true);
}
}
else {
//没有?,可以用dcc
m_nLocalFileID = pSvFileCache->getFileID(m_strURL.c_str());// hashURLFull(m_strURL.c_str());
pSvFileCache->getFileInfo(m_nLocalFileID, chksum);
needDownload = !pSvFileCache->load(m_nLocalFileID, chksum, buff,false,true);
}
}
if (needDownload) {
if (m_bDownloading)
return;
//注意不能是p_pszURL。因为不可靠。如果是p_pszURL就是m_strURL获得的,则在 m_strURL = normalizePath
//的时候,会导致p_pszURL被释放。
m_bDownloading = true;
if (chksum != 0) {
char chkbuf[64] = { 0 };
sprintf(chkbuf, "%x", chksum);
std::string cdnfriendUrl = m_strURL + "?" + chkbuf;
verifyDownload(cdnfriendUrl.c_str(), chksum);
}
else {
verifyDownload(m_strURL.c_str(), chksum);
}
goto end;
}
//loadok:
{
m_nLastAction = LOADFROMCACHE;
//如果本地打开成功,则可以立刻设置回调,因为是在一个线程
m_pBuffer = buff.m_pBuffer;
m_nLength = buff.m_nLen;
if (pSyncResult) {
pSyncResult->m_pBuffer = buff.m_pBuffer;
pSyncResult->m_nLen = m_nLength;
}
//转到js线程执行setState。不要同步了,保持与浏览器一致。
if (m_pMgr->m_pCmdPoster) {
if (!m_bSendToJS_complete) {
m_bSendToJS_complete = true; //这里肯定是js线程,可以处理这个标志
std::function<void()> cb = std::bind(&JCFileRes::onResDownloadOK_JSThread, this, wptr);
m_pMgr->m_pCmdPoster->postToJS(cb);
}
}
else {
onResDownloadOK_JSThread(wptr);
}
}
end:
int a = 0;
}
bool JCFileRes::loadFromCache(JCBuffer& buff, bool bDoCheckSum) {
JCUrl url;
url.parse(m_strURL.c_str());
if (url.m_nProto == JCUrl::file) {
char* pFile = (char*)m_strURL.c_str();
if (strstr(m_strURL.c_str(), "file:///") == pFile) {
if (pFile[9] == ':') {
pFile += 8;
}
else {
pFile += 7;
}
}
return readFileSync(pFile, buff);
}
if (m_bDownloading)
return false;
if (!m_pMgr || !m_pMgr->m_pFileCache)
return false;
if (bDoCheckSum && m_bExtVersion) {
JCSharedBuffer _buff;
JCCachedFileSys::typeChkSum tcs = (JCCachedFileSys::typeChkSum)m_nExtVersionID;
if (m_pMgr->m_pFileCache->load(m_nLocalFileID, tcs, _buff, true, true)) {
if (_buff.m_pBuffer.get()) {
buff.create(_buff.m_nLen);
memcpy(buff.m_pPtr, _buff.m_pBuffer.get(), _buff.m_nLen);
return true;
}
}
return false;
}
return m_pMgr->m_pFileCache->load(m_nLocalFileID, buff, bDoCheckSum);
}
bool JCFileRes::checkIsEncrypted(char *buf, int len){
#ifndef THIN_COMMON_WITHOUT_DOWNLOAD
bool temp = JCEncrypt::decrypt(buf, len);
if (temp)m_nLength = len - JCEncrypt::s_nPreLen;
return temp;
#else
return false;
#endif
}
void JCFileRes::verifyDownload(const char* p_pszURL, unsigned int p_nChkSum) {
m_nLastAction = DOWNLOADING;
JCDownloadMgr* pNetLoader = m_pNetLoader;
std::weak_ptr<int> wptr(m_CallbackRef);
pNetLoader->download(p_pszURL, 0,
std::bind(&JCFileRes::onProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, wptr),
std::bind(&JCFileRes::onDownloaded, this,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3,
std::placeholders::_4,
std::placeholders::_5,
std::placeholders::_6,
p_nChkSum, 1, wptr), m_nOptTimeout, m_nConnTimeout);
}
void JCFileRes::onResDownloadOK_JSThread(std::weak_ptr<int> p_cbref) {
if (!p_cbref.lock())
return;
if (m_nLength == 0) //如果已经为0了,则表示已经处理的,状态改变已经通知给需要的人了,直接返回。
return;
//hugao add
checkIsEncrypted(m_pBuffer.get(), m_nLength);
if (gHandleDataFunc) {
int nNewlen = m_nLength;
char* pNewData = gHandleDataFunc(m_pBuffer.get(), nNewlen);
if (pNewData) {
m_nLength = nNewlen;
m_pBuffer.reset(pNewData);
}
}
setState(ready);
//立即失效。如果再有相同请求,需要重新加载
m_pBuffer.reset((char*)0); //TODO 测试:这个不一定会导致释放
m_nLength = 0;
setState(freed);
m_bSendToJS_complete = false; //处理完了,可以继续post了。
}
void JCFileRes::notifyErrorHandler(int p_nError, int p_nHttpResponse) {
if (!m_bIgnoreError) {
}
}
void JCFileRes::onResDownloadErr_JSThread(std::weak_ptr<int> p_cbref, int p_nError, int p_nHttpResponse) {
if (!p_cbref.lock())
return;
//mnErrNo = downloadError;
m_bDownloading = false;
m_nErrNo = p_nError;
m_nLastHttpResponse = p_nHttpResponse;
setState(error);
//立即失效。如果再有相同请求,需要重新加载
m_pBuffer.reset((char*)0); //TODO 测试:这个不一定会导致释放
m_nLength = 0;
//setState(freed); 不应该再设置成freed,应该保留error,否则再次设置 setOnErrorCB的话
}
inline bool _MaybeAnImg(const char* pBuff, int nLen) {
if (pBuff && nLen > 4) {
static int jpegID = 0x00ffd8ff; //最高位00那个位置不一定是什么
static int gifID = 0x38464947;
static int pngID = 0x474e5089;
int idval = *(int*)pBuff;
return (idval == pngID || idval == gifID || (idval & 0xffffff) == jpegID);
}
return false;
}
inline bool _StrInVec(const char* pStr, std::vector<std::string>& vec) {
for (int ei = 0, sz = vec.size(); ei < sz; ei++) {
if (vec[ei] == pStr) {
return true;
}
}
return false;
}
void JCFileRes::onDownloaded(JCBuffer& p_Buff,
const string& pLocalAddr, const string& pSvAddr,
int pnCurlRet, int pnHttpRet,
const string& pstrHeader,
unsigned int p_nChkSum,
int p_nDownloadNum, std::weak_ptr<int> p_cbref) {
//用这种方式并不可靠,当多线程的时候,可能在这时候还有效,执行完这句就被删除了。
if (!p_cbref.lock())
return;
m_nLastAction = DOWNLOADED;
m_strSvIP = pSvAddr;
LOGI("Downloaded %s@%s s=%x l=%d", m_strURL.c_str(), pSvAddr.c_str(), p_nChkSum, p_Buff.m_nLen);
bool chkErr = false;
JCServerFileCache* pSvFileCache = nullptr;
if (pnCurlRet == CURLE_OK && pnHttpRet >= 200 && pnHttpRet < 300) {
//如果什么都没有返回,则不用继续处理了
if (p_Buff.m_pPtr == NULL || p_Buff.m_nLen == 0)
goto end;
pSvFileCache = m_pMgr->m_pFileCache;
if (pSvFileCache) {
if (p_nChkSum > 0) {
unsigned int downchk = JCCachedFileSys::getChkSum(p_Buff.m_pPtr, p_Buff.m_nLen);
if (downchk != p_nChkSum) {
//校验没有通过。
if (s_strUploadChkErrUrl.length() > 0) {
//上传一次。注意这里必须是在下载线程,以免造成卡顿
JCDownloadMgr* pNetLoader = m_pNetLoader;
if (pNetLoader) {
char postbuf[1024];
sprintf(postbuf, "%s,%s,%08x,%08x,%d,%s",
pLocalAddr.c_str(),
pSvAddr.c_str(),
downchk, //实际
p_nChkSum, //应该
p_Buff.m_nLen,//长度
m_strURL.c_str());//url
pNetLoader->postData(s_strUploadChkErrUrl.c_str(),
postbuf, strlen(postbuf), JCDownloadMgr::defCompleteFunc
);
}
}
//这时候,只能算作临时文件
chkErr = true;
goto updatecache;
}
}
goto updatecache; //
}
//如果没有appcache也要通知onok
//转到js线程执行setState。
m_pBuffer = std::shared_ptr<char>(new char[p_Buff.m_nLen], std::default_delete<char[]>());
memcpy(m_pBuffer.get(), p_Buff.m_pPtr, p_Buff.m_nLen);
m_nLength = p_Buff.m_nLen;
std::weak_ptr<int> wptr(m_CallbackRef);
if (m_pMgr->m_pCmdPoster) {
std::function<void()> cb = std::bind(&JCFileRes::onResDownloadOK_JSThread, this, wptr);
m_pMgr->m_pCmdPoster->postToJS(cb);
}
else {
onResDownloadOK_JSThread(wptr);
}
goto end;
}
else {
//error
std::weak_ptr<int> wptr(m_CallbackRef);
onDownloadError(pnCurlRet, pnHttpRet, wptr);
goto end;
}
updatecache:{
std::string ext = getLowercaseExtOfUrl(m_strURL.c_str());
m_pBuffer = std::shared_ptr<char>(new char[p_Buff.m_nLen], std::default_delete<char[]>());
memcpy(m_pBuffer.get(), p_Buff.m_pPtr, p_Buff.m_nLen);
m_nLength = p_Buff.m_nLen;
std::weak_ptr<int> wptr(m_CallbackRef);
//需要更新缓存。
bool bNeedSave = false;
if (m_bExtVersion || (p_nChkSum != 0 && !chkErr)) {
//长期保存
std::string strLocalFile = pSvFileCache->updateAFile(m_nLocalFileID,
m_pBuffer.get(), p_Buff.m_nLen,
m_bExtVersion ? m_nExtVersionID : p_nChkSum,
m_bExtVersion,
0,0);
}
else if (_MaybeAnImg(p_Buff.m_pPtr, p_Buff.m_nLen) ||
_StrInVec(ext.c_str(), m_pMgr->m_vExtNeedSave) ||
chkErr ) {
//临时保存。进程内不允许删除
JCHttpHeader hh(pstrHeader.c_str());
//hh.getCacheContrl()
//TODO 计算过期时间
std::string strLocalFile = pSvFileCache->updateAFile(m_nLocalFileID,
m_pBuffer.get(), p_Buff.m_nLen,
0,
0,
0, 1);
}
else {
//临时保存,进程内可以删除
//TODO 需要实现临时缓存的管理
/*
std::string strLocalFile = pSvFileCache->updateAFile(m_nLocalFileID,
m_pBuffer.get(), p_Buff.m_nLen,
m_bExtVersion ? m_nExtVersionID : p_nChkSum,
m_bExtVersion,
0, 0);
*/
}
m_nLastAction = UPDATECACHE;
//转到js线程执行setState。
if (m_pMgr->m_pCmdPoster) {
std::function<void()> cb = std::bind(&JCFileRes::onResDownloadOK_JSThread, this, wptr);
m_pMgr->m_pCmdPoster->postToJS(cb);
}
else {
onResDownloadOK_JSThread(wptr);
}
}
//nocache:
//int b = 0;
end:
//int c = 0;
m_bDownloading = false;
}
//问题:这个可能是在另外的线程调用的
void JCFileRes::onDownloadedOld(JCBuffer& p_Buff,
const string& pLocalAddr, const string& pSvAddr,
int pnCurlRet, int pnHttpRet,
const string& pstrHeader,
unsigned int p_nChkSum,
int p_nDownloadNum, std::weak_ptr<int> p_cbref) {
//用这种方式并不可靠,当多线程的时候,可能在这时候还有效,执行完这句就被删除了。
if (!p_cbref.lock())
return;
m_nLastAction = DOWNLOADED;
m_strSvIP = pSvAddr;
LOGI("Downloaded %s@%s s=%x l=%d", m_strURL.c_str(), pSvAddr.c_str(), p_nChkSum, p_Buff.m_nLen);
bool downError = (p_Buff.m_pPtr == NULL || p_Buff.m_nLen == 0);
std::string ext = getLowercaseExtOfUrl(m_strURL.c_str());
JCServerFileCache* pSvFileCache = m_pMgr->m_pFileCache;
if (!downError) {
//先写到fileCache中。这时候还不是js线程,这里写是为了不影响js线程的效率。
//问题是可能导致load和写cache不在一个线程。这个可能问题不大
//如果非要一个线程,则这里不要调用,通过 setOnReadyCB( std::bind(&svFileCache::onFileDownloaded, pSvFileCache, std::placeholders::_1, chksum)) 的方法在js线程中执行
bool chkok = p_nChkSum == 0;
if (p_nChkSum) {
unsigned int downchk = 0;
//如果是文本文件,把\r\n统一换成\n再进行校验,以绕开各个环节的转换
downchk = JCCachedFileSys::getChkSum(p_Buff.m_pPtr, p_Buff.m_nLen);
chkok = (downchk == p_nChkSum);
if (!chkok && checkIgnoreChksum((char*)ext.c_str())) {
LOGW("[%s@%s]Check error,but you can ignore,real:%08x,should be:%08x,loc=%s\n", m_strURL.c_str(), pSvAddr.c_str(),
downchk, p_nChkSum, pLocalAddr.c_str());
//errorflog("[%s@%s]校验错误,但忽略,实%08x,应 %08x,loc=%s\n", m_strURL.c_str(), p_pCurl->m_strSvAddr.c_str(), downchk, p_nChkSum, p_pCurl->m_strLocalAddr.c_str());
chkok = true; //算作正确
//上传一次
if (s_strUploadChkErrUrl.length() > 0) {
//上传一次。注意这里必须是在下载线程,以免造成卡顿
JCDownloadMgr* pNetLoader = m_pNetLoader;
if (pNetLoader) {
char postbuf[1024];
sprintf(postbuf, "%s,%s,%08x,%08x,%d,%s",
pLocalAddr.c_str(),
pSvAddr.c_str(),
downchk, //实际
p_nChkSum, //应该
p_Buff.m_nLen,//长度
m_strURL.c_str());//url
pNetLoader->postData(s_strUploadChkErrUrl.c_str(),
postbuf, strlen(postbuf), JCDownloadMgr::defCompleteFunc
);
}
}
}
if (!chkok) {
LOGW("[%s@%s]check error,real:%08x,should be:%08x,loc=%s\n", m_strURL.c_str(), pSvAddr.c_str(), downchk,
p_nChkSum, pLocalAddr.c_str());
//errorflog("[%s@%s]校验错误,实%08x,应 %08x,loc=%s\n", m_strURL.c_str(), p_pCurl->m_strSvAddr.c_str(), downchk, p_nChkSum, p_pCurl->m_strLocalAddr.c_str());
if (p_nDownloadNum >= MAXDOWNLOADTRY && s_strUploadChkErrUrl.length() > 0) {
//上传一次。注意这里必须是在下载线程,以免造成卡顿
JCDownloadMgr* pNetLoader = m_pNetLoader;
if (pNetLoader) {
char postbuf[1024];
sprintf(postbuf, "%s,%s,%08x,%08x,%d,%s",
pLocalAddr.c_str(),
pSvAddr.c_str(),
downchk, //实际
p_nChkSum, //应该
p_Buff.m_nLen,//长度
m_strURL.c_str());//url
pNetLoader->postData(s_strUploadChkErrUrl.c_str(), postbuf, strlen(postbuf),
JCDownloadMgr::defCompleteFunc
);
}
}
}
}
if (!chkok && p_nDownloadNum < MAXDOWNLOADTRY) {
//继续下载
LOGI("再次尝试下载");
JCDownloadMgr* pNetLoader = m_pNetLoader;
std::weak_ptr<int> wptr(m_CallbackRef);
pNetLoader->download(m_strURL.c_str(), 0,
std::bind(&JCFileRes::onProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, wptr),
std::bind(&JCFileRes::onDownloaded, this,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3,
std::placeholders::_4,
std::placeholders::_5,
std::placeholders::_6,
p_nChkSum, ++p_nDownloadNum, wptr),
0);
return;
}
downError = !chkok;
if (!chkok && s_strUploadChkErrUrl.length() <= 0) {//如果有上传地址,就不再需要提示了
//JCErrorHandleInterface* pEH = getErrorHandler();
//if(pEH){
// pEH->download_chkSumVerifyError(m_strURL.c_str());
//}
}
}
//下载的数据先copy一份,使用完了之后,立即删除
//即使校验错误,也算作正确的。
if (!downError || p_nDownloadNum > 2) {
//注意:这里并没有复制buffer,外部使用者需要自己复制。
m_pBuffer = std::shared_ptr<char>(new char[p_Buff.m_nLen],std::default_delete<char[]>());
memcpy(m_pBuffer.get(), p_Buff.m_pPtr, p_Buff.m_nLen);
m_nLength = p_Buff.m_nLen;
std::weak_ptr<int> wptr(m_CallbackRef);
//如果是缓存文件。需要更新缓存。
bool bNeedSave = false;
if (p_nChkSum) {
bNeedSave = p_nDownloadNum <= 1;
}
else {
//如果是图片文件要恢复
if (p_Buff.m_pPtr && p_Buff.m_nLen > 4) {
static int jpegID = 0x00ffd8ff; //最高位00那个位置不一定是什么
static int gifID = 0x38464947;
static int pngID = 0x474e5089;
int idval = *(int*)p_Buff.m_pPtr;
if (idval == pngID || idval == gifID || (idval & 0xffffff) == jpegID)
bNeedSave = true;
}
//如果是外部版本管理,需要保存
if (!bNeedSave) {
}
if (!bNeedSave) {
//如果是需要同步恢复的资源文件,就要缓存
for (int ei = 0, sz = m_pMgr->m_vExtNeedSave.size(); ei < sz; ei++) {
if (m_pMgr->m_vExtNeedSave[ei] == ext) {
bNeedSave = true;
break;
}
}
}
}
if (bNeedSave){
std::string strLocalFile = pSvFileCache->updateAFile(
m_nLocalFileID, m_pBuffer.get(), p_Buff.m_nLen,
p_nChkSum, 0, 0, 0);
if (strLocalFile.length() > 0) {
m_nLastAction = UPDATECACHE;
}
}
//转到js线程执行setState。
if (m_pMgr->m_pCmdPoster) {
std::function<void()> cb = std::bind(&JCFileRes::onResDownloadOK_JSThread, this, wptr);
m_pMgr->m_pCmdPoster->postToJS(cb);
}
else {
onResDownloadOK_JSThread(wptr);
}
}
else {
m_pBuffer.reset((char*)0);
m_nLength = 0;
//notifyErrorHandler(Curl::EC_PartialFile, 0); //校验错误的responsecode只能0了
std::weak_ptr<int> wptr(m_CallbackRef);
if (m_pMgr->m_pCmdPoster) {
//转到js线程执行setState。
std::function<void()> cb = std::bind(&JCFileRes::onResDownloadErr_JSThread,
this, wptr, -1, 0);
m_pMgr->m_pCmdPoster->postToJS (cb);
}
else {
onResDownloadErr_JSThread(wptr, -1, 0);
}
}
m_bDownloading = false;
}
bool JCFileRes::restoreRes() {
load(m_strURL.c_str(),nullptr);
return true;
}
void JCFileRes::onDownloadError(int p_nError, int p_nHttpResponse, std::weak_ptr<int> p_cbref) {
//std::lock_guard<std::mutex> lock(m_pMgr->m_maplock);//要删除map,可能会与请求的时候冲突
if (!p_cbref.lock())
return;
m_pBuffer.reset((char*)0);
m_nLength = 0;
if (!m_bIgnoreError) {
LOGE("JCFileRes::onDownloadError file error[%d]:%s", p_nError, m_strURL.c_str());
//errorflog("下载文件错误[%d]%s",p_nError, m_strURL.c_str());
}
//m_pMgr->delRes(m_strURL.c_str()); 到资源管理器中统一做
//auto it = m_ResMap.find(m_strURL.c_str());
notifyErrorHandler(p_nError, p_nHttpResponse);
std::weak_ptr<int> wptr(m_CallbackRef);
//转到js线程执行setState。
if (m_pMgr->m_pCmdPoster) {
std::function<void()> cb = std::bind(&JCFileRes::onResDownloadErr_JSThread, this, wptr, p_nError, p_nHttpResponse);
m_pMgr->m_pCmdPoster->postToJS(cb);
}
else {
onResDownloadErr_JSThread(wptr, p_nError, p_nHttpResponse);
}
}
int JCFileRes::onProgress(unsigned int now, unsigned int total, float speed, std::weak_ptr<int> p_cbref) {
return 0;
}
JCFileResManager::JCFileResManager(JCDownloadMgr* pDownloadMgr) {
m_pFileCache = nullptr;
m_pCmdPoster = nullptr;
m_bUrlToLowerCase = false;
m_vExtNeedSave = {".png",".jpg",".wav",".ogg"};
m_pDownloadMgr = pDownloadMgr;
}
JCFileResManager::~JCFileResManager() {
clear();
}
void JCFileResManager::clear() {
std::lock_guard<std::mutex> lock(m_maplock);
FileResMap::iterator it = m_ResMap.begin();
FileResMap::iterator ed = m_ResMap.end();
while (it != ed) {
delete (*it).second;
it++;
}
m_ResMap.clear();
}
//如果已经下载完了,应该同步去打开文件,不要再走下载了(在load中做了)
JCFileRes* JCFileResManager::getRes(const std::string& url, int p_nConnTimeout , int p_nOptTimeout) {
std::lock_guard<std::mutex> lock(m_maplock);//下面要添加资源,需要锁。因为失败的话会删除
JCFileRes* pRes = NULL;
FileResMap::iterator it = m_ResMap.find(url);
if (it == m_ResMap.end()) {
pRes = new JCFileRes(m_pDownloadMgr,this);
if (p_nConnTimeout>0) {
pRes->m_nConnTimeout = p_nConnTimeout;
}
if (p_nOptTimeout > 0) {
pRes->m_nOptTimeout = p_nOptTimeout;
}
m_ResMap[url] = pRes;
//问题:load和下载回调不在一个线程会有问题么?
// 如果load不修改表和文件内容,下载回调在最后再设置ready,应该没事
pRes->load(url.c_str(),nullptr);
//m_pThread = workerThread::getCurThread();
return pRes;
}
pRes = (*it).second;
if (p_nConnTimeout>0) {
pRes->m_nConnTimeout = p_nConnTimeout;
}
if (p_nOptTimeout > 0) {
pRes->m_nOptTimeout = p_nOptTimeout;
}
return pRes;
}
bool JCFileResManager::delRes(const char* p_pszURL) {
std::lock_guard<std::mutex> lock(m_maplock);
auto it = m_ResMap.find(p_pszURL);
if (it != m_ResMap.end()) {
m_ResMap.erase(it);
return true;
}
return false;
}
void JCFileRes::addChkIgnoreChksumExt(const char* p_pszExt) {
s_bHasIgnoreChksum = true;
std::string ext = p_pszExt;
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
s_ignorechklock.lock();
for (int i = 0, sz = s_vIgnoreChksumError.size(); i < sz; i++) {
if (s_vIgnoreChksumError[i] == ext) {
s_ignorechklock.unlock();
return;
}
}
s_vIgnoreChksumError.push_back(ext);
s_ignorechklock.unlock();
}
void JCFileRes::clearChkIgnoreChksumExt() {
s_bHasIgnoreChksum = false;
s_ignorechklock.lock();
s_vIgnoreChksumError.clear();
s_ignorechklock.unlock();
}
bool JCFileRes::checkIgnoreChksum(char* p_pszExt) {
if (!s_bHasIgnoreChksum)
return false;
std::string ext = p_pszExt;
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
s_ignorechklock.lock();
for (int i = 0, sz = s_vIgnoreChksumError.size(); i < sz; i++) {
if (s_vIgnoreChksumError[i] == ext) {
s_ignorechklock.unlock();
return true;
}
}
s_ignorechklock.unlock();
return false;
}
}
//------------------------------------------------------------------------------
//-----------------------------END FILE--------------------------------
@@ -0,0 +1,168 @@
/**
@file JCFileResManager.h
@brief
@author James
@version 1.0
@date 2016_5_11
*/
#ifndef __JCFileResManager_H__
#define __JCFileResManager_H__
#include "JCResource.h"
#include <map>
#include <string>
#include <memory>
#include "../buffer/JCBuffer.h"
#include <mutex>
#include "../util/JCLayaUrl.h"
#define MAXDOWNLOADTRY 3
namespace laya{
/**
* @param [in] data {const char*} 原始文件内容。
* @param [in out] len {int&} 原始文件内容的长度。
* @return char* 如果改变了数据,就返回新的指针。
*/
typedef char* (*HandleFileData)(const char* data, int& len);
/**@brief 设置一个函数,可以用来修改下载的文件内容。一般用来解密。
*/
void setFileDataHandler(HandleFileData h);
/**
* @brief 其实就是一个new,怕万一跨so的话有分配的问题。
*/
char* _conch_mallocData(int len);
class JCFileResManager;
class JCServerFileCache;
class JCDownloadMgr;
class IConchThreadCmdMgr;
//文件资源
//例如避免下载的时候重复下载之类的
//由于downloadManager已经缓存了,因此本身不缓存。
class JCFileRes:public JCResStateDispatcher{
friend class JCFileResManager;
public:
//最后的行为。测试用
enum Action {
INIT,
REQUEST,
LOADFROMCACHE,
DOWNLOADING,
DOWNLOADED,
UPDATECACHE,
};
JCFileRes(JCDownloadMgr* pNetLoader, JCFileResManager* pMgr); //只能由JCFileResManager创建
~JCFileRes();
virtual bool restoreRes();
//直接读取缓存的。如果不在缓存中,则返回false
bool loadFromCache(JCBuffer& buff, bool bDoCheckSum);
void setIgnoreError(bool b) { m_bIgnoreError = b; };
//设置不需要严格校验的文件。对于这些文件,如果校验失败了,则直接算作成功,并且保存下来
static void addChkIgnoreChksumExt(const char* p_pszExt);
static void clearChkIgnoreChksumExt();
static bool checkIgnoreChksum(char* p_pszExt);
/** @brief 加载资源。注意只允许调试或者manager调用。否则可能会同一个资源加载多次。
* @param[in]
* @return
*/
void load(const char* p_pszURL, JCSharedBuffer* pSyncResult);
protected:
void onResDownloadOK_JSThread(std::weak_ptr<int> p_cbref);
void onResDownloadErr_JSThread(std::weak_ptr<int> p_cbref,int p_nError, int p_nHttpResponse);
void notifyErrorHandler(int p_nError, int p_nHttpResponse);
/** @brief 下载完成的回调。
* @param[in] p_Buff 下载结果。
* @param[in] pLocalAddr 下载线程获得的本地地址。
* @param[in] pSvAddr 下载线程获得的服务器地址。
* @param[in] p_nChkSum 本次下载请求希望的校验值
* @param[in] p_nDownloadNum 这是本请求的第几次下载。因为校验错误的话会尝试多次下载。
* @param[in] p_cbref 保护用。
* @return void
*/
void onDownloaded( JCBuffer& p_Buff ,
const std::string& pLocalAddr, const std::string& pSvAddr,
int pnCurlRet, int pnHttpRet,
const std::string& pstrHeader,
unsigned int p_nChkSum, int p_nDownloadNum, std::weak_ptr<int> p_cbref );
void onDownloadedOld(JCBuffer& p_Buff,
const std::string& pLocalAddr, const std::string& pSvAddr,
int pnCurlRet, int pnHttpRet,
const std::string& pstrHeader,
unsigned int p_nChkSum, int p_nDownloadNum, std::weak_ptr<int> p_cbref);
void onDownloadError(int p_nError, int p_nHttpResponse,std::weak_ptr<int> p_cbref );
int onProgress(unsigned int, unsigned int,float,std::weak_ptr<int> p_cbref );
//带校验的下载。p_nChkSum可以为0,表示不检查
void verifyDownload(const char* p_pszURL, unsigned int p_nChkSum );
bool checkIsEncrypted(char *buf,int len);
void normalizeUrl();
public:
Action m_nLastAction; //测试用
std::string m_strURL;
std::string m_strSvIP; //调试用
unsigned int m_nLocalFileID;
unsigned int m_nExtVersionID = 0; //外部版本号。是一个hash后的值。
bool m_bExtVersion = false;
std::shared_ptr<char> m_pBuffer; //注意必须指定deleter std::default_delete<char[]>
int m_nLength;
std::shared_ptr<int> m_CallbackRef;
static std::string s_strUploadChkErrUrl; //上传校验错误的url,不需要则为空
static std::string s_strExtVersion; //外部版本控制字符串。例如 ?v=1, 则为 v= 加上=是为了避免 v=2和vxx=2分不开
int m_nLastHttpResponse = 0;
int m_nConnTimeout = 0;
int m_nOptTimeout = 0;
protected:
JCDownloadMgr* m_pNetLoader;
JCFileResManager* m_pMgr;
bool m_bDownloading;
bool m_bIgnoreError; //是否忽略下载错误
static std::vector<std::string> s_vIgnoreChksumError;
static bool s_bHasIgnoreChksum;
static std::mutex s_ignorechklock;
std::mutex m_CallbackLock;
bool m_bSendToJS_complete; //完成事件已经post给js队列等待处理了。避免同一个对象多次post。必须都在js线程处理这个变量
JCUrl m_Url;
};
class JCFileResManager{
public:
typedef std::map<std::string, JCFileRes*> FileResMap;
JCFileResManager(JCDownloadMgr* pDownloadMgr);
~JCFileResManager();
void setFileCache( JCServerFileCache* p_pFileCache){
m_pFileCache = p_pFileCache;
}
JCFileRes* getRes(const std::string& url,int p_nConnTimeout=0,int p_nOptTimeout=0);
bool delRes(const char* p_pszURL);
void clear();
public:
JCServerFileCache* m_pFileCache;
bool m_bUrlToLowerCase;
std::mutex m_maplock;
std::vector<std::string> m_vExtNeedSave; //这种扩展名的需要保存到临时缓存中。
IConchThreadCmdMgr* m_pCmdPoster;
protected:
JCDownloadMgr* m_pDownloadMgr;
FileResMap m_ResMap;
};
}
//------------------------------------------------------------------------------
#endif //__JCFileResManager_H__
@@ -0,0 +1,477 @@
/**
@file JCResManager.cpp
@brief
@author James
@version 1.0
@date 2016_5_12
*/
#include "JCResManager.h"
#include "JCResource.h"
#include "../util/Log.h"
#include <stdlib.h>
#include <string>
#include <sstream>
#include <iostream>
namespace laya
{
JCResManager::JCResManager(int p_nFreeSize,bool bUseMap)
{
m_nGlobalID = 0;
m_nCurTick = rand()%SLEEPINGAGE; //给一个随机值。避免所有的manager同时清理。
m_nNextCleanTick = m_nCurTick+SLEEPINGAGE;
m_nCurSize = 0;
m_nFreeSize = p_nFreeSize;
m_nMaxSize = 1*m_nFreeSize;//本来是可以有一个阀值的
m_bUseSetResLock = false;
m_bReleasing = false;
m_nID = 0;
m_pUpdatingSzRes = NULL;
m_bChkThread = false;
m_pLastTouchedRes=NULL;
m_nFreedCount = 0;
m_bUseMap = bUseMap;
}
JCResManager::~JCResManager(){
m_bReleasing=true;
destroyAll();
}
void JCResManager::setFreeSize( unsigned int p_nFreeSize )
{
m_nFreeSize = p_nFreeSize;
m_nMaxSize = 1 * m_nFreeSize;//本来是可以有一个阀值的
}
void JCResManager::setFreeSize( unsigned int p_nFreeSize, unsigned int p_nMaxSize ){
m_nFreeSize = p_nFreeSize;
m_nMaxSize = p_nMaxSize;
}
//注意,为了效率不要频繁调用
//如果需要创建,则改名成 getRes
JCResManager::ResType JCResManager::find(const std::string& p_key ){
ResMap::iterator it = m_AllRes.find(p_key);
if(it==m_AllRes.end()){
return NULL;
//TODO 需要在这里创建新的么。
//TYPE pnew = createNew(p_key);
//m_AllRes[p_key]=pnew;
//return pnew;
}else{
//如果已经释放了,需要出发恢复流程?
return (*it).second;
}
}
//这个只有一个res资源,只能处理活动列表的问题,不能根据map来查找。
void JCResManager::add(JCResManager::ResType pRes){
if( m_bChkThread){
if( m_WorkThread!=std::this_thread::get_id()){
LOGE("JCResManager[%d] have been operated by other threads.", m_nID);
throw -22;
}
}
//为了省事,用一个lock,由于add不太频繁,所以应该问题不大
std::unique_lock<std::recursive_mutex> _lock(m_Lock);
if( pRes->isActive()){
LOGE("Resources have been added!\n");
return;
}
pRes->m_pResManager = this;
pRes->m_nTouch=m_nCurTick;
m_ActiveRes.push_back(pRes);
m_pLastTouchedRes = pRes;
updateSz(pRes);
}
bool JCResManager::freeRes(JCResManager::ResType pRes, bool bDel ){
if( m_bChkThread){
if( m_WorkThread!=std::this_thread::get_id()){
LOGE("JCResManager[%d] have been operated by other threads.", m_nID);
throw -22;
}
}
if( !pRes->isActive())
return false;
std::unique_lock<std::recursive_mutex> _lock(m_Lock);
m_ActiveRes.delNode(pRes);
m_nCurSize-=pRes->m_nUsedMem;
pRes->m_nResSize = 0;
pRes->m_nUsedMem = 0;
pRes->freeRes(!bDel);
return true;
}
/*
从map中删除。如果 bDel为true,则同时删除对象。
返回值:
true 找到这个资源了
false 没有这个资源
*/
bool JCResManager::delRes( const std::string& p_key, bool bDel ){
//避免删除某个资源的时候,又调回来了。
if (m_bReleasing)
return true;
if( m_bChkThread){
if( m_WorkThread!=std::this_thread::get_id()){
LOGE("JCResManager[%d] have been operated by other threads", m_nID);
throw -22;
}
}
ResMap::iterator it = m_AllRes.find(p_key);
if(it!=m_AllRes.end()){
(*it).second->m_nResSize = 0;
freeRes( (*it).second, true );
if(bDel){
//DelFun df;
delete ((*it).second);
}
m_AllRes.erase(it);
return true;
}
return false;
}
bool JCResManager::delRes(int p_key, bool bDel) {
//避免删除某个资源的时候,又调回来了。
if (m_bReleasing)
return true;
if (m_bChkThread) {
if (m_WorkThread != std::this_thread::get_id()) {
LOGE("JCResManager[%d] have been operated by other threads", m_nID);
throw - 22;
}
}
if ((size_t)p_key < m_AllResVector.size())
{
ResType pRes = m_AllResVector[p_key];
if (pRes == NULL) return false;
pRes->m_nResSize = 0;
freeRes(pRes, true);
if (bDel) {
//DelFun df;
delete pRes;
}
m_AllResVector[p_key] = NULL;
return true;
}
return false;
}
//每帧都会调用的。
//由于resManager不会太多,所以可以每帧都调用,不用用请求列表的方式。
void JCResManager::tick(){
m_nCurTick++;
if( m_nCurTick>=m_nNextCleanTick){
if( m_nCurSize>m_nFreeSize )
freeRes( (m_nCurSize-m_nFreeSize)*2 );
m_nNextCleanTick+=SLEEPINGAGE;
}
}
//删除所有的,这个不会有回调
void JCResManager::destroyAll(bool bDelObj){
m_bReleasing=true;
freeAll();
if (m_bUseMap)
{
if (bDelObj) {
ResMap::iterator it = m_AllRes.begin();
while (it != m_AllRes.end()) {
delete (*it).second;
it++;
}
}
m_AllRes.clear();
}
else
{
if (bDelObj) {
for (size_t i = 0; i < m_AllResVector.size(); i++)
{
ResType pRes = m_AllResVector[i];
if (pRes)
{
delete pRes;
}
}
}
m_AllResVector.clear();
}
m_nCurSize = 0;
m_bReleasing=false;
m_nGlobalID = 0;
}
//释放所有的。会有回调
void JCResManager::freeAll(){
std::unique_lock<std::recursive_mutex> _lock(m_Lock);
if( m_ActiveRes.size()<=0)
return ;
ListNode* pHead = m_ActiveRes.getHead();
ListNode* pCurNode = pHead->_Next;
while( pCurNode!=pHead){//越往后越新
JCResource* pRes = (JCResource*)pCurNode;
pRes->m_nResSize=0; //阻止freeRes里面再调用freeRes
pRes->freeRes(!m_bReleasing);
pRes->setState(JCResource::freed);
ListNode* pNext = pCurNode->_Next;
m_ActiveRes.delNode(pCurNode);
pCurNode=pNext;
}
m_pLastTouchedRes = NULL;
m_nCurSize=0;
}
//返回实际释放的大小
int JCResManager::freeRes(int p_nSize ){
if( m_bChkThread){
if( m_WorkThread!=std::this_thread::get_id()){
LOGE("JCResManager[%d] have been operated by other threads.", m_nID);
throw -22;
}
}
//TODO 这个应该有线程的问题
m_nFreedCount++;
std::unique_lock<std::recursive_mutex> _lock(m_Lock);
if( m_ActiveRes.size()<=0)
return 0;
int oldnum = m_ActiveRes.size();
int nFreeSize=0;
//int nLastSize = m_nCurSize;
//int nTargetSz = m_nCurSize-p_nSize;
ListNode* pHead = m_ActiveRes.getHead();
ListNode* pCurNode = pHead->_Next;
//int nid=GetCurrentThreadId();
while( pCurNode!=pHead){//越往后越新
JCResource* pRes = (JCResource*)pCurNode;
//先屏蔽了,因为在ios会出现资源管理恢复导致的黑屏闪烁一下问题
//if (pRes->mnResSize <= 0)
//{
// pCurNode = pCurNode->_Next;
// continue;
//}
if( pRes == m_pUpdatingSzRes){
if(pCurNode->_Next==pHead)
break;
else{
LOGE("被保护资源不在最后一个?");
throw -1;
}
}
nFreeSize+=pRes->m_nUsedMem;
if(pRes->m_nUsedMem==0){
//可能是目前的资源还没有加载完成,例如图片还没与解码完成。
}
pRes->m_nUsedMem = 0;
pRes->m_nResSize = 0;
pRes->freeRes(true);
if (pRes == m_pLastTouchedRes)
m_pLastTouchedRes = NULL;
pRes->setState(JCResource::freed);
ListNode* pNext = pCurNode->_Next;
m_ActiveRes.delNode(pCurNode);
pCurNode=pNext;
//if( m_nCurSize<=nTargetSz )
// break;
if(nFreeSize>=p_nSize){
break;
}
}
m_nCurSize-=nFreeSize;
LOGI("freeRes(%d):Total:%d,left:%d,clearedMem:%d\n", m_nID, oldnum, m_ActiveRes.size(),nFreeSize);
return nFreeSize;
//return (nLastSize-m_nCurSize);
}
/*
加到map中。
加到活动列表中。
*/
void JCResManager::setItem(JCResManager::ResType pRes, const char* name){
if( m_bUseSetResLock ) m_setResLock.lock();
if(pRes->m_pResManager==NULL){
pRes->m_pResManager=this;
}
if(name)
m_AllRes[name]=pRes;
if( pRes->getResSize()>0){
m_pUpdatingSzRes = pRes; //防止被删除。
touchRes(pRes);
updateSz(pRes);
m_pUpdatingSzRes = NULL;
}
if( m_bUseSetResLock ) m_setResLock.unlock();
}
void JCResManager::setItem(JCResManager::ResType pRes, int nID) {
if (m_bUseSetResLock) m_setResLock.lock();
if (pRes->m_pResManager == NULL) {
pRes->m_pResManager = this;
}
//放入vector
int nSize = (int)m_AllResVector.size();
if (nID == nSize)
{
m_AllResVector.push_back(pRes);
}
else if (nID < nSize)
{
if (m_AllResVector[nID] == NULL)
{
m_AllResVector[nID] = pRes;
}
else
{
LOGE("JCResManager::addToAllRes error m_vRes[%d] != NULL", nID);
}
}
else
{
int nOldSize = m_AllResVector.size();
m_AllResVector.resize(nID + 1);
m_AllResVector[nID] = pRes;
}
if (pRes->getResSize() > 0) {
m_pUpdatingSzRes = pRes; //防止被删除。
touchRes(pRes);
updateSz(pRes);
m_pUpdatingSzRes = NULL;
}
if (m_bUseSetResLock) m_setResLock.unlock();
}
void JCResManager::addToAllRes(JCResManager::ResType pRes, const char* name){
if( m_bUseSetResLock ) m_setResLock.lock();
if(name)
m_AllRes[name]=pRes;
if( m_bUseSetResLock ) m_setResLock.unlock();
}
void JCResManager::addToAllRes(ResType pRes, int nID)
{
if (m_bUseSetResLock) m_setResLock.lock();
int nSize = (int)m_AllResVector.size();
if (nID == nSize)
{
m_AllResVector.push_back(pRes);
}
else if (nID < nSize)
{
if (m_AllResVector[nID] == NULL)
{
m_AllResVector[nID] = pRes;
}
else
{
LOGE("JCResManager::addToAllRes error m_vRes[%d] != NULL", nID);
}
}
else
{
int nOldSize = m_AllResVector.size();
m_AllResVector.resize(nID + 1);
m_AllResVector[nID] = pRes;
}
if (m_bUseSetResLock) m_setResLock.unlock();
}
void JCResManager::touchRes( JCResManager::ResType pRes, bool restoreRes ){
if( m_bChkThread){
if( m_WorkThread!=std::this_thread::get_id()){
LOGE("JCResManager[%d] have been operated by other threads.", m_nID);
throw -22;
}
}
pRes->m_nTouch = m_nCurTick;
if(m_pLastTouchedRes==pRes){
if( restoreRes && pRes->getState()==JCResource::freed ){
pRes->restoreRes();
}
return;
}
m_pLastTouchedRes = pRes;
if( m_bUseSetResLock ) m_setResLock.lock();
m_ActiveRes.delNode(pRes);
m_ActiveRes.push_back(pRes);
if (!pRes->isActive())
add(pRes);
//问题:如果资源还没准备好怎么办
if( restoreRes && pRes->getState()==JCResource::freed ){
pRes->restoreRes();
}
if( m_bUseSetResLock ) m_setResLock.unlock();
}
void JCResManager::updateRes(JCResManager::ResType pRes ){
if( m_bUseSetResLock ) m_setResLock.lock();
if( pRes->m_nResSize==0){
//freeRes(pRes,m_bReleasing); 这个太容易引起混乱。例如freeRes->setResSize(0)->updateRes()->freeRes()...
}else{
m_pUpdatingSzRes = pRes;
touchRes(pRes); //先把这个资源放到最后。
updateSz(pRes); //然后在清理资源。这样就会满足本资源是保护资源,并且在队列的最后,就能不被删除了。
m_pUpdatingSzRes = NULL;
}
if( m_bUseSetResLock ) m_setResLock.unlock();
}
void JCResManager::updateSz(ResType pRes){
int size = pRes->m_nResSize;// resSize();
int ds = size - pRes->m_nUsedMem;
m_nCurSize += ds;
pRes->m_nUsedMem = size;
pRes->m_nTouch = m_nCurTick; //类似touch,如果是restore了,这个会保证自己不会被轻易释放
//如果ds<=0表示正在清理,这时候不要再freeRes了
if( ds>0 && m_nMaxSize>0 && m_nCurSize>m_nMaxSize ){
freeRes(m_nMaxSize/3);
}
}
void JCResManager::InitWorkThread(){
m_WorkThread = std::this_thread::get_id();
}
bool JCResManager::chkMemSize(){
std::unique_lock<std::recursive_mutex> _lock(m_Lock);
if( m_ActiveRes.size()<=0)
return m_nCurSize==0;
ListNode* pHead = m_ActiveRes.getHead();
ListNode* pCurNode = pHead->_Next;
int nMemSize=0;
while( pCurNode!=pHead){//越往后越新
JCResource* pRes = (JCResource*)pCurNode;
nMemSize += pRes->m_nResSize;
ListNode* pNext = pCurNode->_Next;
pCurNode=pNext;
}
return m_nCurSize==nMemSize;
}
int JCResManager::getGlobalID()
{
m_setResLock.lock();
int nID = m_nGlobalID++;
m_setResLock.unlock();
return nID;
}
void JCResManager::printfActiveTextureInfo()
{
LOGI("JCResManager::printfActiveTextureInfo currentSize=%d", m_nCurSize );
/*
listNode* pHead = m_ActiveRes.getHead();
listNode* pCurNode = pHead->_Next;
int nMemSize = 0;
while (pCurNode != pHead)
{
LayaTexture* pRes = (LayaTexture*)pCurNode;
LOGI("JCResManager printfInfo id=%d gpuID=%d type=%d width=%d height=%d textureWidth=%d textureHeight=%d size=%d", pRes->getTextureID(),pRes->m_pGpuTexture, pRes->m_nType,pRes->getWidth(),pRes->getHeight(), pRes->getTextureWidth(), pRes->getTextureHeight(), pRes->getTextureWidth() * pRes->getTextureHeight() * 4 );
listNode* pNext = pCurNode->_Next;
pCurNode = pNext;
}
*/
}
}
//------------------------------------------------------------------------------
//-----------------------------END FILE--------------------------------
+223
View File
@@ -0,0 +1,223 @@
/**
@file JCResManager.h
@brief
@author James
@version 1.0
@date 2016_5_12
*/
#ifndef __JCResManager_H__
#define __JCResManager_H__
#include <vector>
#include <map>
#include <list>
#include <thread>
#include <mutex>
#include "JCResource.h"
#define SLEEPINGAGE 300
namespace laya
{
/**
* @brief
资源管理的基类。
laya的资源管理方法介绍:
manager主要管理两个东西,
1 根据key(目前是url)来查找资源,这个随着资源的增加会一直增加,除非显式调用删除函数
2 一个活动资源列表。所谓活动资源就是正常状态的资源。与之相对的是非活动资源,即主要内存已经释放,只保留壳的资源。
这个列表是不完整的,不能用来查找资源
几个典型问题:
1.资源的创建
1) 外部自己创建,通过addRes来添加到资源管理中
2) 以通过manager的 getRes(url,true)来创建,同时会调用增加资源。
为了避免使用模板(否则不知道new什么),先不用这种方法
2.内存清理
当add或者restore后的总内存超出上限了,就需要进行内存清理。为了效率,这里的内存清理不会实时进行。
有的资源不希望删除,需要加一个bool canFree()的函数么?
如果不希望删除,则不要添加到资源管理中。
问题:
map的key都是string么?可能是起来类型么。需要用模板来表示key么
原来的mesh2d 是 url+textureGroup
如果一个资源就超出了上限怎么办
某个res增加内存的时候,不能把自己释放掉
测试
函数的多次调用
可能会出的问题
资源活动列表有多个相同的资源
*/
class JCResManager
{
public:
typedef JCResource* ResType;
typedef std::map<std::string, ResType> ResMap;
typedef std::vector<ResType> ResVector;
public:
JCResManager(int p_nFreeSize,bool bUseMap);
~JCResManager();
//设置定期清理的大小
void setFreeSize( unsigned int p_nFreeSize );
void setFreeSize( unsigned int p_nFreeSize, unsigned int p_nMaxSize );
//设置强制清理的大小
void setMaxSize(int p_nSize)
{
m_nMaxSize = p_nSize;
}
unsigned int getFreeSize() { return m_nFreeSize; }
unsigned int getMaxSize() { return m_nMaxSize; }
//返回执行了多少次free的操作,用来做效率检测
unsigned int getFreedCount() { return m_nFreedCount; }
void resetFreeCount() { m_nFreedCount = 0; }
/*
查找一个资源。
注意,为了效率不要频繁调用
如果需要创建,则改名成 getRes
*/
ResType find(const std::string& p_key );
ResType find(int nKey)
{
return m_AllResVector[nKey];
}
//这个只有一个res资源,只能处理活动列表的问题,不能根据map来查找。
//多次调用不会导致内存统计错误
void add(ResType pRes);
/*
从活动列表中删除某个资源。
并不影响map
bDel true 表示不需要资源为恢复做些工作。即这个资源以后不会用到了
*/
bool freeRes(ResType pRes, bool bDel );
/*
从map中删除。如果 bDel为true,则同时删除对象。
返回值:
true 找到这个资源了
false 没有这个资源
*/
bool delRes( const std::string& p_key, bool bDel );
bool delRes(int p_key, bool bDel);
//每帧都会调用的。
//由于resManager不会太多,所以可以每帧都调用,不用用请求列表的方式。
void tick();
//删除所有的,这个不会有回调
void destroyAll(bool bDelObj=false);
//释放所有的。会有回调
void freeAll();
//返回实际释放的大小
int freeRes(int p_nSize );
/*
pRes的大小变化了,需要重新统计。这个会引起touch
这个操作不会导致pRes被释放。
*/
void updateRes(ResType pRes );
/*
加到map中。
加到活动列表中。
*/
void setItem(ResType pRes, const char* name);
void setItem(ResType pRes, int nID);
/*
只是加到map中
*/
void addToAllRes(ResType pRes, const char* name);
void addToAllRes(ResType pRes, int nID);
void touchRes( ResType pRes, bool restoreRes=false );
void useSetResLock(bool b)
{
m_bUseSetResLock = b;
}
void chkThread(bool b)
{
m_bChkThread = b;
}
/*
统计活动列表所有对象实际占用的资源大小,与资源管理器统计的资源大小是否一致。
这个是测试用的,如果不一致,则会产生bug。
*/
bool chkMemSize();
unsigned int count()
{
return m_bUseMap?m_AllRes.size():m_AllResVector.size();
}
/*
设置当前线程为期望共工作线程。
如果设置了chkThread(true)则当关键函数在另外的线程中工作的时候,会抛异常。
*/
void InitWorkThread();
void printfActiveTextureInfo();
/*
当pRes恢复或者释放的时候都会调用到这个来更新占用内存
*/
void updateSz(ResType pRes);
int getGlobalID();
public:
bool m_bUseMap;
ResMap m_AllRes;
int m_nGlobalID;
ResVector m_AllResVector;
typedef simpList ResList;
ResList m_ActiveRes; //活动状态的资源列表。
unsigned int m_nCurTick; //需要一个心跳。
unsigned int m_nNextCleanTick; //下次清理的时间
std::recursive_mutex m_Lock;
unsigned int m_nCurSize; //当前占用的大小
unsigned int m_nFreeSize; //触发间隔清理的阀值
unsigned int m_nMaxSize; //触发强制清理的阀值
bool m_bReleasing;
int m_nID;
protected:
std::recursive_mutex m_setResLock;
bool m_bUseSetResLock; //是否使用m_setResLock. 例如如果都在一个线程,则没必要使用lock,以免影响效率。
protected:
ResType m_pUpdatingSzRes; //请求进入的资源,清理资源的时候,不会去清理这个资源。
bool m_bChkThread;
std::thread::id m_WorkThread;
ResType m_pLastTouchedRes; //记录上次touch的对象。目的是为了提高效率。例如使用大图集的人,会有很多次相邻的touch完全相同
unsigned int m_nFreedCount;
};
}
//------------------------------------------------------------------------------
#endif //__JCResManager_H__
//-----------------------------END FILE--------------------------------
+154
View File
@@ -0,0 +1,154 @@
/**
@file JCResource.cpp
@brief
@author James
@version 1.0
@date 2016_5_12
*/
#include "JCResource.h"
#include "JCResManager.h"
namespace laya{
JCResStateDispatcher::JCResStateDispatcher()
{
m_nResState = getting;
m_nErrNo = noError;
}
JCResource::JCResource()
{
m_pResManager = NULL;
m_nUsedMem=0;
m_nResSize =0;
m_nTouch = 0;
}
JCResource::~JCResource()
{
if(m_pResManager && m_nUsedMem )
{
m_pResManager->freeRes(this,true);
}
m_pResManager = NULL;
}
/*
注意:资源释放后再恢复,如果不做特殊处理,是不会再次出发onload的回调的。因为已经被删掉了。
*/
void JCResStateDispatcher::setState( JCResource::ResState p_state )
{
m_nResState = p_state;
switch(p_state)
{
case getting:
break;
case ready:
{
swap(m_allOnReadyCB, m_tmpOnReadyCB);
m_allOnReadyCB.clear();
m_allOnErrCB.clear();
int nNum = m_tmpOnReadyCB.size();
for (int i = 0; i < nNum; i++){
m_tmpOnReadyCB[i](this);
}
m_tmpOnReadyCB.clear();
}
break;
case error:
{
swap(m_allOnErrCB, m_tmpOnErrCB);
m_allOnErrCB.clear();
m_allOnReadyCB.clear();
int nNum = m_tmpOnErrCB.size();
for( int i=0; i<nNum; i++){
m_tmpOnErrCB[i](this,m_nErrNo);
}
m_tmpOnErrCB.clear();
}
break;
//如果资源被释放了,则onload和onerror都会被清掉,在恢复的时候,不会再调到对应的函数了。
//如果需要调用,就要在回调中再次添加监听
case freed:
{
swap(m_allOnFreeCB, m_tmpOnFreeCB);
m_allOnFreeCB.clear();
int nNum = m_tmpOnFreeCB.size();
for(int i=0; i<nNum; i++){
m_tmpOnFreeCB[i](this);
}
m_tmpOnFreeCB.clear();
}
break;
}
}
//资源准备好之后的回调。可以调用多次,添加多个回调
void JCResStateDispatcher::setOnReadyCB( JCResource::ResOnReadyCB cb )
{
switch( m_nResState )
{
case ready:
cb(this);
break;
case getting:
m_allOnReadyCB.push_back(cb);
break;
case freed:
case error://error的话要再次重试
m_nResState = getting;
m_allOnReadyCB.push_back(cb);
restoreRes(); //完成后需要设置state? 现在如果需要恢复资源的话,调用者自己控制
//还是这里做吧,要不总是容易忘了
//文件资源 怎么办
break;
}
}
void JCResStateDispatcher::setOnFreeCB( JCResource::ResOnReadyCB cb )
{
switch( m_nResState ){
case error:
m_nResState = getting;
case ready:
case getting:
m_allOnFreeCB.push_back(cb);
break;
case freed:
cb(this);
break;
}
}
void JCResStateDispatcher::setOnErrorCB(ResOnErrCB cb)
{
switch( m_nResState ){
case ready:
case freed:
break;
case getting:
m_allOnErrCB.push_back(cb);
break;
case error://问题,如果多次调用,例如想再重试一下,那直接调用并不是期望的
m_nResState = getting;
m_allOnErrCB.push_back(cb);
restoreRes();
//cb(this,mnErrNo );
break;
}
}
void JCResource::touch(bool p_bRestoreRes)
{
if(m_pResManager){
m_pResManager->touchRes(this,p_bRestoreRes);
}
}
void JCResource::setResSize(int sz)
{
if(sz==m_nResSize)return;
m_nResSize = sz;
if(m_pResManager){
m_pResManager->updateRes(this);
}
}
}
//------------------------------------------------------------------------------
//-----------------------------END FILE--------------------------------
+117
View File
@@ -0,0 +1,117 @@
/**
@file JCResource.h
@brief
@author James
@version 1.0
@date 2016_5_12
*/
#ifndef __JCResource_H__
#define __JCResource_H__
#include <functional>
#include <vector>
#include "../util/ListNode.h"
namespace laya
{
class JCResManager;
class JCResStateDispatcher
{
public:
enum ResState
{
getting=0,
ready=1,
freed=2,
error=3
};
enum ResError
{
noError=0,
downloadError=1,
decodeError=2
};
public:
typedef std::function<void(JCResStateDispatcher*)> ResOnReadyCB;
typedef std::function<void(JCResStateDispatcher*,int)> ResOnErrCB;
JCResStateDispatcher();
ResState getState(){return m_nResState;};
//可能会触发回调。
void setState( ResState p_state );
//资源准备好之后的回调。可以调用多次,添加多个回调
void setOnReadyCB(ResOnReadyCB cb );
//资源出错了的回调。
void setOnErrorCB(ResOnErrCB cb);
void setOnFreeCB(ResOnReadyCB cb );
virtual bool restoreRes(){return false;};
public:
//资源准备好了的回调。回调函数在执行之后就会被删除
std::vector<ResOnReadyCB > m_allOnReadyCB,m_tmpOnReadyCB;
std::vector<ResOnErrCB > m_allOnErrCB,m_tmpOnErrCB;
std::vector<ResOnReadyCB > m_allOnFreeCB,m_tmpOnFreeCB;
ResState m_nResState; //当前的状态。外部禁止修改
int m_nErrNo; //错误代码
};
class JCResource :public JCResStateDispatcher, public ListNode
{
friend class JCResManager;
public:
JCResource();
~JCResource();
//本资源占用的容量。
//这个值可能会变。
//问题:如果直接通过manager.addMemSize则不用这个。但是
// 用addMemSize的话,
// 没有地方保存资源占用的大小,在add到资源管理器之前,无法进行加减操作
// 需要到处进行加减的维护
// 用 resSize 的话
// 外部直接调用释放资源,无法通知到resMgr
// 如果是固定大小,如果是异步资源,无法得到一致的大小,会导致问题。变化的大小没有问题。
//virtual int resSize(){return mnResSize;};
//设置资源大小,如果为0的话,会被从活动列表中删除
void setResSize(int sz);
int getResSize(){ return m_nResSize;}
//清理本资源占用的内存。此对象本身不会被释放。
//p_bNeedUpdateSize 表示是否需要通知资源管理器自己状态发生变化了。如果是资源管理器调用的FreeRes,显然应该为false
// 其他情况应该为true。
// 这个参数不用了,外部调用者不应该关心占用空间的问题。如果关心的话,则自己再调用manager去管理
//p_bNeedRestore 表示是否还需要再恢复。例如当整个系统析构的时候,就不再需要了。
// 例如bitmap资源会在释放的时候生成缩略图。如果是系统要关闭了,则没有必要做这个了。
virtual void freeRes( bool p_bNeedRestore ){};
//恢复res的内容,这个是给ResMap使用的,
//当获得成功后,要通知已有的回调
//如果restore后增加占用内存,可能又会导致自己被释放掉。
virtual bool restoreRes(){return false;};
inline bool isActive(){ return !notInChain();}
virtual void touch( bool p_bRestoreRes=false);
public:
int m_nTouch; //上次活动时间
int m_nUsedMem; //当前在管理器中记录的大小。即上次统计的时候的大小。
int m_nResSize; //当前的资源的大小。这个会被随时修改。
JCResManager* m_pResManager;
};
}
//------------------------------------------------------------------------------
#endif //__JCResource_H__
//-----------------------------END FILE--------------------------------