I am building a video player which streams online videos with track selector. If I parse mp4 video, Exoplayer plays the video well but when the video has mpd format, it plays but with black screen.
My code:
val trackSelector = DefaultTrackSelector(context)
val url = "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd"
val simpleExoPlayer = SimpleExoPlayer.Builder(context).setTrackSelector(trackSelector).build()
simpleExoPlayer.setMediaItem(MediaItem.fromUri(url))
simpleExoPlayer.seekTo(0)
simpleExoPlayer.prepare()
I have tried the code above and it works with all formats excepts mdp format.
I want to show video by http data source. but when I set data source, exoplayer immediately start to buffer video, How can start buffering on user play click.
val dataSourceFactory = DefaultDataSourceFactory(context,Util.getUserAgent(context!!,"user_agent"))
player = SimpleExoPlayer.Builder(context!!).build()
playerView!!.player = player
val contentUrl = "my video url.mp4"
videoSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(contentUrl))
player.prepare(videoSource)
P.S:
I try to change DefaultLoadControl to change buffering behavior but I cant find a workaround.
Thanks.
I have more than one video to play one by one. After Creating MediaSource of each video, All these are going to ConcatenatingMediaSource(mediaSources[]). Normally it play one by one. But when video is fast forward using seekTo(), 1st video is ok but other videos do not follow seekTo().
Suppose 1st video is 10s, 2nd 12s, 3rd 10s.
If I call seekTo((long)12*1000) it should play 2nd video with 2s forward. But it plays from the beginning of 2nd video.
Setting VideoSources
DefaultDataSourceFactory defaultDataSourceFactory = new DefaultDataSourceFactory(getApplicationContext(),Util.getUserAgent(getApplicationContext(), "ExoPlayer"));
MediaSource mediaSource = new ExtractorMediaSource.Factory(defaultDataSourceFactory).createMediaSource(videoItem.getVideoUri());
videoItemArrayList.get(k).setVideoSource(mediaSource);
Concatenate Sources
MediaSource[] mediaSources = new MediaSource[videoItemArrayList.size()];
int j=0;
for(VideoItem item : videoItemArrayList){
mediaSources[j] = item.getVideoSource();
++j;
}
concatenatedSource = new ConcatenatingMediaSource(mediaSources);
Setup exoplayer
exoPlayer.prepare(concatenatedSource);
exoPlayer.seekTo(0);
exoPlayer.setPlayWhenReady(true);
exoPlayer.getPlaybackState();
Using exoplayer.seekTo(period) internally calls currentWindowIndex() internally of the source. While you are playing the first video in the concatenated mediasource you end up receiving windowIndex as 0. Use seekTo(windowIndex, time) to solve the issue.
I have an RTMP stream I want to play in my app using the Exoplayer library. My setup for that is as follows:
TrackSelector trackSelector = new DefaultTrackSelector();
RtmpDataSourceFactory rtmpDataSourceFactory = new RtmpDataSourceFactory(bandwidthMeter);
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
factory = new ExtractorMediaSource.Factory(rtmpDataSourceFactory);
factory.setExtractorsFactory(extractorsFactory);
createSource();
mPlayer = ExoPlayerFactory.newSimpleInstance(mActivity, trackSelector, new DefaultLoadControl(
new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE),
1000, // min buffer
3000, // max buffer
1000, // playback
2000, //playback after rebuffer
DefaultLoadControl.DEFAULT_TARGET_BUFFER_BYTES,
true
));
vwExoPlayer.setPlayer(mPlayer);
mPlayer.addListener(mVideoStreamHandler);
mPlayer.addVideoListener(new VideoListener() {
#Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
Log.d("hasil", "onVideoSizeChanged: w:" + width + ", h:" + height);
String res = width + "x" + height;
resolution.setText(res);
}
#Override
public void onRenderedFirstFrame() {
}
});
Where createSource() is as follows:
private void createSource() {
mMediaSource180 = factory.createMediaSource(Uri.parse(API.GAME_VIDEO_STREAM_URL_180));
mMediaSource360 = factory.createMediaSource(Uri.parse(API.GAME_VIDEO_STREAM_URL_360));
mMediaSource720 = factory.createMediaSource(Uri.parse(API.GAME_VIDEO_STREAM_URL_720));
mMediaSourceAudio = factory.createMediaSource(Uri.parse(API.GAME_AUDIO_STREAM_URL));
}
My current problem is that only the first three ExtractorMediaSources work fine in Exoplayer. The mMediaSourceAudio refuses to play in Exoplayer, but works just fine in the VLC Media Player for Android.
Right now I have a suspicion that the format is AAC-LTP, or whatever AAC variant that requires a codec available in VLC but not in default Android. However, I do not have access to the encoding process so I don't know for sure.
If this isn't the case, what is it?
EDIT:
I've been debugging the BandwidthMeter and added a MediaSourceEventListener. When I use the normal Video sources, onDownstreamFormatChanged() gets called, but not when I use that Audio Stream source.
In addition, the BandwidthMeter works fine, with bytes always downloaded in all parts of the stream and more bytes when the video stream comes in, but only in the Audio only stream that, when I call mPlayer.getBufferedPosition(), the returned value is always 0. Also, when I use the Audio Stream source, no OMX code was called - no decoders were set up.
Am I seeing a malformed audio stream, or do I need to change my Exoplayer's settings?
EDIT 2:
Further debugging reveals that, in all the Video streams and Audio stream, the same FlvExtractor is used. Even though the Video streams have the avc video track encoding and mp4a-latm audio track encoding. Is this normal?
Turns out it's because the stream was recognized to have two tracks/sampleQueues. One Audio track, and one track with null format. That null track was supposed to be the video track, which was supposed to exist according to the stream's flvHeader flag.
For now, I get around this by creating a custom MediaSource using a custom MediaPeriod. Said custom MediaPeriod having code to separate the video and audio tracks of the SampleQueues, then using the audio-only SampleQueue[] instead of the source SampleQueue[] when I want to play the audio-only stream.
Though this gives me another point of concern: There's something one can do to alter the 'has audio track (flag & 0x04) and video track (flag & 0x01)' flag in the rtmp stream, right?
Thanks for the comments, I'm new to ExoPlayer. But your comments helped me in debugging and getting multiple workarounds to the issue.
I tried to use custom MediaSource and custom MediaPeriod to address this audio issue. I have observed video format data coming after audio data incase of video+audio wowza stream, so the function maybeFinishPrepare() will wait for getting both video and audio format tag data before invoking onPrepared, incase if video tagData is received first. Incase of audio data received first, it wont wait and will call onPrepare().
With the above changes, I was able to play audio alone and video_audio wowza streams, where rtmp tagHeader with tagTypes were coming in the order of video tagData and then followed by audio data.
I wasn't able to use the same patch with srs server to play both audio_only and video_audio streams with the same changes. srs server is giving tagData in the order of audio and then video tagData,
So, I debugged further in FlvExtractor. In readFlvHeader, I have overriden the hasAudio and hasVideo variables. These variables will be set based on the first few tagHeaders(5 or 6). I used peekFully on input for 6 times in a loop. In each loop after fetching tagType and tagDataSize, tagDataSize is used to input.advancePeekPosition(), and tagType is used to identify whether we have audio/video format data in tagData. After peeking for first 6 consecutive tagHeaders, I was able to get actual values of hasAudio and hasVideo, and ignored the flvHeaders.flags, which were used to set these variables.
Custom FlvExtractor workaround, looked cleaner than custom MediaSource/MediaPeriod, as we will create those many tracks as necessary, as we are setting proper hasVideo/hasAudio values.
I have a list of HLS urls that I need to play consecutively with exoplayer 2. This is not a problem:
val mediaSources = arrayOfNulls<MediaSource>(videoModel.Clips.size)
for (i in videoModel.Clips.indices) {
mediaSources[i] = buildVideoSource(videoModel.Clips.get(i).StreamUrl)
}
val videoSource = if (mediaSources.size == 1) mediaSources[0] else ConcatenatingMediaSource(*mediaSources)
The problem here is that I need to mute these videos, and play a mp3 audio track instead as background tune. How should I go about this? I thought I could just use a MergingMediaSource like this:
val audioSource = buildAudioSource()
val mergedSource = MergingMediaSource(videoSource, audioSource)
player!!.prepare(videoSource)
But that throws the following error: The merge failed because the sources have different period counts.
Source error:
com.google.android.exoplayer2.source.MergingMediaSource$IllegalMergeException
at com.google.android.exoplayer2.source.MergingMediaSource.checkTimelineMerges(MergingMediaSource.java:169)
at com.google.android.exoplayer2.source.MergingMediaSource.handleSourceInfoRefreshed(MergingMediaSource.java:144)
at com.google.android.exoplayer2.source.MergingMediaSource.access$000(MergingMediaSource.java:34)
at com.google.android.exoplayer2.source.MergingMediaSource$1.onSourceInfoRefreshed(MergingMediaSource.java:102)
at com.google.android.exoplayer2.source.ConcatenatingMediaSource.handleSourceInfoRefreshed(ConcatenatingMediaSource.java:142)
at com.google.android.exoplayer2.source.ConcatenatingMediaSource.access$000(ConcatenatingMediaSource.java:34)
at com.google.android.exoplayer2.source.ConcatenatingMediaSource$1.onSourceInfoRefreshed(ConcatenatingMediaSource.java:81)
at com.google.android.exoplayer2.source.hls.HlsMediaSource.onPrimaryPlaylistRefreshed(HlsMediaSource.java:142)
at com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker.onPlaylistUpdated(HlsPlaylistTracker.java:385)
at com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker.access$1200(HlsPlaylistTracker.java:41)
at com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker$MediaPlaylistBundle.processLoadedPlaylist(HlsPlaylistTracker.java:590)
at com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker$MediaPlaylistBundle.onLoadCompleted(HlsPlaylistTracker.java:539)
at com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker$MediaPlaylistBundle.onLoadCompleted(HlsPlaylistTracker.java:472)
at com.google.android.exoplayer2.upstream.Loader$LoadTask.handleMessage(Loader.java:383)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.os.HandlerThread.run(HandlerThread.java:61)
I have 2 questions:
How can I mute incoming HLS stream? I'd preferably not load the audio at all to save data usage.
How can I play a seperate audiotrack under a ConcatenatingMediaSource?
u can disable audio , in exo player Demo application, you can select audio button and choose disable.
you can see the demo code to see how disable the audio