Информация о треке из аудиопотока

 
+1
 
Perl
ava
Alex Feat | 02.10.2013, 11:44
Здравствуйте. Не знаю в какой раздел обратиться, поэтому пишу сюда smile
Есть задача вытащить информацию о текущем треке из аудиопотока.
Проще говоря, есть поток радиостанции нужно узнать, что сейчас играет. Когда спрашиваем радиостанцию, могут ли они дать нам файлик с тем что играет на данный момент они говорят берите инфу из потока.
Хочу спросить, решал кто-нибудь похожую задачу? Может кто знает, какой программой можно вытащить инфу? Ну и в итоге реализация будет на перле.
Comments (9)
ava
arto | 02.10.2013, 12:58 #
А какой поток?
ava
Alex Feat | 02.10.2013, 13:27 #
mp3 и ogg.
Если вы про конкретный, то хотя бы http://mp3.nashe.ru/nashe-128.mp3
ava
arto | 02.10.2013, 15:55 #
А Shout модуль не помогает?
ava
Alex Feat | 03.10.2013, 08:14 #
Не знал о таком, гляну сегодня.

upd
Нет. Он только для стриминга, там есть метод set_metadata, а мне нужен get_metadata smile
ava
Alex Feat | 03.10.2013, 09:10 #
Вот так вот получилось:

#!/usr/bin/perl

use strict;
use warnings;
use Socket;

my $host = 'mp3.nashe.ru';
my $port = 80;

socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp'));

my $iaddr = inet_aton($host);
my $paddr = sockaddr_in($port, $iaddr);

connect(SOCK, $paddr) or die "connect: $!";;
send (SOCK, "GET /nashe-128.mp3 HTTP/1.0\nHOST:$host\nIcy-MetaData: 1\n\n", 0);

my $metaint;
my $is_body = 0;
my $content;
my $title;
while ( <SOCK> ) {
    # ищем нужный нам заголовок, который указывает когда будут метаданные
    if (!$metaint && m/icy-metaint:(\d+)/i) {
        $metaint = $1;
    }
    # Ищем где начинается тело ответа
    if ( m/^\r\n/ ) {
        $is_body = 1;
    }
    next unless $is_body;
    $content .= $_;
    # Пропускаем ту часть контента в которой не может быть метаинформации
    next if length($content) < $metaint;
    # Вот так вот тупо, ищем нужную нам метаинформацию
    if ( m/StreamTitle=(.+?);/ ) { $title = $1; last }
    # Максимальная длина метаинформации 4080, поэтому если мы её не нашли то дальше искать безполезно
    last if length($content) > $metaint + 4080;
}

close(SOCK);

print "Title: $title\n";

1;


С сокетами, к сожалению, опыта работы мало, поэтому если какое-то действие можно было сделать красивее, то с удовольствиям почитаю smile
Пока буду тестить дальше.

upd
Иногда виснет на конекте, в случае если прямой эфир идёт :(
ava
arto | 03.10.2013, 10:37 #
используйте select с таймаутом.
ava
Alex Feat | 03.10.2013, 11:59 #
Итак, компаную всю инфу в 1 пост.

Цитата


Процедура выделения метаданных (названия песни) из потока выглядит так:

1) Запрос метаданных
  Это просто добавление нового поля в HTTP-запрос:


  Icy-MetaData:1


  То есть, весь запрос будет выглядеть так:


  GET path HTTP/1.0 
  Icy-MetaData:1



2) Получение интервала метаданных
  Один из заголовков, которые вернутся на ваш запрос, будет сообщать о том, как часто метаданные будут посылаться в потоке. В частности, сколько байт MP3-данных будет между блоками метаданных. Этот заголовок выглядит так:


  icy-metaint: number



