-
Notifications
You must be signed in to change notification settings - Fork 4
/
soundbridge-upnp-play
executable file
·108 lines (88 loc) · 2.89 KB
/
soundbridge-upnp-play
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
use Data::Dumper;
use List::Util qw(first);
use XML::Simple;
use HTML::Entities;
use Net::UPnP::ControlPoint;
use Net::UPnP::AV::MediaRenderer;
use Soundbridge;
use URI;
use Time::HiRes qw(sleep);
our $CP;
sub find_device($$$) {
my $type = shift;
my $name = shift;
my $st = shift;
$CP ||= Net::UPnP::ControlPoint->new;
state $devices = [ $CP->search(
mx => 1, # this timeout is doubled by Net::UPnP::ControlPoint
# in the select() call it makes (!?)
) ];
first {
$_->getdevicetype =~ /$type/
and $_->getfriendlyname =~ /$name/i
and (not $st or $_->getudn eq $st)
} @$devices;
}
my $xml = XML::Simple->new(
ForceArray => 1,
KeyAttr => 'id',
KeepRoot => 1,
);
if (my $pid = `pidof rygel`) {
chomp $pid;
kill 'TERM', $pid or die "Failed to kill rygel ($pid)\n";
}
system "rygel 2>&1 | logger -t rygel &";
sleep 0.5;
my $src = find_device MediaServer => 'Gst Launch', 'uuid:556a1528-e666-44ad-85c3-da31d25807c4';
my $dst = find_device MediaRenderer => 'Soundbridge', 'uuid:526F6B75-536F-756E-6442-000D4B041635';
die "Unable to find the source 'Gst Launch'\n" unless $src;
die "Unable to find the destination 'Soundbridge'\n" unless $dst;
my $directory = first { $_->getservicetype =~ /ContentDirectory/ }
$src->getservicelist;
# we subvert the api here so we can just fucking do what we want
my $action = $directory->postcontrol(
Browse => {
ObjectID => 0,
BrowseFlag => 'BrowseDirectChildren',
Filter => '*',
StartingIndex => 0,
RequestedCount => 0,
SortCriteria => '',
},
);
# Get the stream we want
my $result = $xml->XMLin($action->getargumentlist->{'Result'})
or die "Bad action result: ", Dumper($action->getargumentlist), "\n";
my $items = $result->{'DIDL-Lite'}[0]{'item'};
my $name = shift || 'streaming';
my $stream = first { /MP3/ } # use MP3 instead?
map { $_->{'content'} }
@{$items->{$name}{'res'}};
die "Unable to find stream '$name' being served by Rygel\n"
unless $stream;
print "Playing '$name' over the soundbridge\n";
# Remove extraneous metadata
delete $items->{$_} for grep { $_ ne $name } keys %$items;
# Use RCP to connect the soundbrige to the special Radio "server".
# The device must be connected to a server or it won't play UPnP audio.
my $host = URI->new($dst->getlocation)->host;
Soundbridge->new( server => "$host:5555", log_level => 0 )->send(<<'.');
ServerDisconnect
SetServerFilter radio
ListServers
ServerConnect 0
.
# Let's go!
my $renderer = Net::UPnP::AV::MediaRenderer->new;
$renderer->setdevice($dst);
$renderer->setAVTransportURI(
CurrentURI => $stream,
CurrentURIMetaData => encode_entities($xml->XMLout($result)),
InstanceID => 0,
);
$renderer->play;