open source
This commit is contained in:
@@ -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--------------------------------
|
||||
@@ -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--------------------------------
|
||||
@@ -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--------------------------------
|
||||
@@ -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--------------------------------
|
||||
Reference in New Issue
Block a user