|
|
Index
|
|
|
General
|
|
|
|
|
|
IPTV |
Video over Internet |
| media |
video resolution |
|
480i, 576i, 720p, 1080i, 1080p |
(any) |
| codec |
|
- MPEG-2
- MPEG4-AVC/H264
- VC-1
|
(any) |
| container |
|
|
- MP4
- QuickTime
- WMedia
- RealNetworks
- Flash Video
- ...
|
| service discovery |
|
|
SD&S |
|
| transmission |
progressive download |
|
- |
x |
| streaming |
live |
|
|
x |
| on-demand |
|
x |
x |
| network delivery |
unicast |
|
content on-demand |
x |
| multicast |
|
x |
x |
| p2p |
|
- |
x |
| receiver |
STB |
|
x |
- |
| PC |
|
|
- browser (RTSP)
- media player (RTP/RTCP)
|
|
Streaming
|
|
|
- Howto
- Articles
- Estàndards / Standards
- Empreses / Companies
- Myplay
|
- Resum / Overview:
- Jerarquia / Hierarchy:
ES
|
format
|
protocol
|
transport
|
info / standard
|
server
|
client
|
-
|
TS
|
-
|
UDP
|
|
- unicast
- gstreamer
- vlc
- cvlc toto.mp4 --sout '#standard{access=udp,mux=ts,dst=192.168.0.8:1234}'
- ffmpeg
- ffmpeg -re -i toto.mp4 -f
mpegts "udp://192.168.0.8:1234?ttl=10&buffer_size=65536&pkt_size=1316"
- Opencaster
- multicast (remember to open the port on the
firewall
and allow multicast, e.g. in shorewall)
- gstreamer
- videolan
- cvlc toto.mp4 --sout '#standard{access=udp,mux=ts,dst=234.1.2.3:1234}'
- dvblast
- ffmpeg
- ffmpeg -re -i toto.mp4 -f
mpegts "udp://234.1.2.3:1234?ttl=10&buffer_size=65536&pkt_size=1316"
- dvbstream
- dvbstream -f 794000 -qam 64 -gi 4 -cr 2_3
-bw 8 -tm 8 -udp
110 111 112 113
- default: -net 224.0.1.2:5004
- dvbstream -f 794000 -qam 64 -gi 4 -cr 2_3
-bw 8 -tm 8 -udp
-net 224.0.1.2:5004 110 111 112 113
|
- unicast
ffplay udp://@1234
ffplay -infbuf
udp://@:1234?reuse=1
ffmpeg -i 'udp://@:1234?fifo_size=1000000'
...
tsudpreceive ...
- multicast
ffplay -infbuf
udp://@234.1.2.3:1234?reuse=1
|
TCP
|
|
|
|
MPEG-2 video
|
-
|
RTP
|
UDP
|
RFC
2250.3.1 |
- default port for the first elementary stream: 5004
- no SDP
- gstreamer
- only video (test)
- gst-launch-1.0 -v videotestsrc !
videoconvert ! x264enc ! rtph264pay config-interval=10
pt=96 ! udpsink host=234.1.2.3 port=5004
- gst-launch-1.0 -v videotestsrc !
video/x-raw,framerate=24/1,width=1280,height=720
! videoconvert ! x264enc !
video/x-h264,stream-format=byte-stream,profile=high
! rtph264pay config-interval=10
pt=96 ! udpsink host=127.0.0.1 port=5004
- only video (mp4 file)
- gst-launch-1.0 -v filesrc
location=bbb_timecode_1280x720_700k_stereo.mp4
! qtdemux name=demux demux.video_0 !
rtph264pay config-interval=10
pt=96 ! udpsink host=127.0.0.1 port=5004
- SDP
shown on verbose information:
- ffmpeg
- RTP
streaming
with ffmpeg
- "Only one stream supported in the RTP
muxer"
- only video:
- ffmpeg -re -i toto.mp4 -c:v copy -an -f rtp rtp://234.1.2.3:5004
- both video (port 5004) and audio (port
5006):
- ffmpeg -re -i toto.mp4 -c:v copy -an -f rtp rtp://234.1.2.3:5004
-vn -c:audio copy -f
rtp rtp://234.1.2.3:5006
- or use -f sap, -f rtsp
- SDP
as local file:
- ffmpeg
- ffmpeg -re -i toto.mp4 -c:v copy -an -f rtp rtp://234.1.2.3:5004
-vn -c:audio copy -f
rtp rtp://234.1.2.3:5006
-metadata title=toto -sdp_file
/tmp/toto.sdp
- ffmpeg -re -i toto.mp4 -c:v copy -an -f rtp rtp://234.1.2.3:5004
-vn -c:audio copy -f
rtp rtp://234.1.2.3:5006
>/tmp/toto.sdp
- only test video
- ffmpeg -re -f lavfi -i
"testsrc=size=1280x720:rate=24" -c:v
libx264 -b:v 700k -pix_fmt yuv420p
-profile:v main -level:v 31 -f rtp
rtp://127.0.0.1:5004 -sdp_file test.sdp
- audio and video (H.264 + AAC)
ffmpeg -re -f lavfi -i
"testsrc=size=1280x720:rate=24" -f
lavfi -i
"sine=frequency=440:sample_rate=48000,
aformat=channel_layouts=stereo" -c:v
libx264 -b:v 700k -pix_fmt yuv420p
-profile:v main -level:v 31 -an -f rtp
rtp://234.1.2.3:8004 -c:a aac -b:a 64k
-vn -f rtp rtp://234.1.2.3:8006
-sdp_file /tmp/test.sdp
- audio and video (VP8 + Opus)
ffmpeg -re -f lavfi -i
"testsrc=size=320x180:rate=24" -f
lavfi -i
"sine=frequency=440:sample_rate=48000,
aformat=channel_layouts=stereo" -c:v
vp8 -b:v 700k -an -f rtp
rtp://234.1.2.3:8004 -c:a opus -strict
-2 -b:a 64k -vn -f rtp
rtp://234.1.2.3:8006 -sdp_file
/tmp/test.sdp
- vlc
- VLC gui: RTP
Audio/Video Profile
- cvlc ... --sout '#rtp{dst=234.1.2.3,port=1234,name=toto,sdp=file:///tmp/aa.sdp}'
- SDP
available via RTSP:
- vlc
- VLC
gui: RTSP
- cvlc toto.mp4 --sout '#rtp{dst=234.1.2.3,port=1234,name=toto,sdp=rtsp://server_address:8554/aa.sdp}'
- cvlc toto.mp4 --rtsp-host=server_address
--sout '#rtp{dst=127.0.0.1,port=5004,sdp=rtsp://:8888/aa.sdp}'
- ffmpeg
- SDP
available via HTTP:
- vlc
- VLC gui: RTP
Audio/Video Profile
- cvlc toto.mp4 --sout '#rtp{dst=234.1.2.3,port=1234,name=toto,sdp=http://server_address:8888/aa.sdp}'
- ffmpeg
- SDP
available via SAP:
- vlc
- cvlc toto.mp4 --sout '#rtp{dst=234.1.2.3,port=1234,sap,name=toto}'
- cvlc toto.mp4 --sout '#rtp{dst=234.1.2.3,port=1234,sdp=sap://,name=toto}'
- WARNING: not discoverable by vlc as a
player; use TS
over RTP instead
- ffmpeg
- ffmpeg -re -i toto.mp4 -c copy [-metadata
title="Toto name"] -f
sap sap://234.1.2.3?same_port=1
- same_port=1 is needed for discovering
and playing with vlc
- ffmpeg -re -i toto.mp4 -c copy -f
sap 'sap://234.1.2.3:5004?same_port=1&announce_addr=224.2.127.254&announce_port=9875'
- these are the default values: rtp port
(5004), announce_addr (224.2.127.254),
announce_port (9875)
|
- no SDP
- gstreamer (with caps)
- (not smoothly)
gst-launch-1.0
udpsrc address=127.0.0.1 port=5004 !
"application/x-rtp, media=(string)video,
clock-rate=(int)90000,
encoding-name=(string)H264,
packetization-mode=(string)1,
profile-level-id=(string)64001f,
payload=(int)96" ! queue ! rtph264depay !
decodebin ! autovideosink
- local SDP file
- ffmpeg
ffplay -protocol_whitelist
rtp,udp,file,http,https,tcp,tls -i
/tmp/aa.sdp
- gstreamer
gst-launch-1.0 filesrc
location=/tmp/sintel.sdp ! sdpdemux
name=demux demux. ! queue ! decodebin !
autovideosink
gst-launch-1.0 filesrc
location=/tmp/sintel.sdp ! sdpdemux
name=demux demux. ! queue ! decodebin !
autovideosink demux. ! queue ! decodebin !
autoaudiosink
gst-launch-1.0 filesrc
location=/tmp/sintel.sdp ! sdpdemux
name=demux demux. ! queue ! rtph264depay !
decodebin ! autovideosink
- SDP available
via RTSP:
ffplay rtsp://server_address:8554/aa.sdp
- ffplay
rtp://@:5004
- Unable to receive RTP payload type 96 without
an SDP file describing it
- SDP available
via HTTP:
- ffmpeg
ffplay
http://server_address:8888/aa.sdp
- gstreamer
gst-launch-1.0 souphttpsrc
location=http://some_server/sintel.sdp !
sdpdemux name=demux demux. ! queue !
decodebin ! autovideosink demux. ! queue !
decodebin ! autoaudiosink
- SDP available
via SAP:
- bash
- get one (
-c 1) SDP from
default SAP multicast addres (224.2.127.254)
sudo tcpdump -c 1 dst
224.2.127.254 -w /tmp/sdp/toto.dump
&& ... >/tmp/sdp/toto.sdp
- and then e.g. gstreamer from local file
- ffplay
ffplay sap://
- default: 224.2.127.254:9875
ffplay sap://224.2.127.254:9875
- do not choose the first received sdp in
announcement
- VLC gui (only when generated with ffmpeg with
same_port=1)
- Visualitza / Llista de reproducció / Xarxa
local:
|
MPEG-2 audio
|
RFC
2250.3.2 |
H.264
|
RFC 3984
|
...
|
|
-
|
TS
|
RTP
|
UDP |
RFC
2250.2
SDP is not needed (?)
|
- videolan
- SDP
shown on verbose information:
- vlc
- cvlc -vv toto.mp4 --sout '#rtp{dst=234.1.2.3,port=1234,mux=ts,name=toto}'
- SDP
available via SAP:
- vlc
- cvlc -vv toto.mp4 --sout '#rtp{dst=234.1.2.3,name=toto,sdp=sap://,mux=ts}'
- cvlc -vv toto.mp4 --sout '#rtp{dst=234.1.2.3,port=1234,name=toto,sap,mux=ts}'
- separate streams (#es):
- vlc -vvv toto.mp4 --sout
'#es{access=rtp,mux=ts,url_audio=192.168.1.2:50002,url_video=192.168.1.2:50004}'
- dvbstream
- dvbstream -f 794000 -qam 64 -gi 4 -cr 2_3 -bw
8 -tm 8 -rtp
110 111 112 113
- default: -net 224.0.1.2:5004
- dvbstream -f 794000 -qam 64 -gi 4 -cr 2_3 -bw
8 -tm 8 -rtp
-net 224.0.1.2:5004 110 111 112 113
|
- ffplay rtp://@:5004
- ffplay
udp://:5004 (lots of errorrs)
- SAP
- ffmpeg
- VLC gui
- Visualitza / Llista de reproducció / Xarxa
local:
|
|
MP4
|
RTP |
UDP |
|
- cvlc
toto.mp4 --sout
'#rtp{dst=192.168.0.8,port=1234,mux=mp4,sap,name=toto}'
- stream_out_rtp stream out error: unsupported
muxer type for RTP (only TS/PS)
- GPAC
- MP4Box -rtp -dst=234.1.2.3 -port=5004
-sdp=/tmp/a.sdp toto.mp4
- MP4Box -rtp -dst...
- GPAC hinting + Darwin
|
|
-
|
TS
|
HTTP
|
TCP
|
progressive
download
|
- vlc
- VLC
gui: HTTP
- cvlc toto.mp4 --http-port=8888 --sout '#standard{access=http{mime=video/mpeg},mux=ts,dst=/dir/toto.ts}'
- VLM
cvlc -I
telnet --telnet-password contrasenya
- telnet
localhost 4212
new channel1 broadcast enabled
setup channel1 input toto.mp4
setup channel1 output
#standard{access=http{mime=video/mpeg},mux=ts,dst=192.168.0.8:8888/sintel}
control channel1 play
- ffmpeg
- nginx
|
- ffplay http://server_address:8888/dir/toto.ts
- vlc http://server_address:8888/dir/toto.ts
- HbbTV
|
HLS
|
|
|
MPEG-DASH
|
|
|
-
|
MP4
|
HTTP
|
TCP
|
progressive
download |
- cvlc
toto.mp4 --http-port=8888 --sout '#standard{access=http,mux=mp4,dst=/toto}'
- stream_out_standard stream out error: mov and
mp4 mux are only valid with file output
- nginx ngx_http_mp4_module
- nginx-vod-module
|
|
|
MPEG-DASH |
|
|
- Documents:
streaming
|
protocol |
documents |
| RTP
/ SRTP / RTCP |
UDP |
| IETF
RFC |
audio |
video |
RTP packet |
| number |
name |
header |
payload |
| PT |
... |
3550
1889 |
RTP |
|
|
|
|
| 3555 |
MIME type registration |
MIME type |
|
|
|
| 3551 |
AV profile |
x |
x |
x |
|
|
| 3640 |
MPEG-4 Elementary streams |
MPEG4 ES
(AAC,...)
|
|
|
|
AU header
section |
auxiliary section |
access unit
data
section |
| agg |
| AU headers length |
AU header 1 |
AU header 2 |
|
| AU size |
AU index |
CTS flag |
CTS delta |
DTS flag |
DTS delta |
RAP flag |
stream state |
|
... |
|
|
|
| frag |
|
|
|
|
|
|
|
|
| 3016 |
MPEG4 AV |
MPEG4 |
|
|
|
| 3119 |
Loss-tolerant MP3 |
MP3 |
- |
|
|
|
| 3267 |
Payload and file storage AMR, AMR-WB |
AMR, AMR-WB |
- |
|
|
|
| 4352 |
Payload AMR-WB+ |
AMR-WB+ |
|
|
|
|
7587
|
RTP Payload
Format for the Opus Speech and Audio Codec
|
Opus
|
-
|
|
|
|
| 2250 |
MPEG-1/2 video |
- |
MPEG-1/2 |
|
|
|
| 2429 |
H.263 video |
- |
H.263 |
|
|
|
| 3984 |
Payload H.264 video |
- |
H.264 |
|
|
| NAL unit |
| NAL header |
payload |
|
|
... |
|
draft-svc
(diff)
|
Payload SVC video |
- |
SVC |
|
|
|
| 4425 |
Payload VC-1 video |
- |
VC-1 |
|
|
|
7741
|
RTP Payload
Format for VP8 Video |
-
|
VP8 |
|
|
|
draft-uberti-payload-vp9
|
RTP Payload
Format for VP9 Video
|
-
|
VP9 |
|
|
|
|
| RTSP
(.org)
(tricky mode) |
TCP |
|
| RSVP |
|
|
| SDP |
|
|
RTSP
|
|
SAP
|
|
SAP
|
|
|
SIP
|
|
|
HTTP
Adaptive Streaming
|
(general)
|
|
| HLS |
- A Review of HTTP Live Streaming (Andrew
Fecheyr-Lippens) (pdf)
|
| DASH |
|
| MSSH |
|
|
|
RTP
|
- Documents
- Jerarquia /
Hierarchy
- SRTP - Secure RTP
- RTCP
- SAP
- SIP
- RTP header (IETF
RFC 3550: section 5.1):
-
| 0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
0 |
1 |
| V=2 |
P |
X |
CC |
M |
PT |
sequence_number |
| timestamp |
| synchronization source
(SSRC) identifier |
| contributing source
(CSRC) identifiers |
- V: Version
- P: Padding
- X: Extension
- CC: CSRC count
- M: marker
- PT: payload
type
- NAL header /
nal_unit_type
- H264/RTP (IETF
RFC
3984):
- WebRTC
- In-band, out-band SPS, PPS:
-
|
description
|
SPS
(7), PPS (8) |
NAL types
|
SDP
(manifest)
|
example to
generate
|
problems
|
|
|
in sdp manifest
(sprop-parameter-sets)
|
in stream
(NALs 7, 8)
|
|
|
|
|
out-band
|
SPS,PPS only in
manifest
|
x
|
-
|
5, 1, 1... 5, 1,
1, ...
|
a=fmtp:96
packetization-mode=1; sprop-parameter-sets=Z01AH+ygoP2AiAAAAwAIAAADAZB4wYyw,aOvssg==;
profile-level-id=4D401F
|
- ffmpeg
-flags:v +global_header -c:v libx264 ...
|
|
in-band
|
SPS,PPS in both
stream and manifest
|
x
|
x
|
7,
8, 5, 1, 1... 7,
8, 5, 1, 1... |
- when coding and muxing:
- when transmuxing:
- ffmpeg ... -bsf:v
h264_mp4toannexb ... -c:v copy
...
|
|
| SPS,PPS only in
stream |
-
|
x
|
7,
8, 5, 1, 1... 7,
8, 5, 1, 1...
|
a=fmtp:96
packetization-mode=1
|
- when coding and muxing:
- ffmpeg [-flags:v -global_header] -c:v
libx264 ...
- gst-launch-1.0 ... rtph264pay config-interval=-1
...
- when transmuxing:
- ffmpeg ... -bsf:v
h264_mp4toannexb ... -c:v copy
...
|
- To check which NALs are transmitted:
ffplay -protocol_whitelist
rtp,udp,file,http,https,tcp,tls,rtmp -v debug -i
toto.sdp
- remote:
ffplay -nodisp
-protocol_whitelist
rtp,udp,file,http,https,tcp,tls,rtmp -v debug -i
toto.sdp
- Transmux RTMP to RTP with ffmpeg:
- When using nginx-rtmp-module,
even if rtmp input contains NALs of type 7, 8 every
GOP at the input, RTMP clients only get these type
of NALs at the beginning. If the client is a
transmuxer (e.g. ffmpeg), you must force reintroduction
of NALs of type 7, 8 every GOP, by adding
option
-bsf:v
h264_mp4toannexb
- dump_extra
- Error (ffmpeg 3.4.2):
- ffmpeg
-flags:v +global_header -bsf:v
dump_extra
-c:v copy
...
- only sends NALs of type 1. NALs of type 5
are missing: the stream cannot be played
- waiting
for SPS/PPS when sending H.264 via RTP
-
|
RTP packet |
| one |
several |
| NAL unit |
one |
single |
fragmentation |
| several |
aggregation |
- |
-
- Structure:
| frame |
AU |
layer |
slice |
NAL |
RTP |
| # |
# |
|
|
type |
PT |
SN |
timestamp |
marker |
| - |
0 |
|
|
8: PPS |
(96) |
0 |
0 |
|
| 1 |
1 |
base |
i |
5: slice IDR |
1 |
0 |
0 |
| ii |
5: slice IDR |
2 |
1? |
| enh sp1 |
i |
21: slice IDR scal |
3 |
0 |
| ii |
21: slice IDR scal |
4 |
1? |
| 2 |
2 |
base |
i |
2: slice A |
5 |
3600 |
0 |
| ii |
2: slice A |
6 |
1 |
| 3 |
3 |
base |
i |
28: FU-A |
7 |
7200 |
0 |
| 8 |
0 |
| ii |
1: slice non-IDR |
9 |
1? |
| enh sp1 |
i |
20: slice non-IDR scal |
10 |
0 |
| enh sp2 |
i |
20: slice non-IDR scal |
11 |
1? |
| 4 |
4 |
base |
i |
MTAP |
12 |
14400 |
1 |
| 5 |
5 |
base |
i |
- Problemes / Problems
- Pèrdua de paquets /
Packet loss
- Implementations:
- SDP
- SDP documents and
standards
- SDP
in gstreamer
- RTP
audio video profile (wp)
-
|
|
|
info
|
|
|
v=0
c=IN IP4 ${destination_address}
m=video ${video_rtp_port} RTP/AVP
${rtp_video_payload_type}
a=rtpmap:${rtp_video_payload_type} H264/90000
a=fmtp:${rtp_video_payload_type} ...
m=audio ${audio_rtp_port} RTP/AVP
${rtp_audio_payload_type}
a=rtpmap:${rtp_audio_payload_type}
${audio_media_subtype}/${rate}/${channels}
a=fmtp:${rtp_audio_payload_type} ... |
- RFC 4566
c=<nettype>
<addrtype>
<connection-address>
- <nettype>
- <addrtype>
- <connection-address>
- <base multicast
address>[/<ttl>]/<number
of addresses_default_1>
m=<media>
<port> <proto> <fmt>
- <media>
- <port>
- <proto>
- <fmt>
a=<attribute>:<value>
- ...
|
video
|
H.264
|
m=video 5100
RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;
sprop-parameter-sets=Z2QAH6zZQFAFuhAAAAMAEAAAAwMA8YMZYA==,aOvssiw=;
profile-level-id=64001F
|
|
m=video 5100
RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1 |
VP8
|
m=video 5100
RTP/AVPF 96
a=rtpmap:96 VP8/90000
a=fmtp:96 max-fr=30; max-fs=3600; |
|
VP9
|
m=video 5100
RTP/AVPF 96
a=rtpmap:96 VP9/90000
a=fmtp:96 max-fr=30; max-fs=3600
|
|
| audio |
AAC (48000Hz, 2
channels)
|
m=audio 5102
RTP/AVP 97
a=rtpmap:97 mpeg4-generic/48000/2
|
|
| opus (48000Hz, 2
channels) |
m=audio 5102
RTP/AVP 97
a=rtpmap:97 opus/48000/2
a=fmtp:97 sprop-stereo=1
|
|
- SDP
generats / Generated SDP:
- when a single instance of ffmpeg generates several rtp
streams, the option sdp-file contains all the streams.
To split them, use the following script:
- sdp_split.sh
#!/bin/bash
common_sdp_path=$1
common_sdp_dirname=$(dirname $common_sdp_path)
common_sdp_basename=$(basename
$common_sdp_path)
common_sdp_name=${common_sdp_basename%.*}
# remove all windows line feeds (^M)
sed -i 's/\r//g' ${common_sdp_path}
# get the number of video streams
number_videos=$(grep "m=video"
${common_sdp_path} | wc -l)
for (( video=1; video<=$number_videos;
video++ ))
do
output_sdp_path="${common_sdp_dirname}/${common_sdp_name}_${video}.sdp"
echo "${output_sdp_path}"
awk
-v selected_video=${video} '
BEGIN {block=0; }
#sub(/\r/,"", $0)
/^v=/ {flag=1}
/^s=/ {$0=$0 "_" selected_video}
/^m=video/ {block++}
flag {if (block==0 || block==selected_video)
{print $0}}
' ${common_sdp_path} >${output_sdp_path}
done
exit 0
- RTP (vlc)
v=0
o=- 15549874082820351558 15549874082820351558 IN
IP4 localhost
s=toto
i=N/A
c=IN IP4 234.1.2.3/255
t=0 0
a=tool:vlc 2.1.5
a=recvonly
a=type:broadcast
a=charset:UTF-8
m=audio 1234 RTP/AVP 96
b=RR:0
a=rtpmap:96 mpeg4-generic/48000/6
a=fmtp:96
streamtype=5;
profile-level-id=15;
mode=AAC-hbr;
config=11b0; SizeLength=13; IndexLength=3;
IndexDeltaLength=3; Profile=1;
m=video 1236 RTP/AVP 96
b=RR:0
a=rtpmap:96 H264/90000
a=fmtp:96
packetization-mode=1;profile-level-id=64001f;sprop-parameter-sets=Z2QAH6zZgFAEWhAAAAMAEAAAAwMA8YMZoA==,aOl4TLIs;
- TS over RTP (vlc)
v=0
o=- 15549875101994746492 15549875101994746492 IN
IP4 localhost
s=toto
i=N/A
c=IN IP4 234.1.2.3/255
t=0 0
a=tool:vlc 2.1.5
a=recvonly
a=type:broadcast
a=charset:UTF-8
m=video 5004 RTP/AVP 33
b=RR:0
a=rtpmap:33
MP2T/90000
- ffmpeg -f sap sap://234.1.2.3?same_port=0
(default)
v=0
o=- 0 0 IN IP4 127.0.0.1
s=Sintel
t=0 0
a=tool:libavformat 55.12.100
m=video 5004
RTP/AVP 96
c=IN IP4 234.1.2.3/255
b=AS:1615
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;
sprop-parameter-sets=Z2QAH6zZgFAEWhAAAAMAEAAAAwMA8YMZoA==,aOl4TLIs;
profile-level-id=64001F
m=audio 5006
RTP/AVP 97
c=IN IP4 234.1.2.3/255
b=AS:440
a=rtpmap:97 MPEG4-GENERIC/48000/6
a=fmtp:97
profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;
config=11B0
- ffmpeg -f sap sap://234.1.2.3?same_port=1
v=0
o=- 0 0 IN IP4 127.0.0.1
s=Sintel
t=0 0
a=tool:libavformat 55.12.100
m=video 5004
RTP/AVP 96
c=IN IP4 234.1.2.3/255
b=AS:1615
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;
sprop-parameter-sets=Z2QAH6zZgFAEWhAAAAMAEAAAAwMA8YMZoA==,aOl4TLIs;
profile-level-id=64001F
m=audio 5004
RTP/AVP 97
c=IN IP4 234.1.2.3/255
b=AS:440
a=rtpmap:97 MPEG4-GENERIC/48000/6
a=fmtp:97
profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;
config=11B0
- Tests (only video)
-
|
|
|
|
ffplay
|
gstreamer
|
|
|
|
|
generated SDP
|
sdp
|
sdp
|
caps
|
|
|
|
|
ffplay
-protocol_whitelist
rtp,udp,file,http,https,tcp,tls -i test.sdp |
gst-launch-1.0
filesrc location=test.sdp ! sdpdemux name=demux
demux. ! queue ! decodebin ! autovideosink |
gst-launch-1.0
udpsrc address=127.0.0.1 port=5004 !
"application/x-rtp, media=(string)video,
clock-rate=(int)90000, encoding-name=(string)H264,
packetization-mode=(string)1,
profile-level-id=(string)64001f,
payload=(int)96" ! queue ! rtph264depay !
decodebin ! autovideosink |
gst-launch-1.0
udpsrc address=127.0.0.1 port=5004 !
"application/x-rtp, media=(string)video,
clock-rate=(int)90000, encoding-name=(string)H264,
packetization-mode=(string)1,
profile-level-id=(string)64001f, sprop-parameter-sets=(string)\"...\",
payload=(int)96" ! queue ! rtph264depay !
decodebin ! autovideosink |
ffmpeg
|
test
|
ffmpeg -re -f lavfi
-i "testsrc=size=1280x720:rate=24" -c:v libx264 -b:v
700k -pix_fmt yuv420p -profile:v high -level:v 31 -f
rtp rtp://127.0.0.1:5004 -sdp_file test.sdp
|
SDP:
v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat 57.71.100
m=video 5004 RTP/AVP 96
b=AS:700
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1
|
correct
|
correct
|
There may be a
timestamping problem, or this computer is too slow. |
- sprop from gstreamer test: correct
- sprop from bbb: There may be a timestamping
problem, or this computer is too slow.
|
file
|
ffmpeg -re -i bbb.mp4
-c:v copy -an -f rtp rtp://127.0.0.1:5004 -sdp_file
test.sdp |
SDP:
v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat 57.71.100
m=video 5004 RTP/AVP 96
b=AS:698
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;
sprop-parameter-sets=Z2QAH6zZQFAFuhAAAAMAEAAAAwMA8YMZYA==,aOvssiw=;
profile-level-id=64001F
|
correct
|
videodecoder
gstvideodecoder.c:2775:gst_video_decoder_prepare_finish_frame:<avdec_h264-0>
decreasing
timestamp (0:00:33.104109030 <
0:00:33.126489781)
|
does not start
playing |
- sprop from gstreamer test: correct
- sprop from bbb: There may be a timestamping
problem, or this computer is too slow.
|
gstreamer
|
test
|
gst-launch-1.0
-v videotestsrc !
video/x-raw,framerate=24/1,width=1280,height=720 !
videoconvert ! x264enc !
video/x-h264,stream-format=byte-stream,profile=high
! rtph264pay config-interval=10 pt=96 !
udpsink host=127.0.0.1 port=5004 |
v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat 57.71.100
m=video 5004 RTP/AVP 96
b=AS:700
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1 |
correct
|
correct
|
correct
|
- sprop from gstreamer test: correct
- sprop from bbb: There may be a timestamping
problem, or this computer is too slow.
|
file
|
gst-launch-1.0 -v
filesrc
location=bbb_timecode_1280x720_700k_stereo.mp4 !
qtdemux name=demux demux.video_0 ! rtph264pay config-interval=10 pt=96 !
udpsink host=127.0.0.1 port=5004 |
v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat 57.71.100
m=video 5004 RTP/AVP 96
b=AS:700
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1 |
correct
|
videodecoder
gstvideodecoder.c:2775:gst_video_decoder_prepare_finish_frame:<avdec_h264-0>
decreasing
timestamp (0:00:09.945574289 <
0:00:09.953238304)
|
There may be a
timestamping problem, or this computer is too slow. |
- sprop from gstreamer test: correct (!)
- sprop from bbb (gstreamer verbose): There may
be a timestamping problem, or this computer is
too slow.
|
|
|
|
- RTMP
(wp)
- Estàndard / Standard
- Adobe’s
Real Time Messaging Protocol (pdf) (Dec 21 2012)
- What is
Real-Time Messaging Protocol (RTMP)?
-
|
|
nginx-rtmp-module
logs
|
header
|
Basic Header (BH) |
RTMP bheader |
- Chunk Type /
fmt
- Stream ID /
csid
|
| Chunk Message Header |
RTMP mheader |
- message size /
mlen
- Timestamp Delta /
time
- Message Type
- 0x01 Set Packet Size Message
- 0x02 Abort
- 0x03 Acknowledge
- 0x04 Control Message (not AMF encoded)
- 0x05 Server Bandwidth
- 0x06 Client Bandwidth
- 0x07 Virtual Control
- 0x08 Audio Packet /
audio (8)
- 0x09 Video Packet /
video (9)
- 0x0F Data Extended / (15)
- 0x10 Container Extended / (16)
- 0x11 Command Extended (am AMF3 type
command) / (17)
- 0x12 Data / meta (18)
- 0x13 Container / (19)
- 0x14 Command (An AMF0 type command) /
amf_cmd
(20)
-
commands
|
standard
|
nginx-rtmp-module
log
|
|
|
[debug] AMF func |
[info]
|
7.2.1
NetConnection
|
7.2.1.1 connect
|
'connect'
|
connect: app='my_app'
args='' flashver='' swf_url=''
tc_url='rtmp://192.168.1.138/my_app'
page_url='' acodecs=0
vcodecs=0 object_encoding=0,
client: 192.168.1.135, server:
0.0.0.0:1935 |
| 7.2.1.2 Call |
|
|
| 7.2.1.3 createStream |
'createstream' |
createStream, client:
192.168.1.135, server:
0.0.0.0:1935 |
| 7.2.2
NetStream |
7.2.2.1 play |
|
|
| 7.2.2.2 play2 |
|
|
| 7.2.2.3 deleteStream |
'deletestream' (1) |
deleteStream, client:
192.168.1.135, server:
0.0.0.0:1935 |
| 7.2.2.4 receiveAudio |
|
|
| 7.2.2.5 receiveVideo |
|
|
| 7.2.2.6 publish |
'publish' |
publish: name='sintel'
args='' type=live silent=0,
client: 192.168.1.135, server:
0.0.0.0:1935 |
|
'@setdataframe' |
[debug] codec: data
frame: width=640 height=360
duration=0 frame_rate=24
video=H264 (7) audio=AAC (10) |
| 7.2.2.7 seek |
|
|
| 7.2.2.8 pause |
|
|
- (1)
[debug]
AMF func 'deletestream' is not
present when connection is closed by drop_idle_publisher:
[error] live: drop idle publisher,
client: 192.168.1.135, server:
0.0.0.0:1935
- 0x15 UDP / (21)
- 0x16 Aggregate / (22)
- 0x17 Present / (23)
|
|
|
|
|
- Timestamps
- Ús / Usage
|
|
|
- Common Media Application Format - CMAF
(MPEG)
- MPEG-DASH
- HTTP Live
Streaming - HLS
- HTTP
Live Streaming draft-pantos-http-live-streaming (IETF)
- HTTP
Live
Streaming Overview (Apple iPhone development center)
- HLS
Tags: Everything you need to know (MUX)
- The
HLS format (broadpeak.io)
- codecs
- video:
- H.264/AVC baseline level 3.0
- audio:
- HE-AAC or AAC-LC up to 48 kHz, stereo audio
- HTTP-Live-Video-Stream-Segmenter-and-Distributor
(Carson McDonald)
- iPhone
HTTP
Streaming with FFMpeg and an Open Source Segmenter
- iPhone
Windowed
HTTP Live Streaming Using Amazon S3 and Cloudfront Proof
of Concept
- Streaming
to
the iPhone and iPod Touch (live, vod, multi-bitrate)
- Streaming
HowTo/Streaming
for the iPhone (Videolan)
- Galaxia HasDevTool
- Best practices
- Bit
rate recommendations
- "The first entry in the master playlist will be
played at the initiation of a stream and is used as
part of a test to determine which stream is most
appropriate."
- Xifratge / Encryption
- Subtítols / Subtitles
- convert srt to webvtt
ffmpeg -i en.srt -f webvtt en.webvtt
ffmpeg -i ca.srt -f webvtt ca.webvtt
- index.m3u8
#EXTM3U
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English",FORCED=NO,AUTOSELECT=YES,URI="en.ttml",LANGUAGE="en"
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Catalan",FORCED=NO,AUTOSELECT=YES,URI="ca.ttml",LANGUAGE="ca"
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=928000,RESOLUTION=640x360,SUBTITLES="subs"
360p.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2592000,RESOLUTION=1280x720,SUBTITLES="subs"
720p.m3u8
#EXT-X-ENDLIST
- ca.ttml
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:3600
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:3600.0,
ca.webvtt
#EXT-X-ENDLIST
- en.ttml
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:3600
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:3600.0,
en.webvtt
#EXT-X-ENDLIST
- Monitor
- AWS
- Monitor
HLS and DASH live streams using a canary monitor
- aws-samples / monitor-hls-and-dash-streams-using-canary-monitor
- Setup
- AWS permissions
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Statement1",
"Effect": "Allow",
"Action": [
"cloudwatch:PutMetricData",
"cloudwatch:ListDashboards",
"cloudwatch:GetDashboard",
"cloudwatch:DeleteDashboards",
"cloudwatch:PutDashboard"
],
"Resource": "*"
}
]
}
- script
- config
- origins/demo.csv (all *.csv files in this dir
will be read)
# endpoint
type (live), technology
(hls/dash), workload
name, endpoint
name, origin
name, monitoring config file,
manifest url, tracking url [optional]
live,
hls,
my_workload,
my_endpoint,
my_origin,
configs/default.json,
https://myserver.org/path/to/index.m3u8
live,
hls,
lab,
testendpoint,
emp,
configs/default.json,
https://xxxxxx.mediapackage.us-west-2.amazonaws.com/out/v1/xxxxxx/index.mpd
- Run
- standalone
./canarymonitor.py --region eu-west-1
- service
- Generated files
- archive
live
my_workload
my_origin
my_endpoint
hls
- reports
<begin_datetime>_to_<end_datetime>.json
- AWS CloudWatch
- Dashboards > Custom dashboards > <
WORKLOAD_NAME>-<Origin_name>-Canary-Monitor
- verify that you have AWS permissions to
manage "
cloudwatch:...Dashboards"
- Metrics > All metrics > Browse > Custom
namespaces > CanaryMonitor
- Workload:
...
- Endpoint
- Origin
- Rendition
multi: index.m3u8
v1: first_variant.m3u8
- RequestType: manifest, ...
- Technology:
hls, dash
- Type:
live
- Metric
name: Latency, BufferFillDuration, ...
- Alarms
- Eyevinn
- Eines / Tools
- show cumulative time based on m3u8:
awk -F: '/^#EXTINF/ {gsub(/,/,"",$2); sum +=
$2; print $2 " " sum} /.ts/ {print $1} END {print
sum}' monovariant.m3u8
- hls_info.sh
#!/bin/bash
m3u8_path=$1
sum=0
while read -r line
do
if [[ ${line} =~ ^"#EXTINF" ]]
then
#echo
${line}
duration=$(echo ${line} | awk -F: '/^#EXTINF/
{gsub(/,/,"",$2); print $2}')
echo -n
"---- start: ${sum} duration: ${duration} "
sum=$(
echo "${sum}+${duration}" | bc -l)
echo -n
"end: ${sum} "
fi
if [[ ${line} =~ "ts"$ ]]
then
echo
"<-- ${line}"
ffprobe
-v 0 -show_entries frame=pkt_pts_time,pict_type
-print_format compact=nk=1:p=0:s='\ '
-select_streams v:0 -i ${line}
fi
done < ${m3u8_path}
echo "sum: ${sum}"
exit 0
- ts2m3u8.sh
#!/bin/bash
function print_help_and_exit {
cat <<EOF
Usage: `basename $0` ts_dir ts_prefix ts_start
ts_end
Generate <ts_prefix>.gen.m3u8 file with
reference to all ts files, in ts_dir, with file
name:
<ts_prefix><ts_start>.ts ..
<ts_prefix><ts_stop>.ts
Examples:
- `basename $0` /tmp stream_low 4 55
EOF
}
ts_dir=$1
ts_prefix=$2
ts_start=$3
ts_end=$4
function m3u8_header {
# m3u8 header
local m3u8_path=$1
# TODO: calculate target
duration from segments
cat >${m3u8_path}
<<EOF
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:1
EOF
}
function m3u8_footer {
# m3u8 footer
local m3u8_path=$1
cat >>${m3u8_path}
<<EOF
#EXT-X-ENDLIST
EOF
}
# output m3u8 file
m3u8_path=${ts_prefix}.gen.m3u8
# create m3u8 header
m3u8_header ${m3u8_path}
# get all ts files
ts_list=$(eval ls -vx
${ts_dir}/${ts_prefix}{$ts_start..$ts_end}.ts)
for ts_path in ${ts_list}
do
# get duration in seconds (with
decimals)
duration=$(ffprobe -i $ts_path
-show_format -v quiet | sed -n 's/duration=//p')
# keep only numbers and decimal
point (discard N/A):
duration=$(echo $duration | tr
-d -c 0-9.)
echo ${ts_path} ${duration}
ts_basename=$(basename
$ts_path)
# put information into m3u8
output file
echo "#EXTINF:${duration},"
>>${m3u8_path}
echo ${ts_basename}
>>${m3u8_path}
done
# create m3u8 footer
m3u8_footer ${m3u8_path}
exit 0
- FAST
- Tools
- Eyevinn
Channel Engine (VOD2Live)
- Create
your own FAST Channels based on VOD2Live
Technology and Open Source Components
- Docker examples based on docker-fast
- Components
- Library: Eyevinn
Channel Engine
- Implementation with plugins: FAST
by Eyevinn Technology (FAST Channel
Engine)
- Docker
- Available
plugins
- Demo
- ScheduleService
- manages channels and schedules
- uses AWS DynamoDB (it can be
local)
- Loop
- Playlist
- Barker
- WebHook
- ...
|
info |
plugin docker |
plugin access |
FAST Engine docker |
docker compose
docker compose up
|
hls player |
| Loop |
repeats a single url |
|
|
docker run --rm -it -p 8000:8000
\
-e FAST_PLUGIN=Loop
\
-e LOOP_VOD_URL=<uri-to-hls-vod>
\
-e LOOP_CHANNEL_NAME=testchannel
\
eyevinntechnology/fast-engine |
|
http://localhost:8000/channels/testchannel/master.m3u8 |
| Playlist |
retrieves a playlist and plays
it from the top |
(any static http server)
E.g.: nginx
docker run -it --rm -p
8888:80 \
-v
/tmp/html:/usr/share/nginx/html
--name web nginx
Host file:
/tmp/html/playlist.txt
|
|
docker run --rm -it -p 8000:8000
\
-e FAST_PLUGIN=Playlist
\
-e PLAYLIST_URL=http://<container_ip_address>:80/playlist.txt
\
-e PLAYLIST_CHANNEL_NAME=testchannel
\
eyevinntechnology/fast-engine |
docker-compose.yaml
services:
web:
image:
nginx
ports:
- 8888:80
volumes:
-
/tmp/html:/usr/share/nginx/html
engine:
image:
eyevinntechnology/fast-engine
environment:
- FAST_PLUGIN=Playlist
-
PLAYLIST_URL=http://web/playlist.txt
-
PLAYLIST_CHANNEL_NAME=testchannel
ports:
- 8000:8000 |
http://localhost:8000/channels/testchannel/master.m3u8 |
| ScheduleService |
|
DynamoDB:
docker run --rm -d -p 6000:8000
amazon/dynamodb-local
ScheduleService:
docker run --rm -p 8080:8080
\
-e
DB=dynamodb://host.docker.internal:6000/eu-north-1
\
-e
AWS_ACCESS_KEY_ID=null \
-e
AWS_SECRET_ACCESS_KEY=null \
-e IF=0.0.0.0 \
eyevinntechnology/schedule-service |
API docs:
http://localhost:8080/api/docs
Channel list:
curl -X 'GET' \
'http://localhost:8080/api/v1/channels'
\
-H 'accept:
application/json'
Current schedule for
channel eyevinn:
curl -X 'GET' \
'http://localhost:8080/api/v1/channels/eyevinn/schedule?date=2023-03-06'
\
-H 'accept:
application/json'
Create a new channel:
curl -X 'POST' \
'http://localhost:8080/api/v1/channels'
\
-H 'accept:
application/json' \
-H 'Content-Type:
application/json' \
-d '{
"id": "mychannel",
"tenant": "test",
"title": "My channel"
}'
|
docker run --rm -p 8000:8000
\
-e
OPTS_USE_DEMUXED_AUDIO=false \
-e FAST_PLUGIN=ScheduleService
\
-e SCHEDULE_SERVICE_API_URL=http://host.docker.internal:8080/api/v1
\
eyevinntechnology/fast-engine |
DynamoDB +
ScheduleService +
FAST Engine:
docker-compose.yml |
http://localhost:8000/channels/eyevinn/master.m3u8
http://localhost:8000/channels/mychannel/master.m3u8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Descàrrega progressiva /
Progressive download
|
- General
- Estàndards / Standards
- HTTP:
- MP4 progressive download
|
|
|
|
|
|
|
|
|
|
|
Conferències / Conferences
|
|
|
|
|
| Platform |
Components |
Technology |
Where / affiliates |
Pressplay
(Duet) |
- Sony
- Vivendi Universal
- EMI
- Madacy, Navarre, OWIE, Razor & Tie, Roadrunner,
Rounder
- TVT Records
- Zomba
|
|
|
| Musicnet |
|
|
|
|
|
Tornado |
|
|
Xarxes CDN / Content Delivery Networks (CDN)
|
|
|
|
|
- SRT Alliance
- Documents
- Haivision / srt
(GitHub)
- Info
- Programari / Software
- Biblioteques / Libraries
- Haivision / srt
(GitHub)
- needed to be installed before compiling gstreamer, ffmpeg, sls
- dependencies
- Mageia
dnf install cmake tcl
libopenssl-devel
- CentOS
sudo yum install cmake tcl
- compilació / compilation
cd src
git clone
https://github.com/Haivision/srt.git
cd srt
./configure
make
sudo make install
- NOTE: srt.pc (needed by:
pkg-config --exists srt)
will be installed to /usr/local/lib64/pkgconfig/
. You may need to update environment variable
accordingly before configuring the compilation
of other packages:
export
PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig/:/usr/local/lib/pkgconfig/
- update ldconfig
echo '/usr/local/lib64'
>/etc/ld.so.conf.d/local64.conf
sudo ldconfig
- eines / tools
-
|
encoders |
servers |
players |
| software |
|
|
|
| maquinari / hardware |
|
|
|
- TSDuck
- Instal·lació / Installation
- from packages
- from source (Building
TSDuck)
git clone https://github.com/tsduck/tsduck.git
- cd
tsduck
- git
checkout v3.40-4165
- sudo
scripts/install-prerequisites.sh
- make
-j10 default docs-html
- sudo
make install
- Transport Stream Utilities
- Usage
- MediaMTX
- SRT
- Escalabilitat / Scalability
- ...
|
|
publish from client |
publish from server |
play
|
|
|
url |
configuration
file (mediamrx.yml)
|
url |
SRT
|
SRT
clients |
srt://localhost:8890?streamid=publish:mystream&pkt_size=1316
- ffmpeg -re -stream_loop -1 -i file.ts -c
copy -f mpegts
'srt://localhost:8890?streamid=publish:mystream&pkt_size=1316'
|
|
srt://localhost:8890?streamid=read:mystream
- FFmpeg
and SRT
- ffmpeg -i
'srt://localhost:8890?streamid=mystream:test'
-c copy output.mp4
|
|
SRT
cameras and servers |
|
paths:
proxied:
# url of the source stream,
in the format
srt://host:port?streamid=streamid&other_parameters
source: srt://original-url |
|
WebRTC
|
WebRTC
clients |
- WHIP (no web page):
http://localhost:8889/mystream/whip
- ffmpeg -re -f lavfi -i
testsrc=size=1280x720:rate=30 \
-f lavfi -i
"sine=frequency=1000:sample_rate=48000" \
-c:v libx264 -pix_fmt yuv420p -preset
ultrafast -b:v 600k \
-c:a libopus -ar 48000 -ac 2 -b:a 128k \
-f whip http://localhost:8889/stream/whip
- gst-launch-1.0 videotestsrc \
!
video/x-raw,width=1920,height=1080,format=I420
\
! x264enc speed-preset=ultrafast
bitrate=2000 \
! video/x-h264,profile=baseline \
! whipclientsink
signaller::whip-endpoint=http://localhost:8889/mystream/whip
- OBS
Studio and WebRTC
- Unity
|
|
- web browser
- WHEP (no whep browser)
- http://localhost:8889/mystream/whep
- GStreamer
and WebRTC
- gst-launch-1.0 whepsrc
whep-endpoint=http://127.0.0.1:8889/stream/whep
use-link-headers=true \
video-caps="application/x-rtp,media=video,encoding-name=H264,payload=127,clock-rate=90000"
\
audio-caps="application/x-rtp,media=audio,encoding-name=PCMU,payload=0,clock-rate=8000"
\
! rtph264depay ! decodebin !
autovideosink
- Unity
|
|
WebRTC
servers
|
|
yaml:
paths:
proxied:
# url of the source stream,
in the format whep://host:port/path (HTTP) or
wheps:// (HTTPS)
source:
wheps://host:port/path
if remote server is a MediaMTX server:
paths:
proxied:
source:
whep://host:port/mystream/whep |
|
RTSP
|
RTSP
clients
|
rtsp://localhost:8554/mystream
- ffmpeg -re -stream_loop -1 -i file.ts -c
copy -f rtsp rtsp://localhost:8554/mystream
- gst-launch-1.0 rtspclientsink name=s
location=rtsp://localhost:8554/mystream \
filesrc location=file.mp4 ! qtdemux name=d \
d.video_0 ! queue ! s.sink_0 \
d.audio_0 ! queue ! s.sink_1
- OpenCV
|
|
rtsp://localhost:8554/mystream
- FFmpeg
and RTSP
- ffmpeg -i rtsp://localhost:8554/mystream
-c copy output.mp4
- GStreamer
and RTSP
- gst-launch-1.0 rtspsrc
location=rtsp://127.0.0.1:8554/mystream
latency=0 ! decodebin ! autovideosink
- VLC
- vlc --network-caching=50
rtsp://localhost:8554/mystream
- OBS
Studio
|
|
RTSP
cameras and servers
|
|
yaml
paths:
proxied:
# url of the source stream,
in the format rtsp://user:pass@host:port/path
source: rtsp://original-url |
/proxied
|
| RTMP |
RTMP
clients |
rtmp://localhost/mystream
- ffmpeg -re -stream_loop -1 -i file.ts -c
copy -f flv rtmp://localhost:1935/mystream
- gst-launch-1.0 -v flvmux name=mux !
rtmpsink location=rtmp://localhost/stream \
videotestsrc !
video/x-raw,width=1280,height=720,format=I420
! x264enc speed-preset=ultrafast
bitrate=3000 key-int-max=60 !
video/x-h264,profile=high ! mux. \
audiotestsrc ! audioconvert ! avenc_aac !
mux.
- OBS
studio and RTMP
|
|
rtmp://localhost/mystream
- FFmpeg
and RTMP
- ffmpeg -i rtmp://localhost/mystream -c
copy output.mp4
- ffmpeg -rtmp_enhanced_codecs
ac-3,av01,avc1,ec-3,fLaC,hvc1,.mp3,mp4a,Opus,vp09
\
-i rtmp://localhost/mystream -c copy
output.mp4
|
|
RTMP
cameras and servers |
|
yaml
paths:
proxied:
# url of the source stream,
in the format rtmp://user:pass@host:port/path
source: rtmp://original-url |
|
| HLS |
HLS
cameras and servers |
|
yaml
paths:
proxied:
# url of the playlist of
the stream, in the format
http://user:pass@host:port/path
source:
http://original-url/stream/index.m3u8 |
|
| MPEG-TS |
MPEG-TS |
- udp+mpegts
- ffmpeg -re -stream_loop -1 -i file.ts -c
copy -f mpegts
'udp://127.0.0.1:3356?pkt_size=1316'
- gst-launch-1.0 -v mpegtsmux name=mux
alignment=1 ! udpsink host=238.0.0.1
port=1234 \
videotestsrc !
video/x-raw,width=1280,height=720,format=I420
! x264enc speed-preset=ultrafast
bitrate=3000 key-int-max=60 !
video/x-h264,profile=high ! mux. \
audiotestsrc ! audioconvert ! avenc_aac !
mux.
- unix+mpegts
- ffmpeg -re -f lavfi -i
testsrc=size=1280x720:rate=30 \
-c:v libx264 -pix_fmt yuv420p -preset
ultrafast -b:v 600k \
-f mpegts unix:/tmp/socket.sock
|
udp+mpegts
paths:
mypath:
source:
udp+mpegts://238.0.0.1:1234
unix+mpegts
paths:
mypath:
source:
unix+mpegts:///tmp/socket.sock
|
|
| RTP |
RTPhttps://mediamtx.org/docs/usage/publish#rtp |
- udp+rtp
- ffmpeg -re -f lavfi -i
testsrc=size=1280x720:rate=30 \
-c:v libx264 -pix_fmt yuv420p -preset
ultrafast -b:v 600k \
-f rtp udp://238.0.0.1:1234?pkt_size=1316
- unix+rtp
- ffmpeg -re -f lavfi -i
testsrc=size=1280x720:rate=30 \
-c:v libx264 -pix_fmt yuv420p -preset
ultrafast -b:v 600k \
-f rtp unix:/tmp/socket.sock
|
udp+rtp
paths:
mypath:
source:
udp+rtp://238.0.0.1:1234
rtpSDP: |
v=0
o=- 123456789
123456789 IN IP4 192.168.1.100
s=H264 Video
Stream
c=IN IP4
192.168.1.100
t=0 0
m=video 5004
RTP/AVP 96
a=rtpmap:96
H264/90000
a=fmtp:96
profile-level-id=42e01e;packetization-mode=1;sprop-parameter-sets=Z0LAHtkDxWhAAAADAEAAAAwDxYuS,aMuMsg==
unix+rtp
paths:
mypath:
source:
unix+rtp:///tmp/socket.sock
rtpSDP: |
v=0
o=- 123456789
123456789 IN IP4 192.168.1.100
s=H264 Video
Stream
c=IN IP4
192.168.1.100
t=0 0
m=video 5004
RTP/AVP 96
a=rtpmap:96
H264/90000
a=fmtp:96
profile-level-id=42e01e;packetization-mode=1;sprop-parameter-sets=Z0LAHtkDxWhAAAADAEAAAAwDxYuS,aMuMsg== |
|
| RPi |
Raspberry
Pi cameras |
|
paths:
cam:
source: rpiCamera |
/cam |
|
Adding audio |
|
|
|
|
Secondary stream |
|
|
|
| Webcam |
Generic
webcams |
|
paths:
cam:
runOnInit: ffmpeg -f v4l2
-i /dev/video0 -c:v libx264 -pix_fmt yuv420p
-preset ultrafast -b:v 600k -f rtsp
rtsp://localhost:$RTSP_PORT/$MTX_PATH
runOnInitRestart: yes |
/cam |
- Documentation
- Publish to the server
- Read from server
- SRS
- SRT
Live Server
-
| fork |
pros
|
cons
|
| Edward-Wu |
|
- stats not working
- minor change to make it compile in Ubuntu
24.04 (make
error #139):
sed -i '/#include
<sys\/time.h>/ i #include
<time.h>' slscore/common.cpp
|
| odensc |
|
- minor change to make it compile in Ubuntu
24.04 (make
error #139):
sed -i '/#include
<sys\/time.h>/ i #include
<time.h>' slscore/common.cpp
|
| OpenIRL |
|
|
| irlserver |
|
- latest version (...) is not compiling
- no git labels
|
| rstular |
|
|
- Docker
- ravenium/srt-live-server
(Docker Hub)
- ravenium/srt-live-server
(GitHub)
- option a: from public repo DockerHub:
- run container:
docker run -d -p
1935:1935/udp
ravenium/srt-live-server
- or run compose (not working on Mageia
Linux):
- mkdir
-p logs; chmod a+w logs; docker
compose up
- option b: build your own image and run
from it:
- clone repo
cd src
git clone
https://github.com/ravenium/srt-live-server.git
cd srt-live-server
- build local image
docker build -t
local-srt-live-server .
- verify:
- run container:
docker run -d -p
1935:1935/udp
local-srt-live-server
- verify that container is running:
- or run from compose:
- local-docker-compose.yaml
version:
'3'
services:
srt:
image:
local-srt-live-server
ports:
- "1935:1935/udp"
volumes:
- ./sls.conf:/etc/sls/sls.conf
- ./logs:/logs
docker compose --file
local-docker-compose.yml up
- compilació / compilation
cd src
git clone
https://github.com/Edward-Wu/srt-live-server.git
cd srt-live-server
# path to find libsrt
export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig/:/usr/local/lib/pkgconfig/
- make
- config
- ús / usage
cd src/srt-live-server/bin
./sls -c ../sls.conf
port=8080
server_ip="192.168.1.141"
- transmit with ffmpeg (caller):
ffmpeg -re -f lavfi -i
testsrc=size=320x240:r=15 -f lavfi -i
anoisesrc=a=0.2 -c:v libx264 -profile:v
baseline -b:v 1M -pix_fmt yuv420p -c:a aac
-b:a 16k -f mpegts "srt://${server_ip}:${port}?streamid=uplive.sls.com/live/test"
- transmit an existing file with srt-live-client
(bin/slc)
./slc -r
srt://${server_ip}:${port}?streamid=uplive.sls.com/live/test
-i source_file.ts
- transmit with obs (compiled wit srt-enabled
ffmpeg):
- Server:
srt://192.168.1.141:8080?streamid=uplive.sls.com/live/test
- Key: (empty)
- receive with ffplay (caller):
ffplay -i "srt://${server_ip}:${port}?streamid=live.sls.com/live/test"
- receive with srt-live-client (bin/slc) and
generate a file:
./slc -r
srt://${server_ip}:${port}?streamid=live.sls.com/live/test
-o destination_file.ts
- estadístiques / statistics
- NOTE: use odensc
fork
- sls.conf
srt {
stat_post_url
http://127.0.0.1/unit/sls/stat ;
stat_post_interval 5;
}
- unit behind
nginx
- unit.conf
location /unit/ {
proxy_pass
http://127.0.0.1:8090/;
proxy_set_header Host
$host;
proxy_set_header
X-Forwarded-For
$proxy_add_x_forwarded_for;
}
- /etc/fastapi/
- asgi.py
from pathlib
import Path
from fastapi import FastAPI, Request
from fastapi.templating import
Jinja2Templates
from pydantic import BaseModel
app = FastAPI()
# variable to store the values received by
post
app.statistics = []
# absolute path for templates
BASE_DIR = Path(__file__).resolve().parent
templates =
Jinja2Templates(directory=str(Path(BASE_DIR,
"templates")))
class Statistics(BaseModel):
port: str
role: str
pub_domain_app: str
stream_name: str
url: str
remote_ip: str
remote_port: str
start_time: str
kbitrate: str
@app.get("/sls/statistics")
async def get_statistics(request:
Request):
"""
Return html table with
statistics
"""
# print(app.statistics)
context = [st.dict()
for st in app.statistics]
return
templates.TemplateResponse(
request=request,
name="statistics.html",
context={"statistics": context},
)
@app.get("/sls/stat")
async def get_stat():
return app.statistics
@app.post("/sls/stat")
async def post_stat(statistics:
list[Statistics]):
# curl -i -X POST -H
'Content-Type: application/json' --data
'[{"port": "8080","role":
"player","pub_domain_app":
"uplive.sls.com/live","stream_name":
"test","url":
"live.sls.com/live/test","remote_ip":
"192.168.1.146","remote_port":
"60788","start_time": "2025-04-25
17:54:43","kbitrate": "1724"}]'
http://localhost:8090/sls/stat
# sample body:
# [
# {
#
"port": "8080",
#
"role": "player",
#
"pub_domain_app": "uplive.sls.com/live",
#
"stream_name": "test",
#
"url": "live.sls.com/live/test",
#
"remote_ip": "192.168.1.146",
#
"remote_port": "60788",
#
"start_time": "2025-04-25 17:54:43",
#
"kbitrate": "1724",
# },
# {
#
"port": "8080",
#
"role": "publisher",
#
"pub_domain_app": "uplive.sls.com/live",
#
"stream_name": "test",
#
"url": "uplive.sls.com/live/test",
#
"remote_ip": "192.168.1.146",
#
"remote_port": "36141",
#
"start_time": "2025-04-25 17:53:45",
#
"kbitrate": "1745",
# },
# {
#
"port": "8080",
#
"role": "listener",
#
"pub_domain_app": "",
#
"stream_name": "",
#
"url": "",
#
"remote_ip": "",
#
"remote_port": "",
#
"start_time": "2025-04-25 17:52:31",
#
"kbitrate": "0",
# },
# ]
# body = await
request.body()
# print(body)
# return {"status":
"OK"}
app.statistics =
statistics
return statistics
@app.post("/sls/on_event")
async def on_event(
on_event: str,
role_name: str, srt_url: str, remote_ip:
str, remote_port: int
):
# "POST
/sls/on_event?on_event=on_connect&role_name=publisher&srt_url=publish/stream/fa0c67a2d70bb2c2-5464f06ed01a6b6e&remote_ip=139.47.125.46&remote_port=39642
HTTP/1.1" 200 OK
# "POST
/sls/on_event?on_event=on_close&role_name=publisher&srt_url=publish/stream/fa0c67a2d70bb2c2-5464f06ed01a6b6e&remote_ip=139.47.125.46&remote_port=39642
HTTP/1.1" 200 OK
# on_event=on_connect
# role_name=publisher
#
srt_url=publish/stream/fa0c67a2d70bb2c2-5464f06ed01a6b6e&
#
remote_ip=139.47.125.46
# remote_port=39642
# body = await
request.body()
# print(body)
print(" on_event:
{}".format(on_event))
print("
role_name: {}".format(role_name))
print(" srt_url:
{}".format(srt_url))
print("
remote_ip: {}".format(remote_ip))
print("
remote_port: {}".format(remote_port))
return {"status": "OK"}
- templates/statistics.html
<html>
<head>
<title>Statistics</title>
</head>
<body>
<h1>Statistics</h1>
<table
border="1">
<tr>
<th>port</th>
<th>role</th>
<th>pub_domain_app</th>
<th>stream_name</th>
<th>url</th>
<th>remote_ip</th>
<th>remote_port</th>
<th>start_time</th>
<th>kbitrate</th>
</tr>
{%
for stat in statistics %}
<tr>
<td>{{ stat.port
}}</td>
<td>{{ stat.role
}}</td>
<td>{{
stat.pub_domain_app }}</td>
<td>{{
stat.stream_name }}</td>
<td>{{ stat.url
}}</td>
<td>{{
stat.remote_ip }}</td>
<td>{{
stat.remote_port }}</td>
<td>{{
stat.start_time }}</td>
<td>{{
stat.kbitrate }}</td>
</tr>
{%
endfor %}
</table>
</body>
</html>
- set config
- get config:
curl -X GET --unix-socket
/var/run/control.unit.sock
http://localhost:8090/config/
- get statistics:
- http://my_server/unit/sls/statistics
- modes (Medium:
SRT)
| mode |
host |
port |
| caller |
remote host |
remote port |
| listener |
|
local listening port |
| rendezvous |
other public host |
local and remote port |
- ...
|
conf |
publish |
play |
| SLS (Edward-Wu) |
# sls.conf
server {
listen 8080;
domain_player live.sls.com
live-1.sls.com;
domain_publisher uplive.sls.com;
app {
app_player live;
app_publisher live;
}
} |
srt://[your.sls.ip]:8080?streamid=uplive.sls.com/live/test |
srt://[your.sls.ip]:8080?streamid=live.sls.com/live/test |
| SLS (OpenIRL) |
# sls.conf
server {
listen 4001;
domain_player play;
domain_publisher publish;
app {
app_player stream;
app_publisher stream;
}
} |
srt://[your.sls.ip]:4001?streamid=publish/stream/test
|
srt://[your.sls.ip]:4001?streamid=play/stream/test
|
| MediaMTX |
# mediamtx.yml
|
- custom streamid syntax
srt://localhost:8890?streamid=publish:mystream&pkt_size=1316
srt://localhost:8890?streamid=publish:mystream:user:pass&pkt_size=1316
- standard
streamid syntax (SRT
Access Control (Stream ID) Guidelines):
srt://localhost:8890?streamid=#!::m=publish,r=mypath,u=myuser,s=mypass&pkt_size=1316
m=<mode:
publish, request,
bidirectional>
- r=<resource_name>
- u=<username>
- s=<session_id>
- h=<host_name>
- t=<type:
stream, file, auth>
|
- custom streamid syntax
srt://localhost:8890?streamid=read:mystream
srt://localhost:8890?streamid=read:mystream:user:pass
- standard
streamid syntax:
|
-
|
transmitter Tx |
receiver Rx |
|
gst-launch-1.0 -v videotestsrc !
"video/x-raw,height=360,width=640" ! videoconvert
! x264enc tune=zerolatency !
"video/x-h264,profile=baseline" ! mpegtsmux !
srtsink uri="srt://:8888/" |
gst-launch-1.0 -v srtsrc
uri=srt://127.0.0.1:8888 ! decodebin !
autovideosink
|
|
ffmpeg -re -f lavfi -i
testsrc=size=320x240:r=15 -f lavfi -i
sine=f=440:b=1 -c:v libx264 -profile:v baseline
-b:v 1M -pix_fmt yuv420p -c:a aac -b:a 16k -f
mpegts srt://:8888
|
ffplay srt://127.0.0.1:8888
|
- transmitter is a client
(caller)
- receiver is a server
(listener; should be started first)
|
port=9998
receiver_ip="192.168.1.141"
- ffmpeg
...
-f mpegts "srt://${receiver_ip}:${port}?pkt_size=1316"
- from test source:
ffmpeg -re -f lavfi -i
testsrc=size=320x240:r=15 -f lavfi -i
anoisesrc=a=0.2 -c:v libx264 -profile:v
baseline -b:v 1M -pix_fmt yuv420p -c:a aac
-b:a 16k -f mpegts "srt://${receiver_ip}:${port}?pkt_size=1316"
gst-launch-1.0 -v videotestsrc !
"video/x-raw,height=360,width=640" !
videoconvert ! x264enc tune=zerolatency !
"video/x-h264,profile=baseline" !
mpegtsmux ! srtsink uri="srt://${receiver_ip}:${port}/"
- transmux from udp:
ffmpeg -fflags +genpts -listen 1 -re
-i udp://239.0.0.1:1234?pkt_size=1316
-acodec copy -vcodec copy -strict -2 -y -f
mpegts "srt://${receiver_ip}:${port}?pkt_size=1316"
|
port=9998
ffplay -i "srt://0.0.0.0:${port}?pkt_size=1316&mode=listener"
ffmpeg -re -i "srt://0.0.0.0:${port}?pkt_size=1316&mode=listener"
...
gst-launch-1.0 -v srtsrc
uri="srt://:${port}" mode=listener ! decodebin
! autovideosink
- transmux to udp:
ffmpeg -re -i "srt://0.0.0.0:${port}?pkt_size=1316&mode=listener"
-vcodec copy -acodec copy -strict -2 -y -f
mpegts udp://239.0.0.2:1234?pkt_size=1316
|
- transmitter is a server
(listener; should be started first)
- receiver is a client
(caller)
|
port=9998
- ffmpeg
...
-f mpegts "srt://0.0.0.0:${port}?pkt_size=1316&mode=listener"
- from test source:
ffmpeg -re -f lavfi -i
testsrc=size=320x240:r=15 -f lavfi -i
anoisesrc=a=0.2 -c:v libx264 -profile:v
baseline -b:v 1M -pix_fmt yuv420p -c:a aac
-b:a 16k -f mpegts "srt://0.0.0.0:${port}?pkt_size=1316&mode=listener"
gst-launch-1.0 -v videotestsrc !
"video/x-raw,height=360,width=640" !
videoconvert ! x264enc tune=zerolatency !
"video/x-h264,profile=baseline" !
mpegtsmux ! srtsink uri="srt://${receiver_ip}:${port}/"
mode=listener
- transmux from udp (e.g. 239.0.0.1:1234):
ffmpeg -fflags +genpts -listen 1 -re
-i udp://239.0.0.1:1234?pkt_size=1316
-acodec copy -vcodec copy -strict -2 -y -f
mpegts "srt://0.0.0.0:${port}?pkt_size=1316&mode=listener"
|
port=9998
transmitter_ip="192.168.1.141"
ffplay -i "srt://${transmitter_ip}:${port}?pkt_size=1316"
ffmpeg -re -i "srt://${transmitter_ip}:${port}?pkt_size=1316"
...
gst-launch-1.0 -v srtsrc uri="srt://${transmitter_ip}:${port}"
! decodebin ! autovideosink
- transmux to udp:
ffmpeg -re -i "srt://${transmitter_ip}:${port}?pkt_size=1316"
-vcodec copy -acodec copy -strict -2 -y -f
mpegts udp://239.0.0.2:1234?pkt_size=1316
|
|
|
port=9998
receiver_public_ip="192.168.1.141"
- from test source:
ffmpeg -re -f lavfi -i
testsrc=size=320x240:r=15 -f lavfi -i
anoisesrc=a=0.2 -c:v libx264 -profile:v
baseline -b:v 1M -pix_fmt yuv420p -c:a aac
-b:a 16k -f mpegts "srt://${receiver_public_ip}:${port}?pkt_size=1316&mode=rendezvous"
|
port=9998
transmitter_public_ip="192.168.1.141"
ffplay -i
"srt://${transmitter_public_ip}:${port}?pkt_size=1316&mode=rendezvous"
|
- NOTE: use
pkt_size=1316
to allocate 7 TS packets into one IP packet: 188*7=1316 <
1500
- Problemes / Problems
- GStreamer logs:
No room to store incoming
packet
|
|
|
- Info
- Programari / Software
|
|
|
|
|
|
|
|
|
|
|
- Estaǹdards / Standards
- MPEG-B Systems,
part 7 (23001-7): Common encryption on ISO-base file format
- OMA DRM content file:
- OMA DCF 1.0
- OMA DCF 2.0, 2.1
- OMA DCF 2.0, 2.1
- ...
-
|
|
|
|
Bento4 |
|
info / standard
|
used by |
details
|
mp4dcfpackager
-method
|
mp4encrypt
-method
|
OMA DRM
DCF
|
- OMA DCF 1.0
- OMA DCF 2.0, 2.1
|
|
|
CBC
|
|
|
|
|
CTR
|
|
OMA DRM
PDCF
|
|
|
|
|
OMA-PDCF-CBC |
|
|
|
|
OMA-PDCF-CTR |
| MARLIN |
|
|
|
|
MARLIN-IPMP-ACBC |
|
|
|
MARLIN-IPMP-ACGK |
ISMA
|
|
|
|
|
ISMA-IAEC |
| PIFF |
|
|
|
|
PIFF-CBC |
|
|
PIFF-CTR |
CENC
|
|
|
|
|
MPEG-CENC |
- Eines / Tools
|
command
(Bento4)
|
standard
|
encrypt
|
decrypt
|
DCF
|
mp4dcfpackager
--method CTR
--content-id cid:toto
--key
000102030405060708090a0b0c0d0e0f:00000000000000000000000000000000
toto.mp4 toto.dcf |
mp4decrypt
--key 1:00112233445566778899aabbccddeeff
toto.dcf toto.odf |
mp4extract toto.odf
|
PDCF
|
mp4encrypt --method
OMA-PDCF-CTR
--key
1:000102030405060708090a0b0c0d0e0f:0000000000000000
--key
2:000102030405060708090a0b0c0d0e0f:0000000000000000
toto.mp4 toto.pdcf |
mp4decrypt
--key
1:000102030405060708090a0b0c0d0e0f:0000000000000000
--key
2:000102030405060708090a0b0c0d0e0f:0000000000000000
toto.pdcf toto.mp4 |
|
|
Gestió de pagaments / Payment
management
|
|
|
|
|
|
|
Continguts / Contents
|
|
|
Seqüències / Sequencies
|
- CIPR
Sequences
- VideoLan
- Sample
HDTV transport streams (multiple TS) (Roku HD1000)
- Apple Quicktime
Trailers
- 20
open
source movies you can edit and redistribute for free
- Movies
- Orange project / Blender
- Xiph.org Test Media
- Opencaster
demo
roll (reference TS) (Avalpa)
- Internet
Archive
- Podcasting
- HD
test sequences (EBU)
- The SVT High Definition Multi Format Test Set
(SGI format: CrowdRun, ParkJoy, DucksTakeOff, InToTree,
OldTownCross, TestPatterns)
- HDTV
test
sequences 2010
- Video
Library
and Tools
- ftp://ftp.ldv.e-technik.tu-muenchen.de/pub/
- Raw
Sequences
from ftp.ldv.e-technik.tu-muenchen.de (YUV format:
BlueSky, Bus, City, Crew, Football, Foreman, Harbour, Ice,
Mobile, ParkRun, PederestianArea, Riverbed, RushHour,
Shields, Soccer, Station, Stockholm, Sunflower, Tractor)
- MPEG
Systems
Test Bitstreams
- Mplayer
samples
- MPEG-4
Streaming resources (GPAC)
- Via
Unichrome
TV Output Testing (interlaced: wrong fields order)
- Apple
HD gallery
- SVC
YUV sequences
- MVC
- The
Official
HD Video Clip List (High Def Forum)
- Containers
samples (MultimediaWiki)
- TV3
A la carta HD
- AVCHD samples
- Lip-sync
- Public domain
- Open Video
Project
- 3D
-
|
|
|
|
|
Reproductors MP3 Players
|
F: FM
X: 512MB
Z: 1GB
Q: 2GB
A: 4GB
- Sistema de connexió
- MTP (Microsoft)
- USB mass storage device class - UMS (wp)
- Samsung
T9 (Abi Forums)
|
|
http://www.francescpinyol.cat/online_audio_video.html
Darrera modificació: 30 de novembre de 2025 / Last update: 30th
November 2025
Cap a casa / Back home. |