Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refresh Media source in exoplayer

I am using Exo Player ExtractorMediaSource for playing video in my android app. I am downloading media from server and save in local database and on a specific time Alarm i play this media using ConcatenatingMediaSource in exo player. but first i check that all video file downloaded or not and start player with downloaded media source . and if any video is not downloaded then i want to download it in background at when it downloaded then i want to add this video in my already created playlist

This is sample code

  private void playAndUpdateVideo(ArrayList<String> mediaSourc) {



        simpleExoPlayerView.setVisibility(View.VISIBLE);
        simpleExoPlayerView.setDefaultArtwork(null);

        mainHandler = new Handler();
        DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
        TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveVideoTrackSelection.Factory(bandwidthMeter);
        TrackSelector trackSelector = new DefaultTrackSelector( videoTrackSelectionFactory);
        dataSourceFactory = new DefaultDataSourceFactory(context,
                Util.getUserAgent(context, "com.cloveritservices.hype"), bandwidthMeter);
// 2. Create a default LoadControl
        extractorsFactory = new DefaultExtractorsFactory();
        LoadControl loadControl = new DefaultLoadControl();

// 3. Create the player
        player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl);
        player.addListener(this);

//Set media controller
        simpleExoPlayerView.setUseController(false);
        simpleExoPlayerView.requestFocus();
// Bind the player to the view.
        simpleExoPlayerView.setPlayer(player);




        MediaSource[] mediaSources = new MediaSource[mediaSourc.size()];
        for (int i=0;i<mediaSourc.size();i++)
        {

            mediaSources[i]= buildMediaSource(Uri.parse(mediaSourc.get(i)));

        }

        MediaSource mediaSource = mediaSources.length == 1 ? mediaSources[0]
                : new ConcatenatingMediaSource(mediaSources);
        LoopingMediaSource loopingSource = new LoopingMediaSource(mediaSource);
        player.prepare(loopingSource);
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
        boolean isChecked = settings.getBoolean("switch", false);
        if (!isChecked)
        player.setVolume(0f);
        else player.setVolume(2f);
        player.setPlayWhenReady(true);

    }

And here i am checking for video file that it is downloaded or not

 if (CommonUtils.isExternalStorageExistAndWritable()) {
                for (int i = 0; i < videoUrl.size(); i++) {


                    if (!new File(Environment.getExternalStorageDirectory().toString() + Constants.PROFILE_VIDEO_FOLDER + CommonUtils.fileFromUrl(videoUrl.get(i))).exists() && !CommonUtils.currentlyDownloading(context,CommonUtils.fileFromUrl(videoUrl.get(i)))) {
                        downloadByDownloadManager(videoUrl.get(i), CommonUtils.fileFromUrl(videoUrl.get(i)));
                        if (flag==Constants.FLAG_PLAY){downloadFlag=true;}
                    }
                }

            } else {
                Toast.makeText(getApplicationContext(), "SD Card not mounted.Please Mount SD Card", Toast.LENGTH_SHORT).show();
            }
            if (flag==Constants.FLAG_PLAY && !downloadFlag)
            {
                playAndUpdateVideo(videoUrl);
            }

  public void downloadByDownloadManager(String url, String fileName1) {
        downloadUrl=url;
        fileName=fileName1;
        request = new DownloadManager.Request(Uri.parse(url));
        request.setDescription("video file");
        request.setTitle(fileName);

        request.setNotificationVisibility(2);
        request.allowScanningByMediaScanner();

                    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
                    request.setDestinationInExternalPublicDir(Constants.PROFILE_VIDEO_FOLDER, fileName);
                    DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);

                    manager.enqueue(request);







        // get download service and enqueue file



    }

Please help that how to add missing video file later to playlist if it is not downloaded.

like image 803
Ghanshyam Sharma Avatar asked Aug 17 '17 05:08

Ghanshyam Sharma


People also ask

How do you play the next ExoPlayer video?

1 Answer. Show activity on this post. 3] Just check by clicking next button from the media controller if that works then you are done, now the videos will be played automatically once finished the current one.

What is Google's ExoPlayer?

ExoPlayer is an app-level media player built on top of low-level media APIs in Android. It is an open source project used by Google apps, including YouTube and Google TV.


1 Answers

