从ZipResourceFile加载视频

时间:2017-11-29 12:17:09

标签: exoplayer apk-expansion-files

我想从未压缩的zip APK扩展文件(ZipResourceFile - OBB)将视频和音频文件加载到SimpleExoPlayer。通常我创建一个MediaSource对象并将其传递给播放器,但是当我使用ZipResourceFile时,我无法获取媒体文件的Uri

MediaSource mediaSource = new ExtractorMediaSource(mediaUri, new DefaultDataSourceFactory(this, userAgent), new DefaultExtractorsFactory(), null, null);

mSimpleExoPlayer.prepare(mediaSource);
mSimpleExoPlayer.setPlayWhenReady(true);

由于

1 个答案:

答案 0 :(得分:1)

有点晚了,但我希望这对某些人有用。

我的解决方案是创建一个自定义DataSource以将其用作ExtractorMediaSource的源。我已经复制了ExoPlayer的AssetDataSource实现并对其进行了修改,以便从APK扩展文件中获取数据。

<强> OBBDataSource.java

import android.content.Context;
import android.net.Uri;
import android.util.Log;

import com.android.vending.expansion.zipfile.APKExpansionSupport;
import com.android.vending.expansion.zipfile.ZipResourceFile;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.TransferListener;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;

/**
 * A {@link DataSource} for reading from the APK Expansion file
 */
public final class OBBDataSource implements DataSource {

    /**
     * Thrown when an {@link IOException} is encountered reading an OBB asset.
     */
    public static final class OBBDataSourceException extends IOException {

        public OBBDataSourceException(IOException cause) {
            super(cause);
        }

    }


    private final TransferListener<? super OBBDataSource> listener;

    private Uri uri;
    private InputStream inputStream;
    private long bytesRemaining;
    private boolean opened;
    private Context context;

    private String TAG = "OBB_DATA_SOURCE";

    /**
     * @param context A context.
     */
    public OBBDataSource(Context context) {
        this(context, null);
        this.context = context;
    }

    /**
     * @param context A context.
     * @param listener An optional listener.
     */
    public OBBDataSource(Context context, TransferListener<? super OBBDataSource> listener) {
        this.context = context;
        this.listener = listener;
    }

    @Override
    public long open(DataSpec dataSpec) throws OBBDataSourceException {
        try {
            uri = dataSpec.uri;
            String path = uri.getPath();
            if (path.startsWith("/android_asset/")) {
                path = path.substring(15);
            } else if (path.startsWith("/")) {
                path = path.substring(1);
            }
            inputStream = getInputStreamFromExpansionAPKFile(path);
            long skipped = inputStream.skip(dataSpec.position);
            if (skipped < dataSpec.position) {
                // assetManager.open() returns an AssetInputStream, whose skip() implementation only skips
                // fewer bytes than requested if the skip is beyond the end of the asset's data.
                throw new EOFException();
            }
            if (dataSpec.length != C.LENGTH_UNSET) {
                bytesRemaining = dataSpec.length;
            } else {
                bytesRemaining = inputStream.available();
                if (bytesRemaining == Integer.MAX_VALUE) {
                    // assetManager.open() returns an AssetInputStream, whose available() implementation
                    // returns Integer.MAX_VALUE if the remaining length is greater than (or equal to)
                    // Integer.MAX_VALUE. We don't know the true length in this case, so treat as unbounded.
                    bytesRemaining = C.LENGTH_UNSET;
                }
            }
        } catch (IOException e) {
            throw new OBBDataSourceException(e);
        }

        opened = true;
        if (listener != null) {
            listener.onTransferStart(this, dataSpec);
        }
        return bytesRemaining;
    }

    @Override
    public int read(byte[] buffer, int offset, int readLength) throws OBBDataSourceException {
        if (readLength == 0) {
            return 0;
        } else if (bytesRemaining == 0) {
            return C.RESULT_END_OF_INPUT;
        }

        int bytesRead;
        try {
            int bytesToRead = bytesRemaining == C.LENGTH_UNSET ? readLength
                    : (int) Math.min(bytesRemaining, readLength);
            bytesRead = inputStream.read(buffer, offset, bytesToRead);
        } catch (IOException e) {
            throw new OBBDataSourceException(e);
        }

        if (bytesRead == -1) {
            if (bytesRemaining != C.LENGTH_UNSET) {
                // End of stream reached having not read sufficient data.
                throw new OBBDataSourceException(new EOFException());
            }
            return C.RESULT_END_OF_INPUT;
        }
        if (bytesRemaining != C.LENGTH_UNSET) {
            bytesRemaining -= bytesRead;
        }
        if (listener != null) {
            listener.onBytesTransferred(this, bytesRead);
        }
        return bytesRead;
    }

    @Override
    public Uri getUri() {
        return uri;
    }

    @Override
    public void close() throws OBBDataSourceException {
        uri = null;
        try {
            if (inputStream != null) {
                inputStream.close();
            }
        } catch (IOException e) {
            throw new OBBDataSourceException(e);
        } finally {
            inputStream = null;
            if (opened) {
                opened = false;
                if (listener != null) {
                    listener.onTransferEnd(this);
                }
            }
        }
    }


    private InputStream getInputStreamFromExpansionAPKFile(String fileName){

        // Get a ZipResourceFile representing a merger of both the main and patch files
        try {
            ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(context, BuildConfig.OBB_FILE_VERSION , -1);
            return expansionFile.getInputStream(fileName);


        } catch (IOException e) {
            Log.w(TAG, "Failed to find expansion file", e);
            return null;
        }

    }

}

示例,如何使用它(Kotlin代码)

        val obbDataSource = OBBDataSource(context)

        val dataSource = ExtractorMediaSource.Factory{
           obbDataSource
        }


        val videoPath = name + VIDEO_FILE_EXTENSION


        val videoMediaSource : MediaSource


        val mediaSource = dataSource.createMediaSource(Uri.parse(videoPath))