diff --git a/app/src/main/java/com/gh/download/cache/ExoCacheManager.kt b/app/src/main/java/com/gh/download/cache/ExoCacheManager.kt new file mode 100644 index 0000000000..e769c906c6 --- /dev/null +++ b/app/src/main/java/com/gh/download/cache/ExoCacheManager.kt @@ -0,0 +1,82 @@ +package com.gh.download.cache + +import android.net.Uri +import com.gh.common.runOnIoThread +import com.gh.gamecenter.BuildConfig +import com.google.android.exoplayer2.upstream.DataSpec +import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter +import com.google.android.exoplayer2.upstream.cache.CacheDataSource +import com.google.android.exoplayer2.upstream.cache.CacheUtil +import com.google.android.exoplayer2.util.Util +import com.halo.assistant.HaloApp +import com.lightgame.utils.Utils +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import tv.danmaku.ijk.media.exo2.ExoSourceManager +import tv.danmaku.ijk.media.exo2.source.GSYExoHttpDataSource +import tv.danmaku.ijk.media.exo2.source.GSYExoHttpDataSourceFactory +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.atomic.AtomicBoolean + + +object ExoCacheManager { + + private val threads = ConcurrentHashMap() + + fun preload(videoUri: String) { + runOnIoThread { + threads[videoUri] = AtomicBoolean(false) + val dataSpec = DataSpec(Uri.parse(videoUri), 0, getContentLength(videoUri), null) + val simpleCache = ExoSourceManager.getCacheSingleInstance(HaloApp.getInstance().application, null) + val dataSourceFactory = GSYExoHttpDataSourceFactory(Util.getUserAgent(HaloApp.getInstance().application, + "ExoCacheManager"), DefaultBandwidthMeter.Builder(HaloApp.getInstance().application).build(), + GSYExoHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS, + GSYExoHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, false) + val cacheDataSource = CacheDataSource(simpleCache, dataSourceFactory.createDataSource()) + try { + CacheUtil.cache(dataSpec, simpleCache, CacheUtil.DEFAULT_CACHE_KEY_FACTORY, cacheDataSource, CacheUtil.ProgressListener { requestLength, bytesCached, newBytesCached -> + if (requestLength == bytesCached) { + threads.remove(videoUri) + } + if (BuildConfig.DEBUG) { + Utils.log("$requestLength--$bytesCached--$newBytesCached") + } + }, threads[videoUri]) + } catch (e: Exception) { + threads.remove(videoUri) + e.printStackTrace() + } + } + } + + fun cancel(videoUri: String) { + threads[videoUri]?.set(true) + } + + fun cancelAll() { + for (entry in threads.entries) { + entry.value.set(true) + } + } + + private fun getContentLength(downloadUrl: String): Long { + var contentLength = -1L + val request = Request.Builder() + .url(downloadUrl) + .build() + var response: Response? = null + try { + response = OkHttpClient.Builder().build().newCall(request).execute() + if (response!!.isSuccessful && response.body() != null) { + val length = response.body()!!.contentLength() + contentLength = if (length == 0L) -1L else length + } + } catch (e: Exception) { + e.printStackTrace() + } finally { + response?.close() + } + return contentLength + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/MainActivity.java b/app/src/main/java/com/gh/gamecenter/MainActivity.java index a48e3ef255..af26581008 100644 --- a/app/src/main/java/com/gh/gamecenter/MainActivity.java +++ b/app/src/main/java/com/gh/gamecenter/MainActivity.java @@ -91,6 +91,7 @@ import com.gh.gamecenter.retrofit.Response; import com.gh.gamecenter.retrofit.RetrofitManager; import com.gh.gamecenter.suggest.SuggestSelectFragment; import com.gh.gamecenter.suggest.SuggestType; +import com.google.android.exoplayer2.upstream.cache.Cache; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.halo.assistant.HaloApp; @@ -140,6 +141,7 @@ import io.reactivex.schedulers.Schedulers; import okhttp3.MediaType; import okhttp3.RequestBody; import okhttp3.ResponseBody; +import tv.danmaku.ijk.media.exo2.ExoSourceManager; import static com.gh.common.util.EntranceUtils.ENTRANCE_BROWSER; import static com.gh.common.util.EntranceUtils.HOST_QQ; @@ -284,7 +286,7 @@ public class MainActivity extends BaseActivity { //启动app删除视频缓存文件 AppExecutor.getIoExecutor().execute(() -> { try { - File cacheFileDirectory = StorageUtils.getIndividualCacheDirectory(this); + /*File cacheFileDirectory = StorageUtils.getIndividualCacheDirectory(this); if (cacheFileDirectory.exists() && cacheFileDirectory.isDirectory()) { for (File file : cacheFileDirectory.listFiles()) { FileUtils.deleteFile(file.getPath()); @@ -299,7 +301,9 @@ public class MainActivity extends BaseActivity { if (!noMediaFile.exists()) { noMediaFile.createNewFile(); - } + }*/ + String dirPath = getCacheDir().getAbsolutePath() + File.separator + "exo"; + FileUtils.deleteFolder(new File(dirPath)); } catch (Exception e) { e.printStackTrace(); } diff --git a/app/src/main/java/com/gh/gamecenter/video/detail/CustomManager.java b/app/src/main/java/com/gh/gamecenter/video/detail/CustomManager.java index 3e323fc939..456a0529d6 100644 --- a/app/src/main/java/com/gh/gamecenter/video/detail/CustomManager.java +++ b/app/src/main/java/com/gh/gamecenter/video/detail/CustomManager.java @@ -7,7 +7,11 @@ import android.view.ViewGroup; import android.view.Window; import com.gh.gamecenter.R; +import com.google.android.exoplayer2.DefaultLoadControl; +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.shuyu.gsyvideoplayer.GSYVideoBaseManager; +import com.shuyu.gsyvideoplayer.model.GSYModel; +import com.shuyu.gsyvideoplayer.player.IPlayerInitSuccessListener; import com.shuyu.gsyvideoplayer.player.IPlayerManager; import com.shuyu.gsyvideoplayer.utils.CommonUtil; @@ -15,6 +19,8 @@ import java.util.HashMap; import java.util.Map; import tv.danmaku.ijk.media.exo2.Exo2PlayerManager; +import tv.danmaku.ijk.media.exo2.IjkExo2MediaPlayer; +import tv.danmaku.ijk.media.player.IMediaPlayer; import static com.shuyu.gsyvideoplayer.utils.CommonUtil.hideNavKey; @@ -33,6 +39,10 @@ public class CustomManager extends GSYVideoBaseManager { private static Map sMap = new HashMap<>(); + private static final int DEFAULT_MIN_BUFFER_MS = 5000; + + private static final int DEFAULT_MAX_BUFFER_MS = 20000; + public CustomManager() { init(); @@ -102,6 +112,16 @@ public class CustomManager extends GSYVideoBaseManager { CustomManager customManager = sMap.get(key); if (customManager == null) { customManager = new CustomManager(); + customManager.setPlayerInitSuccessListener((player, model) -> { + if (player instanceof IjkExo2MediaPlayer) { + DefaultLoadControl defaultLoadControl = new DefaultLoadControl.Builder(). + setBufferDurationsMs(DEFAULT_MIN_BUFFER_MS, DEFAULT_MAX_BUFFER_MS, + DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS, + DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS) + .createDefaultLoadControl(); + ((IjkExo2MediaPlayer) player).setLoadControl(defaultLoadControl); + } + }); sMap.put(key, customManager); } return customManager; @@ -131,7 +151,7 @@ public class CustomManager extends GSYVideoBaseManager { public static void onResumeAll(boolean seek) { if (sMap.size() > 0) { for (Map.Entry header : sMap.entrySet()) { - onResume(header.getKey(),seek); + onResume(header.getKey(), seek); } } } diff --git a/app/src/main/java/com/gh/gamecenter/video/detail/VideoAdapter.kt b/app/src/main/java/com/gh/gamecenter/video/detail/VideoAdapter.kt index 58d7a2a701..d4813c7420 100644 --- a/app/src/main/java/com/gh/gamecenter/video/detail/VideoAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/video/detail/VideoAdapter.kt @@ -20,6 +20,7 @@ import com.gh.common.util.* import com.gh.download.DownloadManager import com.gh.download.cache.CacheManager import com.gh.download.cache.CacheObserver +import com.gh.download.cache.ExoCacheManager import com.gh.gamecenter.R import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.entity.PluginLocation @@ -115,10 +116,10 @@ class VideoAdapter(val mContext: Context, val mRecyclerView: RecyclerView, val m ?: false if (position == mViewModel.startPosition) { if (position + 2 <= videoList.size - 1) {//预加载视频 - CacheManager.getInstance().download(videoList[position + 1].url, object : CacheObserver() {}) - CacheManager.getInstance().download(videoList[position + 2].url, object : CacheObserver() {}) + ExoCacheManager.preload(videoList[position + 1].url) + ExoCacheManager.preload(videoList[position + 2].url) } else if (position + 1 <= videoList.size - 1) { - CacheManager.getInstance().download(videoList[position + 1].url, object : CacheObserver() {}) + ExoCacheManager.preload(videoList[position + 1].url) } videoView.startButton.performClick() LogUtils.uploadVideoStreamingPlaying("开始播放-入口进入", "", mViewModel.path, diff --git a/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailContainerFragment.kt b/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailContainerFragment.kt index 974093fc67..f69b98885b 100644 --- a/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailContainerFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailContainerFragment.kt @@ -25,6 +25,7 @@ import com.gh.common.view.vertical_recycler.PagerLayoutManager import com.gh.download.DownloadManager import com.gh.download.cache.CacheManager import com.gh.download.cache.CacheObserver +import com.gh.download.cache.ExoCacheManager import com.gh.gamecenter.R import com.gh.gamecenter.WebActivity import com.gh.gamecenter.entity.LinkEntity @@ -210,7 +211,7 @@ class VideoDetailContainerFragment : BaseLazyFragment(), OnBackPressedListener { val video = mViewModel.videoList.value?.get(position) video?.let { - CacheManager.getInstance().cancel(video.url) + ExoCacheManager.cancel(video.url) val proxy = CustomProxyCacheManager.getProxy(requireContext(), null) val client = proxy.getClients(video.url) runOnIoThread { client.closeSource() } @@ -260,13 +261,13 @@ class VideoDetailContainerFragment : BaseLazyFragment(), OnBackPressedListener { mScheduledPlayDisposable?.dispose() mScheduledPlayDisposable = null if (pos + 2 <= mAdapter.videoList.size - 1) {//预加载视频 - CacheManager.getInstance().download(mAdapter.videoList[pos + 1].url, object : CacheObserver() {}) - CacheManager.getInstance().download(mAdapter.videoList[pos + 2].url, object : CacheObserver() {}) + ExoCacheManager.preload(mAdapter.videoList[pos + 1].url) + ExoCacheManager.preload(mAdapter.videoList[pos + 2].url) } else if (pos + 1 <= mAdapter.videoList.size - 1) { - CacheManager.getInstance().download(mAdapter.videoList[pos + 1].url, object : CacheObserver() {}) + ExoCacheManager.preload(mAdapter.videoList[pos + 1].url) } if (mLastPosition != pos /*&& videoView?.isInPlayingState != true*/) { - CacheManager.getInstance().cancel(mAdapter.videoList[pos].url) + ExoCacheManager.cancel(mAdapter.videoList[pos].url) CustomManager.releaseAllVideos("detail_${mViewModel.uuid}") videoView?.startButton?.performClick() mBaseHandler.postDelayed({ @@ -481,21 +482,14 @@ class VideoDetailContainerFragment : BaseLazyFragment(), OnBackPressedListener { } SPUtils.setBoolean(Constants.SP_SHOW_SLIDE_GUIDE, true) - val proxy = CustomProxyCacheManager.getProxy(requireContext(), null) + /* val proxy = CustomProxyCacheManager.getProxy(requireContext(), null) runOnIoThread { proxy.allClients.forEach { it.value.closeSource() } - } - /* proxy.shutdown() - tryWithDefaultCatch { - val field = HttpProxyCacheServer::class.java.getDeclaredField("serverSocket") - field.isAccessible = true - val serverSocket = field.get(proxy) as ServerSocket - serverSocket.close() - }*/ + }*/ super.onPause() - CacheManager.getInstance().removeAllCall() + ExoCacheManager.cancelAll() } override fun onDestroyView() { @@ -508,7 +502,7 @@ class VideoDetailContainerFragment : BaseLazyFragment(), OnBackPressedListener { mAdCountDownTimer?.dispose() } super.onDestroyView() - CacheManager.getInstance().removeAllCall() + ExoCacheManager.cancelAll() } private fun findFirstCompletelyVisibleVideoViewByPosition(): DetailPlayerView? { diff --git a/app/src/main/java/com/halo/assistant/HaloApp.java b/app/src/main/java/com/halo/assistant/HaloApp.java index 15cdbb5ef3..703694dee4 100644 --- a/app/src/main/java/com/halo/assistant/HaloApp.java +++ b/app/src/main/java/com/halo/assistant/HaloApp.java @@ -49,6 +49,7 @@ import androidx.lifecycle.ProcessLifecycleOwner; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import tv.danmaku.ijk.media.exo2.Exo2PlayerManager; +import tv.danmaku.ijk.media.exo2.ExoPlayerCacheManager; public class HaloApp extends TinkerAppLike { @@ -180,7 +181,8 @@ public class HaloApp extends TinkerAppLike { } }); PlayerFactory.setPlayManager(Exo2PlayerManager.class); - CacheFactory.setCacheManager(CustomProxyCacheManager.class); + CacheFactory.setCacheManager(ExoPlayerCacheManager.class); + //CacheFactory.setCacheManager(CustomProxyCacheManager.class); } // 3.5 开始将 targetSdk 升级至 26,原来写在 Manifest 的部分 receiver 由于系统限制需要换成在运行时注册 diff --git a/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.java b/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.java index 213bfca889..7363766133 100644 --- a/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.java +++ b/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.java @@ -55,6 +55,7 @@ import java.io.File; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; + import butterknife.BindView; import butterknife.OnClick; import io.reactivex.Observable; @@ -181,7 +182,7 @@ public class SettingsFragment extends NormalFragment { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) { return; } - + mSettingUsageStatsSb.setClickable(false); if (UsageStatsHelper.checkForPermission() && sp.getBoolean(UsageStatsHelper.USAGE_STATUS_SP_KEY, true)) { @@ -265,7 +266,7 @@ public class SettingsFragment extends NormalFragment { private long getFolderSize(File folder) { long size = 0; //忽略视频缓存 - if (folder.getName().equals("video-cache")) return size; + if (folder.getName().equals("video-cache") || folder.getName().equals("exo")) return size; size += folder.length(); if (folder.isDirectory()) { File[] files = folder.listFiles(); @@ -293,7 +294,7 @@ public class SettingsFragment extends NormalFragment { @OnClick({ R.id.setting_cv_fix, R.id.setting_rl_autoinstall, - R.id.setting_rl_cache,R.id.setting_rl_traffic_video, + R.id.setting_rl_cache, R.id.setting_rl_traffic_video, R.id.setting_rl_concerngame, R.id.setting_rl_about, R.id.setting_logout_rl, R.id.setting_network_diagnosis, R.id.setting_privacy_policy, R.id.setting_user_protocol, @@ -391,15 +392,15 @@ public class SettingsFragment extends NormalFragment { case R.id.setting_usage_stats: if (mSettingUsageStatsSb.isChecked()) { DialogUtils.showAlertDialog(getContext(), - "提示", - "关闭后将无法统计游戏时长,确定要关闭吗?", - "确定关闭", - "暂不关闭", - () -> { - mSettingUsageStatsSb.setChecked(false); - sp.edit().putBoolean(UsageStatsHelper.USAGE_STATUS_SP_KEY, false).apply(); - }, - () -> mSettingUsageStatsSb.setCheckedNoEvent(true) + "提示", + "关闭后将无法统计游戏时长,确定要关闭吗?", + "确定关闭", + "暂不关闭", + () -> { + mSettingUsageStatsSb.setChecked(false); + sp.edit().putBoolean(UsageStatsHelper.USAGE_STATUS_SP_KEY, false).apply(); + }, + () -> mSettingUsageStatsSb.setCheckedNoEvent(true) ); } else { mSettingUsageStatsSb.performClick();