jjzjj

java - 如何在不发出单独请求的情况下从 shoutcast 流中分离元数据和轨道

coder 2023-11-23 原文

我制作了一个 radio 应用程序,效果非常好。我也可以播放 radio 流并获取元数据。流媒体服务来自 shoutcast。

唯一的问题是,我将 URL 作为数据源添加到媒体播放器,然后每 5 秒获取一次标题和艺术家。

有什么办法,我可以只发出一个 HTTP 请求,然后拆分音频和元数据,然后将其发送到媒体播放器?

获取元数据的代码。

private void retreiveMetadata() throws IOException {

    int metaDataOffset = 0;

    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder()
        .addHeader("Icy-MetaData", "1")
        .addHeader("Connection", "close")
        .addHeader("Accept", "")
        .url(streamUrl)
        .build();

    request.headers("");


    Response response = client.newCall(request).execute();
    InputStream stream = response.body().byteStream();

    //Map<String, List<String>> headers = response..getHeaderFields();

    if (!response.headers("icy-metaint").equals("")) {
        // Headers are sent via HTTP

        String icyMetaInt = response.headers("icy-metaint").toString();
        icyMetaInt = icyMetaInt.replace("[", "");
        icyMetaInt = icyMetaInt.replace("]", "");

        metaDataOffset = Integer.parseInt(icyMetaInt);
    } else {

        // Headers are sent within a stream
        StringBuilder strHeaders = new StringBuilder();
        char c;
        while ((c = (char)stream.read()) != -1) {
            strHeaders.append(c);
            if (strHeaders.length() > 5 && (strHeaders.substring((strHeaders.length() - 4), strHeaders.length()).equals("\r\n\r\n"))) {
                // end of headers
                break;
            }
        }

        // Match headers to get metadata offset within a stream
        Pattern p = Pattern.compile("\\r\\n(icy-metaint):\\s*(.*)\\r\\n");
        Matcher m = p.matcher(strHeaders.toString());

        if (m.find()) {
            metaDataOffset = Integer.parseInt(m.group(2));
        }
    }

    // In case no data was sent
    if (metaDataOffset == 0) {
        isError = true;
        return;
    }

    // Read metadata
    int b;
    int count = 0;
    int metaDataLength = 4080; // 4080 is the max length
    boolean inData = false;
    StringBuilder metaData = new StringBuilder();
    // Stream position should be either at the beginning or right after headers
    while ((b = stream.read()) != -1) {
        count++;

        // Length of the metadata
        if (count == metaDataOffset + 1) {
            metaDataLength = b * 16;
        }

        if (count > metaDataOffset + 1 && count < (metaDataOffset + metaDataLength)) {
            inData = true;
        } else {
            inData = false;
        }

        if (inData) {
            if (b != 0) {
                metaData.append((char)b);
            }
        }

        if (count > (metaDataOffset + metaDataLength)) {
            break;
        }

    }

    // Set the data
    metadata = IcyStreamMeta.parseMetadata(metaData.toString());

    // Close
    stream.close();
}

public static Map<String, String> parseMetadata(String metaString) {
    Map<String, String> metadata = new HashMap();
    String[] metaParts = metaString.split(";");
    Pattern p = Pattern.compile("^([a-zA-Z]+)=\\'([^\\']*)\\'$");
    Matcher m;
    for (int i = 0; i < metaParts.length; i++) {
        m = p.matcher(metaParts[i]);
        if (m.find()) {
            metadata.put((String)m.group(1), (String)m.group(2));
        }
    }

    return metadata;
}

并将 url 传递给媒体播放器的数据源

String url = "http://68.68.109.106:8356/";
mp = new MediaPlayer();
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {

    mp.setDataSource(url);
    mp.prepare();

} catch (IllegalArgumentException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    Toast.makeText(context, e.toString(), Toast.LENGTH_SHORT).show();
} catch (SecurityException e) {
    // TODO Auto-generated catch block
    Log.e(TAG, "SecurityException");
    Toast.makeText(context, e.toString(), Toast.LENGTH_SHORT).show();
} catch (IllegalStateException e) {
    // TODO Auto-generated catch block
    Log.e(TAG, "IllegalStateException");
    Toast.makeText(context, e.toString(), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
    // TODO Auto-generated catch block
    Log.e(TAG, "IOException");
    Toast.makeText(context, e.toString(), Toast.LENGTH_SHORT).show();
}

最佳答案

您尝试完成的任务并非易事。 (但这并非不可能。)流的元数据仅在流的开头“下载”,因此之后更改它不会影响从流中读取“缓存”的元信息。要读取新属性,您必须重新启动流,这将获取新的 header 等。(但它可能会导致流中断,因此不推荐这样做。)

在声音技术中,通常使用 watermarking .这是一个以不破坏(质量)的方式使用某种数据丰富声音的过程。 ( Usage on YouTube. ) 虽然很难做到,但有一些方法可以在流中隐藏您的信息。我建议阅读 this实现您想要达到的目标。

由于您的硬件是手机,并非所有 Android 设备都足够强大,因此您应该考虑一些新的 HTTP 请求。就 CPU、内存等而言,音频处理并不是一件便宜的事情。如果您这样做,还有一些其他选项需要考虑。五秒轮询不是获取信息的最佳方式,因为您可能会向用户显示错误信息,而错误信息总比没有更糟糕。我推荐基于mqtt的服务器端推送。 Here是一个很好的用法示例。基于这种方法而不是轮询的解决方案使用更少的流量并且更准确。

关于java - 如何在不发出单独请求的情况下从 shoutcast 流中分离元数据和轨道,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37554620/

有关java - 如何在不发出单独请求的情况下从 shoutcast 流中分离元数据和轨道的更多相关文章

  1. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  2. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  5. ruby - 默认情况下使选项为 false - 2

    这是在Ruby中设置默认值的常用方法:classQuietByDefaultdefinitialize(opts={})@verbose=opts[:verbose]endend这是一个容易落入的陷阱:classVerboseNoMatterWhatdefinitialize(opts={})@verbose=opts[:verbose]||trueendend正确的做法是:classVerboseByDefaultdefinitialize(opts={})@verbose=opts.include?(:verbose)?opts[:verbose]:trueendend编写Verb

  6. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  7. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

    在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

  8. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

  9. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  10. ruby - 如何在 Lion 上安装 Xcode 4.6,需要用 RVM 升级 ruby - 2

    我实际上是在尝试使用RVM在我的OSX10.7.5上更新ruby,并在输入以下命令后:rvminstallruby我得到了以下回复:Searchingforbinaryrubies,thismighttakesometime.Checkingrequirementsforosx.Installingrequirementsforosx.Updatingsystem.......Errorrunning'requirements_osx_brew_update_systemruby-2.0.0-p247',pleaseread/Users/username/.rvm/log/138121

随机推荐