Android蓝牙车机字幕显示实现方案

最近有一个需求,App音频播放需要将字幕同步到车机上,并且支持基础的暂停/播放、上一首、下一首等操作。
首先我们需要用到MediaSession。当然如果想要更好的兼容,可以使用MediaSessionCompat。
具体差别如下:

MediaSession 和 MediaSessionCompat 都是 Android 中用于管理媒体会话的类,它们在功能上有相似之处,但也存在一些关键区别,以下为你详细介绍:

1. 兼容性

MediaSession:它是 Android 5.0(API 级别 21)引入的原生类,只能在 Android 5.0 及以上版本使用。如果你的应用需要支持低于 Android 5.0 的设备,直接使用 MediaSession 会导致兼容性问题。
MediaSessionCompat:是 Android Support Library(现在的 AndroidX)提供的兼容性类,它为旧版本的 Android 系统提供了对 MediaSession 功能的支持。通过使用 MediaSessionCompat,你可以在较低版本的 Android 设备上实现与 MediaSession 类似的功能,增强了应用的兼容性。

2. 使用场景

MediaSession:当你的应用只需要支持 Android 5.0 及以上版本,并且不需要考虑兼容性问题时,可以直接使用 MediaSession。它提供了原生的媒体会话管理功能,与系统的集成更加紧密。
MediaSessionCompat:如果你的应用需要支持更广泛的 Android 版本,特别是需要兼容 Android 5.0 以下的设备,建议使用 MediaSessionCompat。它封装了 MediaSession 的功能,并提供了统一的 API 接口,方便开发者在不同版本的 Android 系统上进行开发。

3. API 差异

基本功能相似:MediaSessionCompat 提供了与 MediaSession 类似的 API 接口,包括创建媒体会话、设置回调、设置元数据、处理播放状态等功能。开发者可以使用相似的代码逻辑来实现媒体会话的管理。
细微差异:由于 MediaSessionCompat 是为了兼容旧版本系统而设计的,它可能会在某些 API 的使用方式上与 MediaSession 存在细微差异。例如,在处理某些高级功能时,MediaSession 可能提供了更丰富的选项,而 MediaSessionCompat 可能会进行一些简化或封装。

我选择使用MediaSessionCompat,这不重要,因为功能都是一致的。

讲一下我遇到的问题
1、字幕没有正常显示;
2、每次暂停过后,车机上的时间进度重置到0秒,并且只能前进1秒;
3、无论作何努力,图片就是不显示。

原因
1、MediaMetadata.METADATA_KEY_TITLE 时而做标题,时而做字幕;
2、每次都需要设置正确的时长;
3、图片必须是宽高相等的,且不能太大。

然后直接贴上我的代码

public class FZMediaSession {
    private static final String TAG = "FZMediaSession";

    private static FZMediaSession mInstance;

    private MediaSessionCompat mMediaSession;

    private MediaSessionCompat.Callback mMediaSessionCallback;

    private String mRecentSrt;

    private MediaMetadataCompat.Builder builder;

    private FZMediaSession() {
        builder = new MediaMetadataCompat.Builder();
    }

    public static FZMediaSession getInstance() {
        if (mInstance == null) {
            synchronized (FZMediaSession.class) {
                mInstance = new FZMediaSession();
            }
        }
        return mInstance;
    }