To add new video files to your playlist, you need a new MediaSource implementation which can handle a list of sources to enable resizing. This is fairly simple to achieve, the easiest way to do so is to create a modified implementation of ConcaternatingMediaSource which uses lists instead of arrays to store and iterate over media sources. You then replace the ConcaternatingMediaSource in playAndUpdateVideo with the new implementation using lists. This will allow you to add and remove from your playlist as you wish, you can append new media files when your download complete listener is triggered. Here is the full class for a media source implementation using lists:

public final class DynamicMediaSource implements MediaSource {

    private List<MediaSource> mediaSources;
    private SparseArray<Timeline> timelines;
    private SparseArray<Object> manifests;
    private Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
    private SparseArray<Boolean> duplicateFlags;
    private boolean isRepeatOneAtomic;

    private Listener listener;
    private DynamicTimeline timeline;

    /**
     * @param mediaSources The {@link MediaSource}s to concatenate. It is valid for the same
     *     {@link MediaSource} instance to be present more than once in the array.
     */
    public DynamicMediaSource(List<MediaSource> mediaSources) {
        this(false, mediaSources);
    }

    /**
     * @param isRepeatOneAtomic Whether the concatenated media source shall be treated as atomic
     *     (i.e., repeated in its entirety) when repeat mode is set to {@code Player.REPEAT_MODE_ONE}.
     * @param mediaSources The {@link MediaSource}s to concatenate. It is valid for the same
     *     {@link MediaSource} instance to be present more than once in the array.
     */
    public DynamicMediaSource(boolean isRepeatOneAtomic, List<MediaSource> mediaSources) {
        for (MediaSource mediaSource : mediaSources) {
            Assertions.checkNotNull(mediaSource);
        }
        this.mediaSources = mediaSources;
        this.isRepeatOneAtomic = isRepeatOneAtomic;
        timelines = new SparseArray<Timeline>();
        manifests = new SparseArray<Object>();
        sourceIndexByMediaPeriod = new HashMap<>();
        duplicateFlags = buildDuplicateFlags(mediaSources);
    }

