Как отделить метаданные и трек от потока крикаста, не делая отдельных запросов

Я сделал приложение для радио, которое отлично работает. Я могу воспроизводить радиопоток и получать метаданные. Стриминговый сервис от 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();
}

person Searock    schedule 31.05.2016    source источник
comment
Да, вам просто нужно демультиплексировать метаданные из потока. См. этот ответ для получения более подробной информации: отслеживать информацию из аудиопотока с помощью php">stackoverflow.com/questions/4911062/ Если вам нужен конкретный ответ для Java на Android, вы должны показать свой код о том, как вы получаете данные потока в настоящее время.   -  person Brad    schedule 01.06.2016
comment
@Brad Смотрите мой обновленный ответ. Спасибо.   -  person Searock    schedule 01.06.2016


Ответы (1)


Задача, которую вы пытаетесь выполнить, не является чем-то легким. (Хотя это не невозможно.) Метаданные потока «загружаются» только в начале потока, поэтому их последующее изменение не повлияет на чтение метаинформации, «кэшированной» из потока. Чтобы прочитать новые свойства, вы должны перезапустить поток, это приведет к получению новых заголовков и т. д. (Но это может привести к разрыву потока, поэтому это не рекомендуется).

В звуковых технологиях принято использовать водяной знак. Это процесс, в котором вы обогащаете свой звук какими-то данными, не разрушая (качество). (Использование на YouTube .) Хотя это сложно сделать, есть несколько способов скрыть вашу информацию в ленте. Я рекомендую прочитать это для достижения цели, которую вы хотите достичь.

Поскольку ваше оборудование представляет собой мобильный телефон, а не все устройства Android достаточно сильны, вам следует рассмотреть некоторые новые HTTP-запросы. Обработка звука — недешевая вещь с точки зрения процессора, памяти и т. д. Есть и другие варианты, которые следует рассмотреть, если вы это сделаете. Пятисекундный опрос — не лучший способ получить информацию, потому что вы можете показать пользователю ложную информацию, а ложная информация хуже, чем ничего. Я рекомендую push на стороне сервера на основе mqtt. Вот довольно хороший пример использования. Решение, основанное на этом методе вместо опроса, использует меньше трафика и является более точным.

person Hash    schedule 16.06.2016