在没有 SurfaceView 的情况下使用 Exoplayer

Using Exoplayer without SurfaceView

我想像 Spotify 那样在服务中使用 Exoplayer,但我可以看到大多数实现都使用 SurfaceView 和 VideoController。 Exoplyer 是否可以在没有这两个组件的情况下使用,以便它可以在服务中工作?

我已经创建了一个可以播放你的音频的服务,它类似于官方 exoplayer 的演示。 exoplayer github

我创建了一个类似官方demo的PlayerActivity的PlayerService。我仅在 MISC 部分使用选项 Google play (MP3 Audio) 进行了测试。 请不要评论它完成的代码样式。

public class PlayService extends Service implements
    DemoPlayer.Listener, AudioCapabilitiesReceiver.Listener,
    DemoPlayer.CaptionListener, DemoPlayer.Id3MetadataListener {



// For use within demo app code.
public static final String CONTENT_ID_EXTRA = "content_id";
public static final String CONTENT_TYPE_EXTRA = "content_type";
public static final String PROVIDER_EXTRA = "provider";


private DemoPlayer player;


// For use when launching the demo app using adb.
private static final String CONTENT_EXT_EXTRA = "type";
private static final String TAG = "PlayerService";
private static final int MENU_GROUP_TRACKS = 1;
private static final int ID_OFFSET = 2;


private EventLogger eventLogger;
private MediaController mediaController;
private long playerPosition;
private boolean enableBackgroundAudio;

private Uri contentUri;
private int contentType;
private String contentId;
private String provider;

private AudioCapabilitiesReceiver audioCapabilitiesReceiver;
private boolean playerNeedsPrepare;

public PlayService() {
}

@Override
public IBinder onBind(Intent intent) {
    // TODO: Return the communication channel to the service.
    throw new UnsupportedOperationException("Not yet implemented");
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    audioCapabilitiesReceiver = new AudioCapabilitiesReceiver(this, this);
    audioCapabilitiesReceiver.register();

    contentUri = intent.getData();
    contentType = intent.getIntExtra(CONTENT_TYPE_EXTRA,
            inferContentType(contentUri, intent.getStringExtra(CONTENT_EXT_EXTRA)));
    contentId = intent.getStringExtra(CONTENT_ID_EXTRA);
    provider = intent.getStringExtra(PROVIDER_EXTRA);

    preparePlayer(true);


    return START_STICKY;
}


private static int inferContentType(Uri uri, String fileExtension) {
    String lastPathSegment = !TextUtils.isEmpty(fileExtension) ? "." + fileExtension
            : uri.getLastPathSegment();
    return Util.inferContentType(lastPathSegment);
}

@Override
public void onStateChanged(boolean playWhenReady, int playbackState) {

}

@Override
public void onError(Exception e) {

}

@Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {

}


private void releasePlayer() {
    if (player != null) {
        playerPosition = player.getCurrentPosition();
        player.release();
        player = null;
        eventLogger.endSession();
        eventLogger = null;
    }
}

private DemoPlayer.RendererBuilder getRendererBuilder() {
    String userAgent = Util.getUserAgent(this, "ExoPlayerDemo");
    switch (contentType) {
        case Util.TYPE_SS:
            return new SmoothStreamingRendererBuilder(this, userAgent, contentUri.toString(),
                    new SmoothStreamingTestMediaDrmCallback());
        case Util.TYPE_DASH:
            return new DashRendererBuilder(this, userAgent, contentUri.toString(),
                    new WidevineTestMediaDrmCallback(contentId, provider));
        case Util.TYPE_HLS:
            return new HlsRendererBuilder(this, userAgent, contentUri.toString());
        case Util.TYPE_OTHER:
            return new ExtractorRendererBuilder(this, userAgent, contentUri);
        default:
            throw new IllegalStateException("Unsupported type: " + contentType);
    }
}

private void preparePlayer(boolean playWhenReady) {
    if (player == null) {
        player = new DemoPlayer(getRendererBuilder());
        player.setBackgrounded(true);
        player.addListener(this);
        player.setCaptionListener(this);
        player.setMetadataListener(this);
        player.seekTo(playerPosition);
        playerNeedsPrepare = true;
        //mediaController.setMediaPlayer(player.getPlayerControl());
        //mediaController.setEnabled(true);
        eventLogger = new EventLogger();
        eventLogger.startSession();
        player.addListener(eventLogger);
        player.setInfoListener(eventLogger);
        player.setInternalErrorListener(eventLogger);
    }
    player.setBackgrounded(true);
    if (playerNeedsPrepare) {
        player.prepare();
        playerNeedsPrepare = false;
    }
    player.setBackgrounded(true);
    player.setPlayWhenReady(playWhenReady);
}

@Override
public void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities) {
    if (player == null) {
        return;
    }
    boolean backgrounded = player.getBackgrounded();
    boolean playWhenReady = player.getPlayWhenReady();
    releasePlayer();
    preparePlayer(playWhenReady);
    player.setBackgrounded(backgrounded);
}

@Override
public void onCues(List<Cue> cues) {

}

@Override
public void onId3Metadata(List<Id3Frame> id3Frames) {
    for (Id3Frame id3Frame : id3Frames) {
        if (id3Frame instanceof TxxxFrame) {
            TxxxFrame txxxFrame = (TxxxFrame) id3Frame;
            Log.i(TAG, String.format("ID3 TimedMetadata %s: description=%s, value=%s", txxxFrame.id,
                    txxxFrame.description, txxxFrame.value));
        } else if (id3Frame instanceof PrivFrame) {
            PrivFrame privFrame = (PrivFrame) id3Frame;
            Log.i(TAG, String.format("ID3 TimedMetadata %s: owner=%s", privFrame.id, privFrame.owner));
        } else if (id3Frame instanceof GeobFrame) {
            GeobFrame geobFrame = (GeobFrame) id3Frame;
            Log.i(TAG, String.format("ID3 TimedMetadata %s: mimeType=%s, filename=%s, description=%s",
                    geobFrame.id, geobFrame.mimeType, geobFrame.filename, geobFrame.description));
        } else {
            Log.i(TAG, String.format("ID3 TimedMetadata %s", id3Frame.id));
        }
    }
}

}