如何修改ExoPlayer代码将DASH视频下载为单MP4文件?
问题描述
我想用ExoPlayer下载DASH格式视频,但当前代码执行后会生成多个文件。我怀疑问题出在dataSourceFactory上,试过替换成DefaultDataSourceFactory但没成功。现有代码如下,请问怎么修改才能让保存的文件是MP4格式?
import android.app.Notification import android.os.Environment import android.util.Log import ar.com.flow.iot.R import com.google.android.exoplayer2.database.StandaloneDatabaseProvider import com.google.android.exoplayer2.offline.Download import com.google.android.exoplayer2.offline.DownloadManager import com.google.android.exoplayer2.offline.DownloadService import com.google.android.exoplayer2.scheduler.Scheduler import com.google.android.exoplayer2.ui.DownloadNotificationHelper import com.google.android.exoplayer2.upstream.DefaultHttpDataSource import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor import com.google.android.exoplayer2.upstream.cache.SimpleCache import java.io.File import java.util.concurrent.Executor class MyDownloadService : DownloadService( FOREGROUND_NOTIFICATION_ID, 1000, DOWNLOAD_NOTIFICATION_CHANNEL_ID, R.string.exo_download_notification_channel_name, 0 ) { override fun getDownloadManager(): DownloadManager { Log.w("TAGGG_DOWNLOAD", "getDownloadManager") val databaseProvider = StandaloneDatabaseProvider(this) val downloadsDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) val cacheDirectory = File(downloadsDirectory, "media_cache") val cacheEvictionPolicy = LeastRecentlyUsedCacheEvictor(50 * 1024 * 1024) // 50 MB val downloadCache = SimpleCache( cacheDirectory, cacheEvictionPolicy, databaseProvider ) val dataSourceFactory = DefaultHttpDataSource.Factory() val downloadExecutor = Executor { obj: Runnable -> obj.run() } val downloadManager = DownloadManager( this, databaseProvider, downloadCache, dataSourceFactory, downloadExecutor ) // Optionally, setters can be called to configure the download manager. //downloadManager.requirements = requirements; downloadManager.maxParallelDownloads = 3; return downloadManager } override fun getScheduler(): Scheduler? { Log.e("TAGGG_DOWNLOAD", "Falta implementar getScheduler ") return null } override fun getForegroundNotification( downloads: MutableList<Download>, notMetRequirements: Int ): Notification { Log.w("TAGGG_DOWNLOAD", "getForegroundNotification $downloads $notMetRequirements ") return DownloadNotificationHelper(this, DOWNLOAD_NOTIFICATION_CHANNEL_ID).buildProgressNotification( this, R.drawable.ic_icon_help, null, null, downloads, notMetRequirements ) } companion object { private const val FOREGROUND_NOTIFICATION_ID = 1 const val DOWNLOAD_NOTIFICATION_CHANNEL_ID = "download channel id" } } private fun downloadVideo(uri:Uri) { val downloadRequest = DownloadRequest.Builder(id, uri).build() DownloadService.sendAddDownload( this, MyDownloadService::class.java, downloadRequest, false ) }
解决方案
ExoPlayer默认对DASH这类自适应流媒体采用分片缓存机制,会生成多个小文件。要输出单MP4文件,需从数据源配置、下载请求、缓存管理三个维度修改:
1. 替换数据源工厂为CacheDataSourceFactory
DefaultHttpDataSource.Factory仅处理HTTP请求,无法支持分片合并。需改用DefaultDataSourceFactory结合CacheDataSourceFactory,实现缓存与合并的完整流程:
override fun getDownloadManager(): DownloadManager { Log.w("TAGGG_DOWNLOAD", "getDownloadManager") val databaseProvider = StandaloneDatabaseProvider(this) // 优先用应用私有目录,避免权限问题;若需公共目录可替换回原路径 val downloadsDirectory = getExternalFilesDir(Environment.DIRECTORY_MOVIES) ?: Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES) // 缓存目录用于临时存储分片,最终合并后的MP4会输出到downloadsDirectory val cacheDirectory = File(downloadsDirectory, "exo_cache") val cacheEvictionPolicy = LeastRecentlyUsedCacheEvictor(50 * 1024 * 1024) // 50 MB val downloadCache = SimpleCache(cacheDirectory, cacheEvictionPolicy, databaseProvider) // 创建完整的数据源工厂 val userAgent = com.google.android.exoplayer2.util.Util.getUserAgent(this, "YourAppName") val defaultDataSourceFactory = com.google.android.exoplayer2.upstream.DefaultDataSourceFactory( this, userAgent ) val cacheDataSourceFactory = com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory( downloadCache, defaultDataSourceFactory, com.google.android.exoplayer2.upstream.cache.CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR ) val downloadExecutor = Executor { obj: Runnable -> obj.run() } val downloadManager = DownloadManager( this, databaseProvider, downloadCache, cacheDataSourceFactory, // 替换为CacheDataSourceFactory downloadExecutor ) downloadManager.maxParallelDownloads = 3; return downloadManager }
2. 配置DownloadRequest指定MP4输出格式
创建下载请求时,需明确指定输出格式为MP4,并选择MP4兼容的媒体轨道(H.264视频+AAC音频):
private fun downloadVideo(uri: Uri) { // 构建轨道选择参数,指定MP4兼容的编码格式 val trackSelector = com.google.android.exoplayer2.trackselection.DefaultTrackSelector(this) val trackSelectionParameters = trackSelector.parameters .buildUpon() .setPreferredVideoMimeType(com.google.android.exoplayer2.util.MimeTypes.VIDEO_H264) .setPreferredAudioMimeType(com.google.android.exoplayer2.util.MimeTypes.AUDIO_AAC) .build() val downloadRequest = com.google.android.exoplayer2.offline.DownloadRequest.Builder(id, uri) .setTrackSelectionParameters(trackSelectionParameters) .setOutputFileFormat(com.google.android.exoplayer2.offline.DownloadRequest.OUTPUT_FILE_FORMAT_MP4) .build() DownloadService.sendAddDownload( this, MyDownloadService::class.java, downloadRequest, false ) }
3. 权限适配
- Android 10及以下:需申请
WRITE_EXTERNAL_STORAGE权限 - Android 11+:建议使用应用私有目录(
getExternalFilesDir)避免权限申请;若需存储到公共目录,需申请MANAGE_EXTERNAL_STORAGE权限
内容的提问来源于stack exchange,提问作者Alejandro Giraudo
相关产品推荐
相关产品推荐