    public void changePlayState(boolean isPlaying, long durationMs, long positionMs, float speed) {
        initSession();
        displayDuration(durationMs);
        FZLogger.d(TAG, "isPlaying:" + isPlaying + ",positionMs: " + positionMs);
        int playState = isPlaying ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED;
        mMediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
                .setState(playState, positionMs, speed)
                .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE |
                        PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)
                .build());
    }

    public void displayDuration(long durationMs) {
        builder.putLong(MediaMetadata.METADATA_KEY_DURATION, durationMs);
        executeMetadata(builder.build());
    }

    public void displayAlbum(String title, String artList) {
        FZLogger.d(TAG, "displayAlbum:title:" + title + ", album:" + artList);
        initSession();
        builder = new MediaMetadataCompat.Builder();
        builder.putString(MediaMetadata.METADATA_KEY_TITLE, title);//标题
        builder.putString(MediaMetadata.METADATA_KEY_ALBUM, title);//标题
        if (TextUtils.isEmpty(artList)) {
            builder.putString(MediaMetadata.METADATA_KEY_ARTIST, IShowDubbingApplication.getInstance().getString(R.string.qpy_app_name));//作者或者专辑名称
        } else {
            builder.putString(MediaMetadata.METADATA_KEY_ARTIST, artList);//作者或者专辑名称
        }

        if (!mMediaSession.isActive()) {
            builder.putString(MediaMetadata.METADATA_KEY_TITLE, title);//标题
        } else {
            builder.putString(MediaMetadata.METADATA_KEY_ALBUM, title);//标题
        }

        executeMetadata(builder.build());

//        builder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, title);//标题
//        builder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, title);//标题
//        builder.putString(MediaMetadata.METADATA_KEY_AUTHOR, "Author");//标题
//        executeMetadata(builder.build(), false);
//        if (!mMediaSession.isActive()) {
//            mMediaSession.setActive(true);
//        }
    }

    public void displaySrt(String srt) {
        if (TextUtils.isEmpty(srt)) {
            return;
        }
        if (srt.equals(mRecentSrt)) {
            return;
        }
        mRecentSrt = srt;
        FZLogger.d(TAG, "displaySrt:" + srt);
        builder.putString(MediaMetadata.METADATA_KEY_TITLE, srt);//字幕
        executeMetadata(builder.build());
    }

    public void displayCover(String coverUrl) {
        FZLogger.d(TAG, "displayCover:" + coverUrl);
        if (TextUtils.isEmpty(coverUrl)) {
            builder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, null);
            builder.putBitmap(MediaMetadata.METADATA_KEY_ART, null);
            executeMetadata(builder.build());
        } else {
            String savePath = FileFactory.COMMON.getFilePath(FileFactory.getFileName(coverUrl), true);
            if (FZUtils.isExist(savePath)) {
                displayCoverByLocalPath(savePath);
            } else {
                downloadCoverPic(coverUrl, savePath);
            }
        }
    }

    private void displayCoverByLocalPath(String localPath) {
        if (!FZUtils.isExist(localPath)) {
            return;
        }
        Uri fileUri = FileProvider.getUriForFile(IShowDubbingApplication.getInstance().getBaseContext(),
                BuildConfig.APPLICATION_ID + ".fileProvider", new File(localPath));
        if (fileUri != null) {
            FZLogger.d(TAG, "displayCoverByLocalPath:" + fileUri);
            Bitmap bitmap = centerCropBitmap(BitmapFactory.decodeFile(localPath), 200);
            builder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap);
            builder.putBitmap(MediaMetadata.METADATA_KEY_ART, bitmap);
//            builder.putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, fileUri.toString());
            executeMetadata(builder.build());
        }
    }

    private void initSession() {
        if (mMediaSession == null) {
            mMediaSession = new MediaSessionCompat(IShowDubbingApplication.getInstance().getApplicationContext(), "remusic");
        }

        if (mMediaSessionCallback == null) {
            mMediaSessionCallback = new MediaSessionCompat.Callback() {
                @Override
                public void onPause() {
                    super.onPause();
                    FZFmAudioPlayer.getInstance().pause();
                }

                @Override
                public void onSkipToNext() {
                    super.onSkipToNext();
                    try {
                        FZFmAudioPlayer.getInstance().playNext();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onSkipToPrevious() {
                    super.onSkipToPrevious();
                    try {
                        FZFmAudioPlayer.getInstance().playPrevious();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onPlay() {
                    super.onPlay();
                    FZFmAudioPlayer.getInstance().resume();
                }

            };
            mMediaSession.setCallback(mMediaSessionCallback);
        }
    }


    private void executeMetadata(MediaMetadataCompat metadataCompat) {
        initSession();
        if (!mMediaSession.isActive()) {
            mMediaSession.setActive(true);
        }
        mMediaSession.setMetadata(metadataCompat);
    }

    public void release() {
//        if (mMediaSession != null) {
//            mMediaSession.release();
//            mMediaSession = null;
//        }
//        mMediaSessionCallback = null;
    }

    public Bitmap centerCropBitmap(Bitmap originalBitmap, int n) {
        int width = originalBitmap.getWidth();
        int height = originalBitmap.getHeight();
        int left = 0;
        int top = 0;
        int right = width;
        int bottom = height;

        // 计算裁剪的起始位置
        if (width > height) {
            left = (width - height) / 2;
            right = left + height;
        } else if (height > width) {
            top = (height - width) / 2;
            bottom = top + width;
        }

        // 先裁剪成正方形
        Bitmap squareBitmap = Bitmap.createBitmap(originalBitmap, left, top, right - left, bottom - top);

        // 再将正方形缩放成 n x n 的大小
        return Bitmap.createScaledBitmap(squareBitmap, n, n, true);
    }

    private void downloadCoverPic(String url, String savePath) {
        FZFileDownloader.getInstance().getFileDownloadTaskBuilder()
                .withDownloadUrl(url)
                .withSavePath(savePath)
                .withListener(new DownloadListener() {
                    @Override
                    public void onConnectIng() {

                    }

                    @Override
                    public void onGetSize(long totalSize) {

                    }

                    @Override
                    public void onDownloadIng(float progressF, int progress) {

                    }

                    @Override
                    public void onCancel() {

                    }

                    @Override
                    public void onError(String error) {

                    }

                    @Override
                    public void onRepeatTask() {

                    }

                    @Override
                    public void onDone(String localPath) {
                        FZLogger.d(TAG, "onDone, localPath == " + localPath);
                        displayCoverByLocalPath(localPath);
                    }
                }).build().start();
    }

}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容