ExoMedia / Exoplayer MP3 元数据提取器

ExoMedia / Exoplayer MP3 metadata extractor

嗨,我正在尝试使用以下流制作一个简单的无线电应用程序:http://lb.topradio.be/topradio.mp3

目前,为了方便起见,我正在使用 ExoMedia 作为我的默认音频播放器。
它有这个永远不会被调用的 setMetadataListener 方法,因为在 Mp3Extractor class 中 peekId3Data 方法有这个检查。

if (scratch.readUnsignedInt24() != Id3Decoder.ID3_TAG) {
    // Not an ID3 tag.
    break;
}

然而,当我在 VLC Media Player 中抛出相同的流时,它能够成功地从流中获取元数据。
这是如何工作的?
我可以用自定义 Extractor 复制它吗?

谢谢

Exoplayer repository 上打开了一个问题。基本上,元数据是通过 headers 传输的,而不是在流本身中。

我在 audiostream-metadata-retriever 上取得了成功,尽管由于流缓存和元数据在实际歌曲开始播放之前到达,我遇到了一些曲目同步问题。

编辑:

RxJava 实现:

private long icyMetaInt = 1000;

public void showStreamData() {
    Log.d(TAG, "Show stream metadata");
    streamService.getStreamData()
            .subscribeOn(Schedulers.io())
            .repeatWhen(delays -> delays.concatMap(metaIntDelay -> {
                Log.d(TAG, "icyMetaDelay = " + icyMetaInt + "ms ");
                return Observable.timer(icyMetaInt, TimeUnit.MILLISECONDS);
            }))
            .subscribe(new Observer<Response<ResponseBody>>() {
                @Override
                public void onSubscribe(Disposable disposable) {
                    Log.d(TAG, "OnSubscribe");
                }

                @Override
                public void onNext(Response<ResponseBody> response) {
                    Log.d(TAG, "onNext - " + response);
                    if (response.isSuccessful()) {
                        InputStream stream = response.body().byteStream();
                        if (stream != null) {
                            icyMetaInt = Integer.parseInt(response.headers().get("icy-metaint"));
                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
                            try {
                                long skipped = stream.skip(icyMetaInt);
                                while (skipped != icyMetaInt) {
                                    skipped += stream.skip(icyMetaInt - skipped);
                                }

                                int symbolLength = stream.read();
                                int metaDataLength = symbolLength * 16;
                                if (metaDataLength > 0) {
                                    for (int i = 0; i < metaDataLength; ++i) {
                                        int metaDataSymbol = stream.read();
                                        if (metaDataSymbol > 0) {
                                            baos.write(metaDataSymbol);
                                        }
                                    }

                                    String result = baos.toString()
                                            .replace("StreamTitle=", "")
                                            .replaceAll("'", "")
                                            .replaceAll(";", "");
                                    baos.reset();

                                    Log.d(TAG, result);
                                    runOnUiThread(() -> textView.setText(result));

                                    Log.d(TAG, response.headers().get("ice-audio-info"));
                                    Log.d(TAG, response.headers().get("icy-description"));
                                    Log.d(TAG, response.headers().get("icy-genre"));
                                    Log.d(TAG, response.headers().get("icy-name"));
                                    Log.d(TAG, response.headers().get("icy-url"));
                                }

                            } catch (IOException e) {
                                onError(e);
                                Log.e(TAG, "Failed to obtain metadata");
                            } finally {
                                try {
                                    baos.close();
                                    stream.close();
                                } catch (IOException e) {
                                    onError(e);
                                }
                            }
                        }
                    }
                }

                @Override
                public void onError(Throwable throwable) {
                    Log.d(TAG, "onError");
                    throwable.printStackTrace();
                }

                @Override
                public void onComplete() {
                    Log.d(TAG, "onComplete");
                }
            });
}

改装界面:

import io.reactivex.Observable;
import okhttp3.ResponseBody;
import retrofit2.Response;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.Streaming;

public interface StreamService {

    @GET("stream")
    @Headers("Icy-MetaData:1")
    @Streaming
    Observable<Response<ResponseBody>> getStreamData();
}