    @Override
    public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
        this.listener = listener;
        for (int i = 0; i < mediaSources.size(); i++) {
            if (!duplicateFlags.get(i)) {
                final int index = i;
                mediaSources.get(i).prepareSource(player, false, new Listener() {
                    @Override
                    public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
                        handleSourceInfoRefreshed(index, timeline, manifest);
                    }
                });
            }
        }
    }

    @Override
    public void maybeThrowSourceInfoRefreshError() throws IOException {
        for (int i = 0; i < mediaSources.size(); i++) {
            if (!duplicateFlags.get(i)) {
                mediaSources.get(i).maybeThrowSourceInfoRefreshError();
            }
        }
    }

    @Override
    public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
        int sourceIndex = timeline.getChildIndexByPeriodIndex(id.periodIndex);
        MediaPeriodId periodIdInSource =
            new MediaPeriodId(id.periodIndex - timeline.getFirstPeriodIndexByChildIndex(sourceIndex));
        MediaPeriod mediaPeriod = mediaSources.get(sourceIndex).createPeriod(periodIdInSource, allocator);
        sourceIndexByMediaPeriod.put(mediaPeriod, sourceIndex);
        return mediaPeriod;
    }

    @Override
    public void releasePeriod(MediaPeriod mediaPeriod) {
        int sourceIndex = sourceIndexByMediaPeriod.get(mediaPeriod);
        sourceIndexByMediaPeriod.remove(mediaPeriod);
        mediaSources.get(sourceIndex).releasePeriod(mediaPeriod);
    }

    @Override
    public void releaseSource() {
        for (int i = 0; i < mediaSources.size(); i++) {
            if (!duplicateFlags.get(i)) {
                mediaSources.get(i).releaseSource();
            }
        }
    }

    private void handleSourceInfoRefreshed(int sourceFirstIndex, Timeline sourceTimeline,
                                       Object sourceManifest) {
        // Set the timeline and manifest.
        timelines.put(sourceFirstIndex, sourceTimeline);
        manifests.put(sourceFirstIndex, sourceManifest);

        // Also set the timeline and manifest for any duplicate entries of the same source.
        for (int i = sourceFirstIndex + 1; i < mediaSources.size(); i++) {
            if (mediaSources.get(i).equals(mediaSources.get(sourceFirstIndex))) {
                timelines.put(i, sourceTimeline);
                manifests.put(i, sourceManifest);
            }
        }

        for(int i= 0; i<mediaSources.size(); i++){
            if(timelines.get(i) == null){
                // Don't invoke the listener until all sources have timelines.
                return;
            }
        }

        timeline = new DynamicTimeline(timelines, isRepeatOneAtomic);
        listener.onSourceInfoRefreshed(timeline, new ArrayList(asList(manifests)));
    }

    private static SparseArray<Boolean> buildDuplicateFlags(List<MediaSource> mediaSources) {
        SparseArray<Boolean> duplicateFlags = new SparseArray<Boolean>();
        IdentityHashMap<MediaSource, Void> sources = new IdentityHashMap<>(mediaSources.size());
        for (int i = 0; i < mediaSources.size(); i++) {
            MediaSource source = mediaSources.get(i);
            if (!sources.containsKey(source)) {
                sources.put(source, null);
                duplicateFlags.append(i,false);
            } else {
                duplicateFlags.append(i,true);
            }
        }
        return duplicateFlags;
    }

    /**
     * A {@link Timeline} that is the concatenation of one or more {@link Timeline}s.
     */
    public static final class DynamicTimeline extends AbstractConcatenatedTimeline {

        private final SparseArray<Timeline> timelines;
        private final int[] sourcePeriodOffsets;
        private final int[] sourceWindowOffsets;
        private final boolean isRepeatOneAtomic;

        public DynamicTimeline(SparseArray<Timeline> timelines, boolean isRepeatOneAtomic) {
            super(timelines.size());
            int[] sourcePeriodOffsets = new int[timelines.size()];
            int[] sourceWindowOffsets = new int[timelines.size()];
            long periodCount = 0;
            int windowCount = 0;
            for (int i = 0; i < timelines.size(); i++) {
                Timeline timeline = timelines.get(i);
                periodCount += timeline.getPeriodCount();
                Assertions.checkState(periodCount <= Integer.MAX_VALUE,
                    "ConcatenatingMediaSource children contain too many periods");
                sourcePeriodOffsets[i] = (int) periodCount;
                windowCount += timeline.getWindowCount();
                sourceWindowOffsets[i] = windowCount;
            }
            this.timelines = timelines;
            this.sourcePeriodOffsets = sourcePeriodOffsets;
            this.sourceWindowOffsets = sourceWindowOffsets;
            this.isRepeatOneAtomic = isRepeatOneAtomic;
        }

        @Override
        public int getWindowCount() {
            return sourceWindowOffsets[sourceWindowOffsets.length - 1];
        }

        @Override
        public int getPeriodCount() {
            return sourcePeriodOffsets[sourcePeriodOffsets.length - 1];
        }

        @Override
        public int getNextWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode) {
            if (isRepeatOneAtomic && repeatMode == Player.REPEAT_MODE_ONE) {
                repeatMode = Player.REPEAT_MODE_ALL;
            }
            return super.getNextWindowIndex(windowIndex, repeatMode);
        }

        @Override
        public int getPreviousWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode) {
            if (isRepeatOneAtomic && repeatMode == Player.REPEAT_MODE_ONE) {
                repeatMode = Player.REPEAT_MODE_ALL;
            }
            return super.getPreviousWindowIndex(windowIndex, repeatMode);
        }

        @Override
        public int getChildIndexByPeriodIndex(int periodIndex) {
            return Util.binarySearchFloor(sourcePeriodOffsets, periodIndex, true, false) + 1;
        }

        @Override
        protected int getChildIndexByWindowIndex(int windowIndex) {
            return Util.binarySearchFloor(sourceWindowOffsets, windowIndex, true, false) + 1;
        }

        @Override
        protected int getChildIndexByChildUid(Object childUid) {
            if (!(childUid instanceof Integer)) {
                return C.INDEX_UNSET;
            }
            return (Integer) childUid;
        }

        @Override
        protected Timeline getTimelineByChildIndex(int childIndex) {
            return timelines.get(childIndex);
        }

        @Override
        public int getFirstPeriodIndexByChildIndex(int childIndex) {
            return childIndex == 0 ? 0 : sourcePeriodOffsets[childIndex - 1];
        }

        @Override
        protected int getFirstWindowIndexByChildIndex(int childIndex) {
            return childIndex == 0 ? 0 : sourceWindowOffsets[childIndex - 1];
        }

        @Override
        protected Object getChildUidByChildIndex(int childIndex) {
            return childIndex;
        }

    }

}

To check when the file is downloaded and set a download completed listener, you can use a BroadcastReceiver. A detailed example of how to set up the BroadcastReceiver is provided here.

like image 199
sparkplug Avatar answered Sep 19 '22 09:09

sparkplug