package com.media.cache; import android.net.Uri; import android.text.TextUtils; import com.android.baselib.utils.LogUtils; import com.media.cache.hls.M3U8; import com.media.cache.hls.M3U8Utils; import com.media.cache.listener.IVideoInfoCallback; import com.media.cache.listener.IVideoInfoParseCallback; import com.media.cache.model.Video; import com.media.cache.model.VideoCacheInfo; import com.media.cache.utils.DownloadExceptionUtils; import com.media.cache.utils.HttpUtils; import com.media.cache.utils.LocalProxyThreadUtils; import com.media.cache.utils.LocalProxyUtils; import java.io.File; import java.util.HashMap; public class VideoInfoParserManager { private static VideoInfoParserManager sInstance; private LocalProxyConfig mConfig; public static VideoInfoParserManager getInstance() { if (sInstance == null) { synchronized (VideoInfoParserManager.class) { if (sInstance == null) { sInstance = new VideoInfoParserManager(); } } } return sInstance; } public void initConfig(LocalProxyConfig config) { mConfig = config; } public synchronized void parseVideoInfo(VideoCacheInfo info, IVideoInfoCallback callback, HashMap<String, String> headers) { LocalProxyThreadUtils.submitRunnableTask(() -> doParseVideoInfoTask(info, callback, headers)); } private void doParseVideoInfoTask(VideoCacheInfo info, IVideoInfoCallback callback, HashMap<String, String> headers) { try { if (info == null) { callback.onBaseVideoInfoFailed(new Throwable("Video info is null.")); return; } if (!HttpUtils.matchHttpSchema(info.getUrl())) { callback.onBaseVideoInfoFailed(new Throwable("Can parse the request resource's schema.")); return; } String finalUrl = info.getUrl(); LogUtils.d("doParseVideoInfoTask finalUrl="+finalUrl); //Redirect is enabled, send redirect request to get final location. if (mConfig.isRedirect()) { finalUrl = HttpUtils.getFinalUrl(mConfig, info.getUrl(), headers); if (TextUtils.isEmpty(finalUrl)) { callback.onBaseVideoInfoFailed(new Throwable("FinalUrl is null.")); return; } callback.onFinalUrl(finalUrl); } info.setFinalUrl(finalUrl); Uri uri = Uri.parse(finalUrl); String fileName = uri.getLastPathSegment(); LogUtils.d("parseVideoInfo fileName = " + fileName); //By suffix name. if (fileName != null) { fileName = fileName.toLowerCase(); if (fileName.endsWith(".m3u8")) { parseM3U8Info(info, callback, headers); return; } else if (fileName.endsWith(".mp4")) { LogUtils.i("parseVideoInfo MP4_TYPE"); info.setVideoType(Video.Type.MP4_TYPE); callback.onBaseVideoInfoSuccess(info); return; } else if (fileName.endsWith(".mov")) { LogUtils.i("parseVideoInfo QUICKTIME_TYPE"); info.setVideoType(Video.Type.QUICKTIME_TYPE); callback.onBaseVideoInfoSuccess(info); return; } else if (fileName.endsWith(".webm")) { LogUtils.i("parseVideoInfo WEBM_TYPE"); info.setVideoType(Video.Type.WEBM_TYPE); callback.onBaseVideoInfoSuccess(info); return; } else if (fileName.endsWith(".3gp")) { LogUtils.i("parseVideoInfo GP3_TYPE"); info.setVideoType(Video.Type.GP3_TYPE); callback.onBaseVideoInfoSuccess(info); return; } } String mimeType = null; //Add more video mimeType. mimeType = HttpUtils.getMimeType(mConfig, finalUrl, headers); LogUtils.i("parseVideoInfo mimeType="+mimeType); if (mimeType != null) { mimeType = mimeType.toLowerCase(); if (mimeType.contains(Video.Mime.MIME_TYPE_MP4)) { LogUtils.i("parseVideoInfo MP4_TYPE"); info.setVideoType(Video.Type.MP4_TYPE); callback.onBaseVideoInfoSuccess(info); } else if (isM3U8Mimetype(mimeType)) { parseM3U8Info(info, callback, headers); } else if (mimeType.contains(Video.Mime.MIME_TYPE_WEBM)) { LogUtils.i("parseVideoInfo QUICKTIME_TYPE"); info.setVideoType(Video.Type.WEBM_TYPE); callback.onBaseVideoInfoSuccess(info); } else if (mimeType.contains(Video.Mime.MIME_TYPE_QUICKTIME)) { LogUtils.i("parseVideoInfo WEBM_TYPE"); info.setVideoType(Video.Type.QUICKTIME_TYPE); callback.onBaseVideoInfoSuccess(info); } else if (mimeType.contains(Video.Mime.MIME_TYPE_3GP)) { LogUtils.i("parseVideoInfo GP3_TYPE"); info.setVideoType(Video.Type.GP3_TYPE); callback.onBaseVideoInfoSuccess(info); } else if (mimeType.contains(Video.Mime.MIME_TYPE_MP3)){ info.setVideoType(Video.Type.MP3_TYPE); callback.onBaseVideoInfoSuccess(info); } else { callback.onBaseVideoInfoFailed(new VideoCacheException(DownloadExceptionUtils.MIMETYPE_NOT_FOUND_STRING)); } } else { callback.onBaseVideoInfoFailed(new VideoCacheException(DownloadExceptionUtils.MIMETYPE_NULL_ERROR_STRING)); } } catch (Exception e) { callback.onBaseVideoInfoFailed(e); } } private boolean isM3U8Mimetype(String mimeType) { return mimeType.contains(Video.Mime.MIME_TYPE_M3U8_1) || mimeType.contains(Video.Mime.MIME_TYPE_M3U8_2) || mimeType.contains(Video.Mime.MIME_TYPE_M3U8_3) || mimeType.contains(Video.Mime.MIME_TYPE_M3U8_4); } private void parseM3U8Info(VideoCacheInfo info, IVideoInfoCallback callback, HashMap<String, String> headers) { try { M3U8 m3u8 = M3U8Utils.parseM3U8Info(mConfig, info.getUrl(), false, null); //HLS LIVE video cannot be proxy cached. if (m3u8.hasEndList()) { String saveName = LocalProxyUtils.computeMD5(info.getUrl()); File dir = new File(mConfig.getCacheRoot(), saveName); if (!dir.exists()) { dir.mkdir(); } M3U8Utils.createRemoteM3U8(dir, m3u8); info.setSaveDir(dir.getAbsolutePath()); info.setVideoType(Video.Type.HLS_TYPE); callback.onM3U8InfoSuccess(info, m3u8); } else { info.setVideoType(Video.Type.HLS_LIVE_TYPE); callback.onLiveM3U8Callback(info); } } catch (Exception e) { callback.onM3U8InfoFailed(e); } } public void parseM3U8File(VideoCacheInfo info, IVideoInfoParseCallback callback) { File remoteM3U8File = new File(info.getSaveDir(), "remote.m3u8"); if (!remoteM3U8File.exists()) { callback.onM3U8FileParseFailed(info, new Throwable("Cannot find remote.m3u8 file.")); return; } try { M3U8 m3u8 = M3U8Utils.parseM3U8Info(mConfig, info.getUrl(), true, remoteM3U8File); callback.onM3U8FileParseSuccess(info, m3u8); } catch (Exception e) { callback.onM3U8FileParseFailed(info, e); } } }