Vincent Futia
Vincent Futia

Reputation: 103

Playing HLS with ExoPlayer

I'm trying to create a very simple view by extending SurfaceView and rendering the ExoPlayer video to the its surface. I want to support HLS and ONLY HLS. I seem to get audio consistently but I never see video rendered to the screen. I'm curious as to what I could be doing wrong.

The only error that I am seeing in my Logcat is the following:

E/OMXMaster﹕ A component of name 'OMX.qcom.audio.decoder.aac' already exists, ignoring this one.

Below is my code.

private static final int BUFFER_SEGMENT_SIZE = 256 * 1024;
private static final int BUFFER_SEGMENTS = 64;

private ExoPlayer mExoPlayer;
private Handler mHandler;
private AudioCapabilitiesReceiver mAudioCapabilitiesReceiver;
private AudioCapabilities mAudioCapabilities;
private ManifestFetcher<HlsPlaylist> playlistFetcher;
private String mUserAgent;
String url = "http://solutions.brightcove.com/bcls/assets/videos/Great-Blue-Heron.m3u8";

public ExoPlayerView(Context context) {
    super(context);
}

public ExoPlayerView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public ExoPlayerView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

@Override
public void init() {
    mHandler = new Handler();
    mUserAgent = Util.getUserAgent(getContext(), "CX Video Player");
    HlsPlaylistParser parser = new HlsPlaylistParser();
    playlistFetcher = new ManifestFetcher<>(url, new DefaultUriDataSource(getContext(), mUserAgent),
            parser);
    mAudioCapabilitiesReceiver = new AudioCapabilitiesReceiver(getContext(), this);
    mAudioCapabilitiesReceiver.register();
}

@Override
public void play() {
    mExoPlayer.setPlayWhenReady(true);
}

@Override
public void stop() {
    mExoPlayer.stop();
    release();
}

@Override
public void pause() {
    mExoPlayer.setPlayWhenReady(false);
}

@Override
public void seekTo(long timeMillis) {
    mExoPlayer.seekTo(timeMillis);
}

@Override
public long getCurrentPosition() {
    return mExoPlayer.getCurrentPosition();
}

@Override
public boolean isPlaying() {
    return false;
}

@Override
public void playNext() {

}

@Override
public boolean isPlayingLastVideo() {
    return false;
}

@Override
public int getDuration() {
    return (int)mExoPlayer.getDuration();
}

@Override
public void addVideo(Uri uri) {

}

@Override
public void addVideos(List<Uri> uris) {

}

@Override
public void release() {
    mAudioCapabilitiesReceiver.unregister();
    mExoPlayer.release();
}

@Override
public void onSingleManifest(HlsPlaylist hlsPlaylist) {
    final int numRenderers = 2;

    LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE));
    DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();

    int[] variantIndices = null;
    if (hlsPlaylist instanceof HlsMasterPlaylist) {
        HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) hlsPlaylist;
        try {
            variantIndices = VideoFormatSelectorUtil.selectVideoFormatsForDefaultDisplay(
                    getContext(), masterPlaylist.variants, null, false);
        } catch (MediaCodecUtil.DecoderQueryException e) {
            e.printStackTrace();
            return;
        }
        if (variantIndices.length == 0) {
            return;
        }
    }

    DataSource dataSource = new DefaultUriDataSource(getContext(), bandwidthMeter, mUserAgent);
    HlsChunkSource hlsChunkSource = new HlsChunkSource(dataSource, url, hlsPlaylist, bandwidthMeter,
            variantIndices, HlsChunkSource.ADAPTIVE_MODE_SPLICE, mAudioCapabilities);
    HlsSampleSource hlsSampleSource = new HlsSampleSource(hlsChunkSource, loadControl, BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE);

    // Build the track renderers
    TrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(hlsSampleSource, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
    TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(hlsSampleSource);

    // Build the ExoPlayer and start playback
    mExoPlayer = ExoPlayer.Factory.newInstance(numRenderers);
    mExoPlayer.prepare(videoRenderer, audioRenderer);

    // Pass the surface to the video renderer.
    mExoPlayer.sendMessage(videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, getHolder().getSurface());

    mExoPlayer.setPlayWhenReady(true);
}

@Override
public void onSingleManifestError(IOException e) {

}

@Override
public void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities) {
    mAudioCapabilities = audioCapabilities;
    playlistFetcher.singleLoad(mHandler.getLooper(), this);
}
}

Upvotes: 4

Views: 2793

Answers (2)

Joe Maher
Joe Maher

Reputation: 5460

I had a similar situation where i could hear the audio but no video, just a black screen. Turns out i was trying to send the set surface mesagge:

player.sendMessage(videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface);

before the video render was actually initialised and ready which is why i could hear audio only

Upvotes: 0

Vincent Futia
Vincent Futia

Reputation: 103

So I found my issue. After going back through the demo code, I noticed that the demo SurfaceView was wrapped in com.google.android.exoplayer.AspectRatioFrameLayout. After wrapping my SurfaceView in this same layout, the video magically started playing.

Upvotes: 1

Related Questions