Android 使用不带SurfaceView的Exoplayer

Android 使用不带SurfaceView的Exoplayer,android,android-service,exoplayer,Android,Android Service,Exoplayer,我想像Spotify那样在服务中使用Exoplayer,但我可以看到大多数实现都使用SurfaceView和视频控制器。Exoplyer是否可以在没有这两个组件的情况下使用,以便在服务中工作?我创建了一个可以播放音频的服务,它类似于官方Exoplyer的演示 我创建了一个PlayerService,它与官方演示的PlayerActivity非常相似。我只测试了MISC部分中的Google play(MP3音频)选项。 请不要评论代码样式,它是用来尝试这一点的 public class PlayS

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

我创建了一个可以播放音频的服务,它类似于官方Exoplyer的演示

我创建了一个PlayerService,它与官方演示的PlayerActivity非常相似。我只测试了MISC部分中的Google play(MP3音频)选项。 请不要评论代码样式,它是用来尝试这一点的

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));
        }
    }
}
公共类PlayService扩展服务实现
DemoPlayer.Listener,AudioCapabilitiesReceiver.Listener,
DemoPlayer.CaptionListener,DemoPlayer.ID3MetadatListener{
//用于演示应用程序代码中。
公共静态最终字符串CONTENT\u ID\u EXTRA=“CONTENT\u ID”;
公共静态最终字符串CONTENT\u TYPE\u EXTRA=“CONTENT\u TYPE”;
公共静态最终字符串提供程序\u EXTRA=“提供程序”;
私人玩家;
//供使用adb启动演示应用程序时使用。
私有静态最终字符串内容\u EXT\u EXTRA=“type”;
私有静态最终字符串标记=“PlayerService”;
私有静态最终整数菜单\组\轨道=1;
专用静态最终整数ID_偏移=2;
私有事件记录器事件记录器;
专用媒体控制器;
私人长期持股;
私有布尔启用背景音频;
私有Uri-contentUri;
私有int内容类型;
私有字符串contentId;
私有字符串提供者;
私人音频能力接收者音频能力接收者;
私有布尔playerNeedsPrepare;
公共服务{
}
@凌驾
公共IBinder onBind(意向){
//TODO:将通信通道返回到服务。
抛出新的UnsupportedOperationException(“尚未实现”);
}
@凌驾
公共int onStartCommand(Intent Intent、int标志、int startId){
audioCapabilitiesReceiver=新的audioCapabilitiesReceiver(这个,这个);
audioCapabilitiesReceiver.register();
contentUri=intent.getData();
contentType=intent.getIntExtra(CONTENT\u TYPE\u EXTRA,
推断ContentType(contentUri,intent.getStringExtra(CONTENT\u EXT\u EXTRA));
contentId=intent.getStringExtra(CONTENT\u ID\u EXTRA);
provider=intent.getStringExtra(provider\u EXTRA);
preparePlayer(真);
返回开始时间;
}
私有静态int-inferContentType(Uri、字符串文件扩展名){
字符串lastPathSegment=!TextUtils.isEmpty(文件扩展名)?“+文件扩展名”
:uri.getLastPathSegment();
返回Util.inferContentType(lastPathSegment);
}
@凌驾
状态更改时的公共无效(布尔playWhenReady,int playbackState){
}
@凌驾
公共无效申报人(例外e){
}
@凌驾
VideoSizeChanged上的公共空白(整数宽度、整数高度、整数未分配比例、浮点像素宽度高度比){
}
私有无效释放玩家(){
如果(玩家!=null){
playerPosition=player.getCurrentPosition();
player.release();
player=null;
endSession();
eventLogger=null;
}
}
私有DemoPlayer.RenderBuilder GetRenderBuilder(){
字符串userAgent=Util.getUserAgent(这是“ExoPlayerDemo”);
开关(contentType){
case Util.TYPE\u SS:
返回新的SmoothStreamingRenderBuilder(this,userAgent,contentUri.toString(),
新的SmoothStreamingTestMediaDrmCallback());
case Util.TYPE_破折号:
返回新的DashRenderBuilder(this、userAgent、contentUri.toString(),
新的WidevineTestMediaDrmCallback(contentId,提供者));
case Util.TYPE_HLS:
返回新的HLSRenderBuilder(this,userAgent,contentUri.toString());
case Util.TYPE_其他:
返回新的ExtractorRendererBuilder(this、userAgent、contentUri);
违约:
抛出新的IllegalStateException(“不支持的类型:“+contentType”);
}
}
专用void preparePlayer(布尔playWhenReady){
if(player==null){
player=新的DemoPlayer(getRenderBuilder());
player.setBackgrounded(true);
player.addListener(这个);
player.setCaptionListener(这个);
setMetadataListener(这个);
player.seekTo(玩家位置);
PlayerNedSpRepare=真;
//setMediaPlayer(player.getPlayerControl());
//mediaController.setEnabled(true);
eventLogger=新的eventLogger();
eventLogger.startSession();
player.addListener(事件记录器);
player.setInfoListener(事件记录器);
player.setInternalErrorListener(事件记录器);
}
player.setBackgrounded(true);
如果(播放已准备就绪){
player.prepare();
playerNedsprepare=false;
}
player.setBackgrounded(true);
player.setplaywhenredy(playwhenredy);
}
@凌驾
更改音频功能上的公共无效(音频功能音频功能){
if(player==null){
返回;
}
boolean background=player.getbackground();
布尔playWhenReady=player.getPlayWhenReady();
释放玩家();
准备播放(播放时播放);
球员。挫折感(背景);
}
@凌驾
公共void onCues(列表提示){
}
@凌驾
公共元数据(列表id3Frames){
用于(Id3Frame Id3Frame:id3Frames){
if(TXXFRAME的id3Frame实例){
TxxxFrame TxxxFrame=(TxxxFrame)id3Frame;
Log.i(TAG,String.format(“ID3 TimedMetadata%s:description=%s,value=%s”),txxframe.id,
txxxxframe.description,txxxxframe.value));
}else if(PrivFrame的id3Frame实例){
PrivFrame PrivFrame=(PrivFrame)id3Frame;
Log.i(TAG,String.format(“ID3 TimedMetadata%s:owner=%s”,privFrame.id,privFrame.owner));
}else if(GeobFrame的id3Frame实例){
GeobFrame GeobFrame=(GeobFrame)id3Frame;
Log.i(标记,字符串.format(“ID3 TimedMetadata%s:mimeType=%s,