3) Получение данных
  Считываем поток данных и считаем байты. Когда число байт стало равно number, мы дошли до блока метаданных. Первая часть блока – это указатель длины. Как уже говорилось, он равен (длина метаданных / 16). Умножаем его на 16, чтобы получить длину метаданных (максимальная длина метаданных = 4080). Теперь считываем это количество байт – и мы имеем строку, содержащую метаданные. Обнуляем счетчик данных и повторяем все заново.


  Следует заметить, что чаще всего длина метаданных равна 0, то есть их просто нет в потоке. Метаданные, как правило, посылаются в двух местах: сразу после соединения и когда сменяются песни.



4) Разбор метаданных
  Часть строки метаданных должна выглядеть так:


  StreamTitle='title of the song';


Взято с http://www.getinfo.ru/article545.html (на случай если сайт будет недоступен)

Исходник:

#!/usr/bin/perl

use strict;
use warnings;
use Socket;
use Fcntl;
use Errno;

my $struct = [
    {
        alias   => 'nashe',
        host    => 'mp3.nashe.ru',
        port    => 80,
        uri     => '/nashe-128.mp3',
    },
    {
        alias   => 'premiun',
        host    => 'listen.rpfm.ru',
        port    => 9000,
        uri     => '/premium128',
    },
];

foreach my $element (0..1) {
        my $host = $struct->[$element]->{host};
        my $port = $struct->[$element]->{port};

        socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp'));

        my $iaddr = inet_aton($host);
        my $paddr = sockaddr_in($port, $iaddr);

        unless (connect(SOCK, $paddr)) {
             Errno::EINPROGRESS == $! or Errno::EWOULDBLOCK or
                 die "connect: $!";

             vec(my $win = '', fileno(SOCK), 1) = 1;

             unless (select(undef, $win, undef, 2.5)) {
                 close (SOCK);
                 die "Time is out!\n";
             }

             if (defined (my $ret = getsockopt(SOCK, SOL_SOCKET, SO_ERROR))) {
                 die "connection failed: $!" if $! = unpack('i', $ret);
             } elsif (!getpeername(SOCK)) {
                 die "connection failed: $!";
             }
        }

        fcntl(SOCK, F_SETFL, 0);
        send (SOCK, "GET ".$struct->[$element]->{uri}." HTTP/1.0\nHOST:$host\nIcy-MetaData: 1\n\n", 0);

        my $metaint = 0;
        my $is_body = 0;
        my $content;
        my $title;
        while ( <SOCK> ) {
            if (!$metaint && m/icy-metaint:\s*?(\d+)/i) {
                $metaint = $1;
            }
            if ( m/^\r\n/ ) {
                $is_body = 1;
            }
            next unless $is_body;
            $content .= $_;
            next if length($content) < $metaint;
            if ( m/StreamTitle=(.+?);/ ) { $title = $1; last }
            last if length($content) > $metaint + 4080;
        }

        close(SOCK);

        print "Title for ".$struct->[$element]->{alias}.": $title\n";
}

1;


Результат:

Title for nashe: 'Пикник - Из Коры Себе Подругу Выструг'
Title for premiun: 'Aura Dione - Friends DJ Nejtrino amp DJ Stranger Remix '


PS. Решение конечно не претендует на идеальность, но надеюсь будет полезно кому-нибудь smile
ava
noize | 07.10.2013, 10:59 #
Grost, оформили бы в виде модуля да закинули на CPAN. Глядишь, другим тоже полезно будет
ava
Alex Feat | 07.10.2013, 11:20 #
Думал над этим, но надо оформить работу с сокетом нормально. Т.к. это задача реальная, а не учебная, то в проекте это будет в виде модуля. Ну и для того чтобы закинуть на cpan надо писать тесты и доку, вроде.
Please register or login to write.
Firm of day
Вы также можете добавить свою фирму в каталог IT-фирм, и публиковать статьи, новости, вакансии и другую информацию от имени фирмы.
Подробнее
Contributors
  arto   noize ava  Alex Feat
advanced
Submit