├── NEWS ├── INSTALL ├── TODO ├── usr ├── share │ └── gstreamill │ │ └── admin │ │ ├── locales │ │ ├── dev │ │ │ └── translation.json │ │ └── zh_CN │ │ │ └── translation.json │ │ ├── img │ │ ├── logo.png │ │ └── waiting.gif │ │ ├── js │ │ └── gstreamill.js │ │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ │ ├── footer.html │ │ ├── css │ │ └── gstreamill.css │ │ ├── schemas │ │ ├── live_source_test.json │ │ ├── live_source_v4l2.json │ │ ├── transcode_source_decodebin.json │ │ ├── live_source_network.json │ │ ├── transcode_encoder_pad_mp4.json │ │ ├── live_encoder_phone.json │ │ ├── transcode_encoder_phone_mp4.json │ │ ├── live_encoder_sdtv.json │ │ ├── transcode_encoder_pad_m3u8.json │ │ ├── live_encoder_hdtv.json │ │ ├── transcode_encoder_tv_mp4.json │ │ ├── transcode_encoder_hdtv_mp4.json │ │ ├── transcode_encoder_phone_m3u8.json │ │ ├── transcode_encoder_hdtv_m3u8.json │ │ └── transcode_encoder_tv_m3u8.json │ │ ├── index.html │ │ ├── header.html │ │ ├── system.html │ │ ├── setnetwork.html │ │ ├── mediaman.html │ │ └── gstreamill.html ├── lib │ └── systemd │ │ └── system │ │ └── gstreamill.service.in └── Makefile.am ├── etc ├── gstreamill.d │ └── gstreamill.conf ├── Makefile.am └── init │ └── gstreamill.conf.in ├── AUTHORS ├── docs ├── _static │ ├── jobs.png │ ├── logo.png │ ├── diagram.png │ ├── newlivejob.png │ ├── use_as_origin.png │ ├── newlivejob.v4l2-1.png │ ├── newlivejob.v4l2-2.png │ ├── newlivejob.v4l2-3.png │ ├── newlivejob.v4l2-4.png │ ├── newlivejob.v4l2-5.png │ ├── newlivejob.v4l2-6.png │ ├── newlivejob.v4l2-7.png │ ├── newlivejob.v4l2-8.png │ └── newlivejob.v4l2-9.png ├── develop.rst ├── _themes │ └── sphinx13 │ │ ├── theme.conf │ │ ├── static │ │ ├── relbg.png │ │ ├── bodybg.png │ │ ├── footerbg.png │ │ ├── headerbg.png │ │ └── listitem.png │ │ └── layout.html ├── documents.rst ├── install.rst ├── introduction.rst ├── _templates │ └── index.html ├── Makefile └── usage.rst ├── tools ├── cleangstmill.sh ├── hd2.5Mjobs.txt ├── hd4Mjobs.txt ├── hd8Mjobs.txt ├── startjobs.sh ├── stoplivejobs.py ├── postlivejobs.py ├── sdjobs.txt ├── postjob.py ├── preload.py ├── segment.temp ├── segmentjobs.txt ├── template.job ├── sd.temp └── hd2.5M.temp ├── autogen.sh ├── src ├── mediaman.h ├── utils.h ├── Makefile.am ├── log.h ├── jobdesc.h ├── m3u8playlist.h ├── httpmgmt.h ├── httpstreaming.h ├── gstreamill.h ├── source.h ├── job.h ├── utils.c ├── encoder.h └── tssegment.h ├── gstreamill.spec.in ├── test ├── httpmgmt.py ├── httpload.py ├── httpclient.py └── m3u8client.py ├── Makefile.am ├── examples ├── hlssrc.job ├── aac.only.job ├── cctv.flv.audio.only.job ├── audio.only.job ├── cctv.flv.job ├── rtmpsrc.job ├── tssegment.job ├── 4k.265.job ├── copy.264.trans.aac.job ├── v4l2.job ├── test.job ├── x265.job ├── easycap.job ├── rtspsrc.audio.trans.video.copy.job ├── rtspsrc.job ├── multiaudio.job ├── subtitleoverlay.job ├── transcoder.264.aac.output.ts.job ├── transcoder.cbr.job ├── subtitle.job ├── XI006AEPRO.CARD.job ├── decodebin.job ├── rtmp.job ├── ip.job ├── mpeg2_1080p_ac3.job └── transcoder.264.aac.output.m3u8.job ├── configure.ac ├── Dockerfile └── ChangeLog /NEWS: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | see README 2 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | 2 | - DASH output 3 | - transcode job 4 | - record job 5 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/locales/dev/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | } 3 | -------------------------------------------------------------------------------- /etc/gstreamill.d/gstreamill.conf: -------------------------------------------------------------------------------- 1 | { 2 | "language": "English" 3 | } 4 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | gstreamill is written and maintained by 2 | 3 | Zhang Ping 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/_static/jobs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_static/jobs.png -------------------------------------------------------------------------------- /docs/_static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_static/logo.png -------------------------------------------------------------------------------- /docs/_static/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_static/diagram.png -------------------------------------------------------------------------------- /docs/_static/newlivejob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_static/newlivejob.png -------------------------------------------------------------------------------- /tools/cleangstmill.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf /var/run/gstreamill/gstreamill.pid 4 | rm -rf /dev/shm/* 5 | -------------------------------------------------------------------------------- /docs/_static/use_as_origin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_static/use_as_origin.png -------------------------------------------------------------------------------- /docs/develop.rst: -------------------------------------------------------------------------------- 1 | Gstreamill development 2 | ====================== 3 | 4 | http://github.com/i4tv/gstreamill 5 | -------------------------------------------------------------------------------- /docs/_static/newlivejob.v4l2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_static/newlivejob.v4l2-1.png -------------------------------------------------------------------------------- /docs/_static/newlivejob.v4l2-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_static/newlivejob.v4l2-2.png -------------------------------------------------------------------------------- /docs/_static/newlivejob.v4l2-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_static/newlivejob.v4l2-3.png -------------------------------------------------------------------------------- /docs/_static/newlivejob.v4l2-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_static/newlivejob.v4l2-4.png -------------------------------------------------------------------------------- /docs/_static/newlivejob.v4l2-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_static/newlivejob.v4l2-5.png -------------------------------------------------------------------------------- /docs/_static/newlivejob.v4l2-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_static/newlivejob.v4l2-6.png -------------------------------------------------------------------------------- /docs/_static/newlivejob.v4l2-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_static/newlivejob.v4l2-7.png -------------------------------------------------------------------------------- /docs/_static/newlivejob.v4l2-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_static/newlivejob.v4l2-8.png -------------------------------------------------------------------------------- /docs/_static/newlivejob.v4l2-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_static/newlivejob.v4l2-9.png -------------------------------------------------------------------------------- /docs/_themes/sphinx13/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = sphinx13.css 4 | pygments_style = trac 5 | -------------------------------------------------------------------------------- /docs/_themes/sphinx13/static/relbg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_themes/sphinx13/static/relbg.png -------------------------------------------------------------------------------- /docs/_themes/sphinx13/static/bodybg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_themes/sphinx13/static/bodybg.png -------------------------------------------------------------------------------- /docs/_themes/sphinx13/static/footerbg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_themes/sphinx13/static/footerbg.png -------------------------------------------------------------------------------- /docs/_themes/sphinx13/static/headerbg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_themes/sphinx13/static/headerbg.png -------------------------------------------------------------------------------- /docs/_themes/sphinx13/static/listitem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/docs/_themes/sphinx13/static/listitem.png -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/usr/share/gstreamill/admin/img/logo.png -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/img/waiting.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/usr/share/gstreamill/admin/img/waiting.gif -------------------------------------------------------------------------------- /tools/hd2.5Mjobs.txt: -------------------------------------------------------------------------------- 1 | CCTV1 udp://237.123.43.3:6120 hd2.5M 2 | CCTV5 udp://237.123.43.2:6120 hd2.5M 3 | SZTV udp://237.123.43.6:6120 hd2.5M 4 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf --verbose --force --install --make || { 4 | echo 'autogen.sh failed'; 5 | exit 1; 6 | } 7 | -------------------------------------------------------------------------------- /tools/hd4Mjobs.txt: -------------------------------------------------------------------------------- 1 | BJTV udp://237.123.43.7:6120 hd4M 2 | HNTV udp://237.123.43.4:6120 hd4M 3 | TJTV udp://237.123.43.11:6120 hd4M 4 | HBTV udp://237.123.43.12:6120 hd4M 5 | -------------------------------------------------------------------------------- /tools/hd8Mjobs.txt: -------------------------------------------------------------------------------- 1 | JSTV udp://237.123.43.8:6120 hd8M 2 | ZJTV udp://237.123.43.10:6120 hd8M 3 | HLJTV udp://237.123.43.5:6120 hd8M 4 | GDTV udp://237.123.43.13:6120 hd8M 5 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/js/gstreamill.js: -------------------------------------------------------------------------------- 1 | Document.GstreamillConf = { 2 | "language": { 3 | "中文": "zh_CN", 4 | "English": "en" 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/usr/share/gstreamill/admin/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/usr/share/gstreamill/admin/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i4tv/gstreamill/HEAD/usr/share/gstreamill/admin/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /tools/startjobs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -d /etc/gstreamill.d ]; then 4 | for job in $(ls /etc/gstreamill.d/jobs.d/*.job); do 5 | curl -H "Content-Type: application/json" --data @$job http://localhost:20118/admin/start 2>&1 > /dev/null 6 | done 7 | fi 8 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tools/stoplivejobs.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import fileinput 4 | import requests 5 | 6 | if __name__ == "__main__": 7 | server = sys.argv[1] 8 | jobs = sys.argv[2] 9 | for job in fileinput.input(jobs): 10 | name, source, temp = job.split() 11 | r = requests.get(server + name) 12 | print r.text 13 | -------------------------------------------------------------------------------- /usr/lib/systemd/system/gstreamill.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=The gstreamill encoder server 3 | 4 | [Service] 5 | Type=forking 6 | PIDFile=/var/run/gstreamill/gstreamill.pid 7 | ExecStartPre=$(bindir)/cleangstmill.sh 8 | ExecStart=$(bindir)/gstreamill 9 | ExecStop=/bin/kill -s SIGTERM $MAINPID 10 | ExecStartPost=$(bindir)/startjobs.sh 11 | Restart=on-failure 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /src/mediaman.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * mediaman 4 | * 5 | * Copyright (C) Zhang Ping 6 | */ 7 | 8 | #ifndef __MEDIAMAN_H__ 9 | #define __MEDIAMAN_H__ 10 | 11 | #include 12 | 13 | gboolean media_append (gchar *path, gchar *buf, gssize size); 14 | gssize media_size (gchar *path); 15 | gssize media_md5sum (gchar *path); 16 | gchar * media_transcode_in_list (gchar *path); 17 | gchar * media_transcode_out_list (gchar *path); 18 | gchar * media_transcode_rm (gchar *media); 19 | 20 | #endif /* __MEDIAMAN_H__ */ 21 | -------------------------------------------------------------------------------- /etc/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | gstreamillconfdir = /etc/gstreamill.d 3 | gstreamillconf_DATA = \ 4 | gstreamill.d/gstreamill.conf 5 | 6 | install-exec-hook: 7 | ps -p1 | grep init && { \ 8 | sed -e 's:$$(bindir):$(bindir):' init/gstreamill.conf.in > init/gstreamill.conf; \ 9 | install -c -m 755 init/gstreamill.conf '/etc/init/gstreamill.conf'; \ 10 | } || true 11 | test -s /etc/gstreamill.d/jobs.d || { \ 12 | mkdir -p /etc/gstreamill.d/jobs.d; \ 13 | chown -R root:gstreamill /etc/gstreamill.d; \ 14 | chmod -R g+w /etc/gstreamill.d; \ 15 | } 16 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * utils 4 | * 5 | * Copyright (C) Zhang Ping 6 | */ 7 | 8 | #ifndef __UTILS_H__ 9 | #define __UTILS_H__ 10 | 11 | #include 12 | #include 13 | 14 | gchar * unicode_file_name_2_shm_name (gchar *filename); 15 | gchar * get_address (struct sockaddr in_addr); 16 | gushort get_port (struct sockaddr in_addr); 17 | gchar *timestamp_to_segment_dir (time_t timestamp); 18 | gint segment_dir_to_timestamp (gchar *dir, time_t *timestamp); 19 | gint remove_dir (gchar *dir); 20 | 21 | #endif /* __UTILS_H__ */ 22 | -------------------------------------------------------------------------------- /tools/postlivejobs.py: -------------------------------------------------------------------------------- 1 | 2 | # python postlivejobs.py http://localhost:20118/admin/start segmentjobs.txt 3 | 4 | import sys 5 | import fileinput 6 | import requests 7 | 8 | if __name__ == "__main__": 9 | server = sys.argv[1] 10 | jobs = sys.argv[2] 11 | for job in fileinput.input(jobs): 12 | name, source, temp = job.split() 13 | tempfile = temp + ".temp" 14 | print name, source, tempfile 15 | template = open(tempfile).read() 16 | jsonjob = template % (name, source) 17 | r = requests.post(server, jsonjob) 18 | print r.text 19 | -------------------------------------------------------------------------------- /tools/sdjobs.txt: -------------------------------------------------------------------------------- 1 | CCTV1 udp://238.123.45.1:8000 sd 2 | CCTV2 udp://238.123.45.2:8004 sd 3 | CCTV3 udp://238.123.45.101:9004 sd 4 | CCTV4 udp://238.123.45.55:8216 sd 5 | CCTV5 udp://238.123.45.102:9008 sd 6 | CCTV6 udp://238.123.45.104:9016 sd 7 | CCTV7 udp://238.123.45.7:8024 sd 8 | CCTV8 udp://238.123.45.19:8072 sd 9 | CCTV9 udp://238.123.45.9:8032 sd 10 | CCTV10 udp://238.123.45.58:8228 sd 11 | CCTV11 udp://238.123.45.11:8040 sd 12 | CCTV12 udp://238.123.45.59:8232 sd 13 | CCTV13 udp://238.123.45.13:8048 sd 14 | CCTV14 udp://238.123.45.60:8236 sd 15 | CCTV15 udp://238.123.45.14:8052 sd 16 | CCTV16 udp://238.123.45.15:8056 sd 17 | -------------------------------------------------------------------------------- /docs/documents.rst: -------------------------------------------------------------------------------- 1 | .. Gstreamill Tutorial documentation master file, created by 2 | sphinx-quickstart on Fri May 24 08:45:43 2013. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Gstreamill Tutorial's documentation! 7 | =============================================== 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | introduction 15 | install 16 | usage 17 | jobs 18 | managment 19 | develop 20 | 21 | Indices and tables 22 | ================== 23 | 24 | * :ref:`genindex` 25 | * :ref:`modindex` 26 | * :ref:`search` 27 | 28 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | gstreamill_CFLAGS = $(gstreamer_CFLAGS) $(gstreamerapp_CFLAGS) $(gstreamerpluginsbase_CFLAGS) $(augeas_CFLAGS) -Wall 2 | 3 | bin_PROGRAMS = gstreamill 4 | 5 | gstreamill_LDADD = $(gstreamer_LIBS) $(gstreamerapp_LIBS) $(gstreamerpluginsbase_LIBS) $(augeas_LIBS) $(gio_LIBS) -lrt -lpthread -lgstvideo-1.0 -lgstmpegts-1.0 -lgstcodecparsers-1.0 6 | 7 | gstreamill_SOURCES = utils.c main.c gstreamill.c httpserver.c source.c encoder.c job.c log.c httpstreaming.c httpmgmt.c mediaman.c parson.c jobdesc.c m3u8playlist.c tssegment.c 8 | 9 | include_HEADERS = encoder.h gstreamill.h httpmgmt.h httpserver.h httpstreaming.h jobdesc.h job.h log.h m3u8playlist.h mediaman.h parson.h source.h utils.h tssegment.h 10 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/css/gstreamill.css: -------------------------------------------------------------------------------- 1 | 2 | .body_content { 3 | width: 780px; 4 | margin-top: 5px; 5 | margin-left: auto; 6 | margin-right: auto; 7 | } 8 | 9 | .main-content { 10 | padding-left: 0px; 11 | } 12 | 13 | .pipeline-stat, .network-conf { 14 | margin-left: 5px; 15 | margin-right: 5px; 16 | } 17 | 18 | .well { 19 | padding-bottom: 0px; 20 | } 21 | 22 | .well > .table { 23 | margin-bottom: 0px; 24 | } 25 | 26 | #joblist li { 27 | margin: 0px 0px 0px 0px; 28 | padding: 0px 0px 10px 0px; 29 | border-top: 0px; 30 | border-left: 0px; 31 | border-right: 0px; 32 | } 33 | 34 | #joblist li:last-child { 35 | border: 0; 36 | } 37 | 38 | #transcodeinlist li, #transcodeoutlist li { 39 | border: 0px; 40 | } 41 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/schemas/live_source_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "type": "testsrc", 4 | "source": { 5 | "elements": { 6 | "videotestsrc": { 7 | "caps": "video/x-raw,width=720,height=576,framerate=25/1" 8 | }, 9 | "audiotestsrc": { 10 | "property": { 11 | "wave": 8 12 | } 13 | } 14 | }, 15 | "bins": [ 16 | "videotestsrc ! appsink name=video", 17 | "audiotestsrc ! appsink name=audio" 18 | ] 19 | }, 20 | "encoders": [ 21 | ], 22 | "m3u8streaming": { 23 | "version": 3, 24 | "window-size": 10, 25 | "segment-duration": 3 26 | }, 27 | "dvr_duration": 86400 28 | } 29 | -------------------------------------------------------------------------------- /docs/install.rst: -------------------------------------------------------------------------------- 1 | Gstreamill install 2 | ****************** 3 | 4 | Gstreamill have been tested on Ubuntu 16.04.2 and CentOS 7.0. 5 | 6 | Install gstreamill from source on ubuntu 14.04 7 | ============================================== 8 | 9 | Install the prerequistites:: 10 | 11 | sudo apt-get install autoconf automake libtool libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libaugeas-dev 12 | 13 | get lastest release tarball:: 14 | 15 | wget https://github.com/i4tv/gstreamill/archive/v0.8.5.tar.gz 16 | 17 | unpack tarball:: 18 | 19 | tar xvf v0.8.5.tar.gz 20 | 21 | Build and install gstreamill:: 22 | 23 | cd gstreamill-x.y.z 24 | ./autogen.sh 25 | ./configure 26 | make 27 | make install 28 | 29 | Start gstreamill:: 30 | 31 | sudo service gstreamill start 32 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 |
6 |
7 | 8 | 29 | -------------------------------------------------------------------------------- /gstreamill.spec.in: -------------------------------------------------------------------------------- 1 | Name: gstreamill 2 | Version: @VERSION@ 3 | Release: 0 4 | Summary: a gstreamer based stream mill 5 | 6 | Group: Applications/Multimedia 7 | License: GPL 8 | URL: http://www.i4tv.org/ 9 | Source: gstreamill-@VERSION@.tar.gz 10 | BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) 11 | 12 | BuildRequires: gstreamer 13 | Requires: which pkgconfig 14 | 15 | %description 16 | gstreamill is a gstreamer based stream mill. 17 | 18 | %prep 19 | %setup -q 20 | 21 | 22 | %build 23 | %configure 24 | make %{?_smp_mflags} 25 | 26 | 27 | %install 28 | rm -rf %{buildroot} 29 | make install DESTDIR=%{buildroot} 30 | 31 | 32 | %clean 33 | rm -rf %{buildroot} 34 | 35 | 36 | %files 37 | %defattr(-,root,root,-) 38 | %doc 39 | /usr/bin/gstreamill 40 | 41 | 42 | 43 | %changelog 44 | 45 | -------------------------------------------------------------------------------- /test/httpmgmt.py: -------------------------------------------------------------------------------- 1 | import time 2 | import httplib 3 | 4 | def stop(server, port, job): 5 | conn = httplib.HTTPConnection(server, port) 6 | stop = "/admin/stop/%s" % job 7 | conn.request("GET", stop) 8 | resp = conn.getresponse() 9 | if resp.status == 200: 10 | data = resp.read () 11 | print data 12 | conn.close() 13 | 14 | def start(server, port, jobfile): 15 | conn = httplib.HTTPConnection(server, port) 16 | headers = {"Content-Type": "application/json"} 17 | job = open(jobfile).read() 18 | conn.request("POST", "/admin/start", job, headers) 19 | resp = conn.getresponse() 20 | if resp.status == 200: 21 | data = resp.read () 22 | print "start job response:", data 23 | conn.close() 24 | 25 | while True: 26 | start ("localhost", 20118, "examples/test.job") 27 | time.sleep(10) 28 | stop ("localhost", 20118, "test") 29 | time.sleep(10) 30 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/schemas/live_source_v4l2.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "type": "v4l2src", 4 | "source": { 5 | "elements": { 6 | "alsasrc": { 7 | "property": { 8 | "device": "", 9 | "blocksize": 8192 10 | } 11 | }, 12 | "v4l2src": { 13 | "property": { 14 | "device": "", 15 | "io-mode": 1, 16 | "do-timestamp": true 17 | }, 18 | "caps": "video/x-raw,width=720,height=576" 19 | } 20 | }, 21 | "bins": [ 22 | "alsasrc ! queue ! appsink name=audio", 23 | "v4l2src ! queue ! appsink name=video" 24 | ] 25 | }, 26 | "encoders": [ 27 | ], 28 | "m3u8streaming": { 29 | "version": 3, 30 | "window-size": 10, 31 | "segment-duration": 3 32 | }, 33 | "dvr_duration": 86400 34 | } 35 | -------------------------------------------------------------------------------- /etc/init/gstreamill.conf.in: -------------------------------------------------------------------------------- 1 | description "gstreamill encoder server" 2 | author "Zhang Ping " 3 | 4 | start on (filesystem and net-device-up IFACE=lo) 5 | stop on runlevel [!2345] 6 | 7 | env DAEMON=$(bindir)/gstreamill 8 | env PIDFILE=/var/run/gstreamill/gstreamill.pid 9 | 10 | pre-start script 11 | rm -rf /var/run/gstreamill/gstreamill.pid 12 | mkdir -p /var/run/gstreamill/mq 13 | mount -t mqueue none /var/run/gstreamill/mq 14 | rm -rf /var/run/gstreamill/mq/* 15 | umount /var/run/gstreamill/mq 16 | rm -rf /dev/shm/* 17 | end script 18 | 19 | post-start script 20 | if [ -d /etc/gstreamill.d ]; then 21 | for job in $(ls /etc/gstreamill.d/jobs.d/*.job); do 22 | curl -H "Content-Type: application/json" --data @$job http://localhost:20118/admin/start 23 | done 24 | fi 25 | end script 26 | 27 | post-stop script 28 | killall -q gstreamill || true 29 | end script 30 | 31 | expect fork 32 | respawn 33 | respawn limit 10 5 34 | 35 | exec $DAEMON 36 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/schemas/transcode_source_decodebin.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "", 3 | "debug" : "4", 4 | "is-live" : false, 5 | "log-path" : "", 6 | "source" : { 7 | "elements" : { 8 | "filesrc" : { 9 | "property" : { 10 | "location" : "" 11 | } 12 | }, 13 | "deinterlace" : { 14 | "property" : { 15 | "method" : 4, 16 | "fields" : 0 17 | } 18 | }, 19 | "appsink" : { 20 | "property" : { 21 | "sync" : false, 22 | "drop" : true 23 | } 24 | } 25 | }, 26 | "bins" : [ 27 | "filesrc ! queue ! decodebin name=decodebin", 28 | "decodebin.src ! queue ! deinterlace ! queue ! appsink name = video", 29 | "decodebin.src ! queue ! appsink name = audio" 30 | ] 31 | }, 32 | "encoders" : [ 33 | ] 34 | } 35 | 36 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ## Makefile.am -- Process this file with automake to produce Makefile.in 2 | SUBDIRS = src usr etc 3 | 4 | EXTRA_DIST = examples tools test etc 5 | 6 | install-exec-hook: 7 | id gstreamill || test "$$?" -eq 9 || { \ 8 | useradd gstreamill; \ 9 | } 10 | usermod -a -G video,audio gstreamill; 11 | chown root:gstreamill $(bindir)/gstreamill 12 | chmod u+s $(bindir)/gstreamill 13 | test -s /var/lib/gstreamill || { \ 14 | mkdir -p /var/lib/gstreamill; \ 15 | chown -R root:gstreamill /var/lib/gstreamill; \ 16 | chmod -R g+w /var/lib/gstreamill; \ 17 | } 18 | test -s /var/log/gstreamill || { \ 19 | mkdir -p /var/log/gstreamill; \ 20 | } 21 | chown -R root:gstreamill /var/log/gstreamill; 22 | chmod -R g+w /var/log/gstreamill; 23 | test -s /usr/share/gstreamill && { \ 24 | chown -R root:gstreamill /usr/share/gstreamill; \ 25 | chmod -R g+w /usr/share/gstreamill; \ 26 | } 27 | test -s /usr/lib/systemd/system/gstreamill.service && { \ 28 | systemctl enable gstreamill; \ 29 | install -c -m 755 tools/cleangstmill.sh '$(bindir)/cleangstmill.sh'; \ 30 | install -c -m 755 tools/startjobs.sh '$(bindir)/startjobs.sh'; \ 31 | } || true 32 | -------------------------------------------------------------------------------- /examples/hlssrc.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "hlssrc", 3 | "is-live" : true, 4 | "source" : { 5 | "elements" : { 6 | "souphttpsrc" : { 7 | "property" : { 8 | "location" : "http://1.11.111.5/000010_01/playlist.m3u8?CONTENTID=000010_01" 9 | } 10 | }, 11 | "appsink" : { 12 | "property" : { 13 | "sync" : false, 14 | "drop" : true 15 | } 16 | } 17 | }, 18 | "bins" : [ 19 | "souphttpsrc ! queue ! hlsdemux name=hlssrc", 20 | "hlssrc.src_0 ! queue ! appsink name=mpegts" 21 | ] 22 | }, 23 | "encoders" : [ 24 | { 25 | "elements" : { 26 | "appsrc" : { 27 | "property" : { 28 | "is-live" : true, 29 | "format" : 3 30 | } 31 | } 32 | }, 33 | "bins" : [ 34 | "appsrc name=mpegts ! queue ! appsink sync=FALSE" 35 | ], 36 | "udpstreaming" : "127.0.0.1:12345" 37 | } 38 | ] 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * log. 3 | * 4 | * Copyright (C) Zhang Ping 5 | * 6 | */ 7 | 8 | #ifndef __LOG_H__ 9 | #define __LOG_H__ 10 | 11 | #include 12 | #include 13 | 14 | typedef struct _Log Log; 15 | typedef struct _LogClass LogClass; 16 | 17 | struct _Log { 18 | GObject parent; 19 | 20 | gchar *log_path; 21 | gchar *access_path; 22 | FILE *log_hd; 23 | FILE *access_hd; 24 | GstLogFunction func; 25 | }; 26 | 27 | struct _LogClass { 28 | GObjectClass parent; 29 | }; 30 | 31 | #define TYPE_LOG (log_get_type()) 32 | #define LOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_LOG, Log)) 33 | #define LOG_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), TYPE_LOG, LogClass)) 34 | #define IS_LOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_LOG)) 35 | #define IS_LOG_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE ((cls), TYPE_LOG)) 36 | #define LOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_LOG, LogClass)) 37 | #define log_new(...) (g_object_new(TYPE_LOG, ## __VA_ARGS__, NULL)) 38 | 39 | GType log_get_type (void); 40 | 41 | gint log_set_log_handler (Log *log); 42 | gint log_set_stdout_handler (); 43 | 44 | #endif /* __LOG_H__ */ 45 | -------------------------------------------------------------------------------- /src/jobdesc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * json type of job description parser 3 | * 4 | * Copyright (C) Zhang Ping 5 | * 6 | */ 7 | 8 | #ifndef __JOBDESC_H__ 9 | #define __JOBDESC_H__ 10 | 11 | #include 12 | 13 | gboolean jobdesc_is_valid (gchar *job); 14 | gchar * jobdesc_get_name (gchar *job); 15 | gint jobdesc_encoders_count (gchar *job); 16 | gint jobdesc_streams_count (gchar *job, gchar *pipeline); 17 | gint jobdesc_astreams_count (gchar *job, gint index); 18 | gboolean jobdesc_is_live (gchar *job); 19 | gchar * jobdesc_get_debug (gchar *job); 20 | gchar * jobdesc_get_log_path (gchar *job); 21 | gchar ** jobdesc_bins (gchar *job, gchar *pipeline); 22 | gchar ** jobdesc_element_properties (gchar *job, gchar *element); 23 | gchar * jobdesc_element_property_value (gchar *job, gchar *property); 24 | gchar * jobdesc_element_caps (gchar *job, gchar *element); 25 | gchar * jobdesc_udpstreaming (gchar *job, gchar *pipeline); 26 | gboolean jobdesc_m3u8streaming (gchar *job); 27 | guint jobdesc_m3u8streaming_version (gchar *job); 28 | guint jobdesc_m3u8streaming_window_size (gchar *job); 29 | GstClockTime jobdesc_m3u8streaming_segment_duration (gchar *job); 30 | guint64 jobdesc_dvr_duration (gchar *job); 31 | 32 | #endif /* __JOBDESC_H__ */ 33 | -------------------------------------------------------------------------------- /tools/postjob.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | 向gstreamill提交转码任务,当转码任务多于n个的时候等待,否则提交。 5 | 可用于批量对电视剧或者电影进行无人值守的转码。 6 | """ 7 | 8 | import glob 9 | import httplib 10 | import json 11 | import time 12 | 13 | movies = glob.glob("*.ts") 14 | index = 1 15 | 16 | for movie in movies: 17 | 18 | gstreamill = httplib.HTTPConnection("localhost:20118") 19 | gstreamill.request("GET", "/stat/gstreamill") 20 | response = gstreamill.getresponse() 21 | status = json.loads(response.read()) 22 | 23 | while len(status["job"]) >= 5: # 当前运行的job数达到了4个,等待10s 24 | print "%d job, waitting ..." % len(status["job"]) 25 | time.sleep(10) 26 | gstreamill = httplib.HTTPConnection("localhost:20118") 27 | gstreamill.request("GET", "/stat/gstreamill") 28 | response = gstreamill.getresponse() 29 | status = json.loads(response.read()) 30 | continue 31 | 32 | # 当前运行的job数少于4个,提交一个job 33 | name = movie[0:-3] #去掉 .ts 扩展名 34 | template = open("template.job").read() 35 | job = template % (index, index, name, name) 36 | gstreamill.request("POST", "/start", job) 37 | response = gstreamill.getresponse() 38 | print "post job %d, return %s" % (index, response.reason) 39 | index += 1 40 | -------------------------------------------------------------------------------- /examples/aac.only.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "aac.only", 3 | "source" : { 4 | "elements" : { 5 | "souphttpsrc" : { 6 | "property" : { 7 | "is-live" : true, 8 | "location" : "http://ys.vtings.com/live128_1.aac" 9 | } 10 | } 11 | }, 12 | "bins" : [ 13 | "souphttpsrc ! decodebin name=decodebin", 14 | "decodebin.src ! appsink name=audio" 15 | ] 16 | }, 17 | "encoders" : [ 18 | { 19 | "elements": { 20 | "appsrc" : { 21 | "property" : { 22 | "format" : 3, 23 | "is-live" : true 24 | } 25 | }, 26 | "appsink" : { 27 | "property" : { 28 | "sync" : false 29 | } 30 | } 31 | }, 32 | "bins" : [ 33 | "appsrc name=audio ! audioconvert ! voaacenc ! aacparse ! mpegtsmux ! appsink" 34 | ] 35 | } 36 | ], 37 | "m3u8streaming" : { 38 | "version" : 3, 39 | "window-size" : 10, 40 | "segment-duration" : 3.00 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /examples/cctv.flv.audio.only.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "cctv.audio.only", 3 | "source" : { 4 | "elements" : { 5 | "souphttpsrc" : { 6 | "property" : { 7 | "is-live" : true, 8 | "location" : "http://tv.cdn.site:9000/flv?app=live&stream=drmcctv3_1_1500" 9 | } 10 | } 11 | }, 12 | "bins" : [ 13 | "souphttpsrc ! flvdemux name=flvdemux", 14 | "flvdemux.audio ! appsink name=audio" 15 | ] 16 | }, 17 | "encoders" : [ 18 | { 19 | "elements": { 20 | "appsrc" : { 21 | "property" : { 22 | "format" : 3, 23 | "is-live" : true 24 | } 25 | }, 26 | "appsink" : { 27 | "property" : { 28 | "sync" : false 29 | } 30 | } 31 | }, 32 | "bins" : [ 33 | "appsrc name=audio ! aacparse ! mpegtsmux ! appsink" 34 | ] 35 | } 36 | ], 37 | "m3u8streaming" : { 38 | "version" : 3, 39 | "window-size" : 10, 40 | "segment-duration" : 3.00 41 | }, 42 | "dvr_duration": 86400 43 | } 44 | 45 | -------------------------------------------------------------------------------- /test/httpload.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import cStringIO 3 | 4 | def request_segment(host, port, location): 5 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 6 | s.connect((host, port)) 7 | s.sendall('GET %s HTTP/1.1\r\n\r\n' % location) 8 | response = cStringIO.StringIO() 9 | while True: 10 | response.write(s.recv(500000)) 11 | if response.tell() > 400000: 12 | break; 13 | print "data len %d" % response.tell() 14 | response.seek(0) 15 | head = cStringIO.StringIO() 16 | while True: 17 | l = response.readline() 18 | head.write(l) 19 | if l == "\r\n": 20 | break 21 | head.seek(0) 22 | for l in head: 23 | print l 24 | 25 | len = 0 26 | while True: 27 | chunklenX = response.readline() 28 | if chunklenX == "": 29 | print "error" 30 | print repr(response.read(100)) 31 | exit(0) 32 | chunklen = int(chunklenX, 16) 33 | len += chunklen 34 | chunk = response.read(chunklen+2) 35 | print "length: %d, chunk length hex: %s, decimal: %d, %s...%s" % (len, chunklenX[:-2], chunklen, repr(chunk[:10]), repr(chunk[-10:])) 36 | if len > 200000: 37 | break 38 | s.close() 39 | 40 | while True: 41 | request_segment("localhost", 20119, "/live/test/encoder/0") 42 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/schemas/live_source_network.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "type": "networksrc", 4 | "source": { 5 | "elements": { 6 | "udpsrc": { 7 | "property": { 8 | "uri": "" 9 | } 10 | }, 11 | "tsdemux": { 12 | "property": { 13 | "program-number": 1 14 | } 15 | }, 16 | "deinterlace" : { 17 | "property" : { 18 | "mode" : 1, 19 | "method" : 2, 20 | "fields" : 1 21 | } 22 | }, 23 | "appsink": { 24 | "property": { 25 | "sync": false, 26 | "drop": true 27 | } 28 | } 29 | }, 30 | "bins": [ 31 | "udpsrc ! queue ! tsdemux name=demuxer", 32 | "demuxer.video ! queue ! mpegvideoparse ! queue ! mpeg2dec ! queue ! appsink name = video", 33 | "demuxer.audio ! mpegaudioparse ! queue ! mad ! queue ! appsink name = audio" 34 | ] 35 | }, 36 | "encoders": [ 37 | ], 38 | "m3u8streaming": { 39 | "version": 3, 40 | "window-size": 10, 41 | "segment-duration": 3 42 | }, 43 | "dvr_duration": 86400 44 | } 45 | -------------------------------------------------------------------------------- /examples/audio.only.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "audio.only", 3 | "source" : { 4 | "elements" : { 5 | "audiotestsrc" : { 6 | "property" : { 7 | "wave" : 8 8 | } 9 | } 10 | }, 11 | "bins" : [ 12 | "audiotestsrc ! appsink name=audio" 13 | ] 14 | }, 15 | "encoders" : [ 16 | { 17 | "elements": { 18 | "appsrc" : { 19 | "property" : { 20 | "format" : 3, 21 | "is-live" : true 22 | } 23 | }, 24 | "faac" : { 25 | "property" : { 26 | "bitrate": 64000 27 | } 28 | }, 29 | "appsink" : { 30 | "property" : { 31 | "sync" : false 32 | } 33 | } 34 | }, 35 | "bins" : [ 36 | "appsrc name=audio ! audioconvert ! audioresample ! voaacenc ! aacparse ! muxer.", 37 | "mpegtsmux name=muxer ! queue ! appsink" 38 | ] 39 | } 40 | ], 41 | "m3u8streaming" : { 42 | "version" : 3, 43 | "window-size" : 10, 44 | "segment-duration" : 3.00 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /examples/cctv.flv.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "cctv", 3 | "source" : { 4 | "elements" : { 5 | "souphttpsrc" : { 6 | "property" : { 7 | "is-live" : true, 8 | "location" : "http://tv.cdn.site:9000/flv?app=live&stream=drmcctv3_1_1500" 9 | } 10 | } 11 | }, 12 | "bins" : [ 13 | "souphttpsrc ! flvdemux name=flvdemux", 14 | "flvdemux.video ! appsink name=video", 15 | "flvdemux.audio ! appsink name=audio" 16 | ] 17 | }, 18 | "encoders" : [ 19 | { 20 | "elements": { 21 | "appsrc" : { 22 | "property" : { 23 | "format" : 3, 24 | "is-live" : true 25 | } 26 | }, 27 | "appsink" : { 28 | "property" : { 29 | "sync" : false 30 | } 31 | } 32 | }, 33 | "bins" : [ 34 | "appsrc name=video ! h264parse ! muxer.", 35 | "appsrc name=audio ! aacparse ! muxer.", 36 | "mpegtsmux name=muxer ! queue ! appsink sync=FALSE" 37 | ] 38 | } 39 | ], 40 | "m3u8streaming" : { 41 | "version" : 3, 42 | "window-size" : 10, 43 | "segment-duration" : 3.00 44 | }, 45 | "dvr_duration": 86400 46 | } 47 | 48 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/schemas/transcode_encoder_pad_mp4.json: -------------------------------------------------------------------------------- 1 | { 2 | "elements" : { 3 | "appsrc" : { 4 | "property" : { 5 | "is-live" : true, 6 | "format" : 3 7 | } 8 | }, 9 | "videoscale" : { 10 | "caps" : "video/x-raw,width=720,height=576" 11 | }, 12 | "x264enc" : { 13 | "property" : { 14 | "name" : "x264enc", 15 | "bframes" : 3, 16 | "threads" : 4, 17 | "bitrate" : 800, 18 | "byte-stream" : "TRUE", 19 | "pass" : 0, 20 | "mb-tree" : true, 21 | "dct8x8" : true, 22 | "speed-preset" : 0, 23 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:b-pyramid=2:direct=spatial:nal-hrd=vbr" 24 | } 25 | }, 26 | "voaacenc" : { 27 | "property" : { 28 | "bitrate" : 64000 29 | } 30 | }, 31 | "filesink" : { 32 | "property" : { 33 | "location" : "" 34 | } 35 | } 36 | }, 37 | "bins" : [ 38 | "appsrc name=video ! queue ! videoconvert ! queue ! videoscale ! queue ! x264enc ! queue ! muxer.", 39 | "appsrc name=audio ! queue ! audioconvert ! voaacenc ! queue ! aacparse ! queue ! muxer.", 40 | "mp4mux name=muxer ! queue ! filesink" 41 | ] 42 | } 43 | 44 | -------------------------------------------------------------------------------- /examples/rtmpsrc.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "rtmpsrc", 3 | "is-live" : true, 4 | "source" : { 5 | "elements" : { 6 | "rtmpsrc" : { 7 | "property" : { 8 | "location" : "rtmp://58.200.131.2:1935/livetv/hunantv" 9 | } 10 | }, 11 | "appsink" : { 12 | "property" : { 13 | "sync" : false, 14 | "drop" : true 15 | } 16 | } 17 | }, 18 | "bins" : [ 19 | "rtmpsrc ! queue ! flvdemux name=flvsrc", 20 | "flvsrc.video ! queue ! appsink name=video", 21 | "flvsrc.audio ! queue ! appsink name=audio" 22 | ] 23 | }, 24 | "encoders" : [ 25 | { 26 | "elements" : { 27 | "appsrc" : { 28 | "property" : { 29 | "is-live" : true, 30 | "format" : 3 31 | } 32 | } 33 | }, 34 | "bins" : [ 35 | "appsrc name=video ! queue ! h264parse ! queue ! mpegts.", 36 | "appsrc name=audio ! queue ! mpegts.", 37 | "mpegtsmux name=mpegts ! queue ! appsink sync=FALSE" 38 | ], 39 | "udpstreaming" : "127.0.0.1:12345" 40 | } 41 | ], 42 | "m3u8streaming" : { 43 | "version" : 3, 44 | "window-size" : 4, 45 | "segment-duration" : 5.00 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/schemas/live_encoder_phone.json: -------------------------------------------------------------------------------- 1 | { 2 | "elements": { 3 | "appsrc": { 4 | "property": { 5 | "is-live": true, 6 | "format": 3 7 | } 8 | }, 9 | "videoscale" : { 10 | "caps" : "video/x-raw,format=I420,width=640,height=360" 11 | }, 12 | "x264enc": { 13 | "property": { 14 | "bframes" : 3, 15 | "threads" : 4, 16 | "qp-min" : 1, 17 | "qp-max" : 51, 18 | "qp-step" : 10, 19 | "bitrate" : 300, 20 | "byte-stream" : true, 21 | "pass" : 0, 22 | "mb-tree" : true, 23 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:b-pyramid=2:direct=spatial:nal-hrd=vbr" 24 | } 25 | }, 26 | "voaacenc": { 27 | "property": { 28 | "bitrate": 64000 29 | } 30 | }, 31 | "appsink": { 32 | "property": { 33 | "sync": false 34 | } 35 | } 36 | }, 37 | "bins": [ 38 | "appsrc name=video ! queue ! videoconvert ! queue ! videoscale ! queue ! x264enc ! queue ! muxer.", 39 | "appsrc name=audio ! queue ! audioconvert ! voaacenc ! queue ! aacparse ! queue ! muxer.", 40 | "mpegtsmux name=muxer ! queue ! appsink" 41 | ], 42 | "udpstreaming": "225.0.0.1:10000" 43 | } 44 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/schemas/transcode_encoder_phone_mp4.json: -------------------------------------------------------------------------------- 1 | { 2 | "elements" : { 3 | "appsrc" : { 4 | "property" : { 5 | "is-live" : true, 6 | "format" : 3 7 | } 8 | }, 9 | "videoscale" : { 10 | "caps" : "video/x-raw,width=640,height=360" 11 | }, 12 | "x264enc" : { 13 | "property" : { 14 | "name" : "x264enc", 15 | "bframes" : 3, 16 | "threads" : 4, 17 | "bitrate" : 300, 18 | "byte-stream" : "TRUE", 19 | "rc-lookahead" : 50, 20 | "pass" : 0, 21 | "mb-tree" : true, 22 | "dct8x8" : true, 23 | "speed-preset" : 0, 24 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:b-pyramid=2:direct=spatial:nal-hrd=vbr" 25 | } 26 | }, 27 | "voaacenc" : { 28 | "property" : { 29 | "bitrate" : 64000 30 | } 31 | }, 32 | "filesink" : { 33 | "property" : { 34 | "location" : "" 35 | } 36 | } 37 | }, 38 | "bins" : [ 39 | "appsrc name=video ! queue ! videoconvert ! queue ! videoscale ! queue ! x264enc ! queue ! muxer.", 40 | "appsrc name=audio ! queue ! audioconvert ! voaacenc ! queue ! aacparse ! queue ! muxer.", 41 | "mp4mux name=muxer ! queue ! filesink" 42 | ] 43 | } 44 | 45 | -------------------------------------------------------------------------------- /docs/introduction.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ************ 3 | 4 | Gstreamill is an open source, GPL licensed "stream mill" based on gstreamer-1.0. The "stream mill" can be used as live encoder, transcoder, when used as live encoder, supported output types include udp, http progressive, hls, and recording also supported 5 | 6 | To send start job request to gstreamill let it to provoke live encode or transcode, a job is description of live encoder or transcode in json format. 7 | 8 | User send job to gstreamill, gstreamill generate gstreamer pipelines use the job and set their state to playing. The job been sent to gstreamill use http protocol, the job is json type description. The following diagram illustrate gstreamill logic: 9 | 10 | .. image:: _static/diagram.png 11 | 12 | Jobs are submited to gstreamill use json over http. source and encoders are gstreamer pipelines. 13 | 14 | Use case: 15 | 16 | .. image:: _static/use_as_origin.png 17 | 18 | gstreamer 19 | ========= 20 | 21 | 22 | element 23 | ------- 24 | 25 | 26 | bin 27 | --- 28 | 29 | pads&caps 30 | ---------- 31 | 32 | 33 | Gstreamill 34 | ========== 35 | 36 | Jobs 37 | ---- 38 | 39 | Logs 40 | ---- 41 | 42 | Default log directory is /var/log/gstreamill. Log file is gstreamill.log. In default log file size is 4M, log rotate size is 100. 43 | 44 | Subprocess, also job process, if it is live job, it's default log directory is /var/log/gstreamill/job-name/, job process's log file is gstreamill.log, if it is transcode job, it's log directory is set in job file's "log-path" configure item. 45 | -------------------------------------------------------------------------------- /examples/tssegment.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "cctv88", 3 | "is-live" : true, 4 | "source" : { 5 | "elements" : { 6 | "udpsrc" : { 7 | "property" : { 8 | "uri": "udp://238.123.45.21:8080" 9 | } 10 | }, 11 | "appsink" : { 12 | "property" : { 13 | "sync" : false, 14 | "drop" : true 15 | } 16 | } 17 | }, 18 | "bins" : [ 19 | "udpsrc ! queue ! tssegment ! queue ! appsink name = tssegment" 20 | ] 21 | }, 22 | "encoders" : [ 23 | { 24 | "elements" : { 25 | "appsrc" : { 26 | "property" : { 27 | "is-live" : true, 28 | "format" : 3 29 | } 30 | }, 31 | "tssegment": { 32 | "property": { 33 | "bitrate": 1200 34 | } 35 | }, 36 | "appsink": { 37 | "property": { 38 | "sync": false 39 | } 40 | } 41 | }, 42 | "bins" : [ 43 | "appsrc name=tssegment ! queue ! appsink" 44 | ] 45 | } 46 | ], 47 | "m3u8streaming" : { 48 | "version" : 3, 49 | "window-size" : 4, 50 | "segment-duration" : 10.00 51 | }, 52 | "dvr_duration": 86400 53 | } 54 | 55 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/schemas/live_encoder_sdtv.json: -------------------------------------------------------------------------------- 1 | { 2 | "elements": { 3 | "appsrc": { 4 | "property": { 5 | "is-live": true, 6 | "format": 3 7 | } 8 | }, 9 | "videoscale" : { 10 | "caps" : "video/x-raw,format=I420,width=720,height=576" 11 | }, 12 | "x264enc" : { 13 | "property" : { 14 | "bframes" : 3, 15 | "threads" : 4, 16 | "bitrate" : 1200, 17 | "byte-stream" : "TRUE", 18 | "rc-lookahead" : 25, 19 | "key-int-max" : 100, 20 | "pass" : 5, 21 | "mb-tree" : true, 22 | "dct8x8" : true, 23 | "speed-preset" : 0, 24 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:b-pyramid=2:direct=spatial" 25 | } 26 | }, 27 | "voaacenc": { 28 | "property": { 29 | "bitrate": 64000 30 | } 31 | }, 32 | "appsink": { 33 | "property": { 34 | "sync": false 35 | } 36 | } 37 | }, 38 | "bins": [ 39 | "appsrc name=video ! queue ! videoconvert ! queue ! videoscale ! queue ! x264enc ! queue ! muxer.", 40 | "appsrc name=audio ! queue ! audioconvert ! voaacenc ! queue ! aacparse ! queue ! muxer.", 41 | "mpegtsmux name=muxer ! queue ! appsink" 42 | ], 43 | "udpstreaming": "225.0.0.1:10000" 44 | } 45 | -------------------------------------------------------------------------------- /examples/4k.265.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "265", 3 | "debug" : "gstreamill:3", 4 | "is-live" : true, 5 | "source" : { 6 | "elements" : { 7 | "udpsrc" : { 8 | "property" : { 9 | "uri": "udp://238.123.43.80:2000" 10 | } 11 | }, 12 | "appsink" : { 13 | "property" : { 14 | "sync" : false, 15 | "drop" : true 16 | } 17 | } 18 | }, 19 | "bins" : [ 20 | "udpsrc ! queue ! tssegment ! queue ! appsink name = tssegment" 21 | ] 22 | }, 23 | "encoders" : [ 24 | { 25 | "elements" : { 26 | "appsrc" : { 27 | "property" : { 28 | "is-live" : true, 29 | "format" : 3 30 | } 31 | }, 32 | "tssegment": { 33 | "property": { 34 | "bitrate": 1200 35 | } 36 | }, 37 | "appsink": { 38 | "property": { 39 | "sync": false 40 | } 41 | } 42 | }, 43 | "bins" : [ 44 | "appsrc name=tssegment ! queue ! appsink" 45 | ] 46 | } 47 | ], 48 | "m3u8streaming" : { 49 | "version" : 3, 50 | "window-size" : 4, 51 | "segment-duration" : 10.00 52 | }, 53 | "dvr_duration": 86400 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/m3u8playlist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * m3u8 playlist 3 | * 4 | * Copyright (C) Zhang Ping 5 | * 6 | */ 7 | 8 | #ifndef __M3U8PLAYLIST_H__ 9 | #define __M3U8PLAYLIST_H__ 10 | 11 | #define M3U8_HEADER_TAG "#EXTM3U\n" 12 | #define M3U8_VERSION_TAG "#EXT-X-VERSION:%d\n" 13 | #define M3U8_ALLOW_CACHE_TAG "#EXT-X-ALLOW-CACHE:%s\n" 14 | #define M3U8_TARGETDURATION_TAG "#EXT-X-TARGETDURATION:%d\n" 15 | #define M3U8_MEDIA_SEQUENCE_TAG "#EXT-X-MEDIA-SEQUENCE:%lu\n" 16 | #define M3U8_INF_TAG "#EXTINF:%.2f,\n%s\n" 17 | #define M3U8_STREAM_INF_TAG "#EXT-X-STREAM-INF:PROGRAM-ID=%d,BANDWIDTH=%s000" 18 | #define M3U8_X_ENDLIST_TAG "#EXT-X-ENDLIST\n" 19 | 20 | typedef struct _M3U8Entry 21 | { 22 | GstClockTime duration; 23 | gchar *url; 24 | } M3U8Entry; 25 | 26 | typedef struct _M3U8Playlist 27 | { 28 | GRWLock lock; 29 | guint version; 30 | gint window_size; 31 | guint64 sequence_number; 32 | 33 | GQueue *entries; 34 | gchar *playlist_str; 35 | } M3U8Playlist; 36 | 37 | M3U8Playlist * m3u8playlist_new (guint version, guint window_size, guint64 sequence); 38 | void m3u8playlist_free (M3U8Playlist *playlist); 39 | gint m3u8playlist_add_entry (M3U8Playlist *playlist, const gchar *url, gfloat duration); 40 | gchar * m3u8playlist_live_get_playlist (M3U8Playlist *playlist); 41 | gchar * m3u8playlist_timeshift_get_playlist (gchar *path, guint64 duration, guint version, guint window_size, time_t shift_position); 42 | gchar * m3u8playlist_callback_get_playlist (gchar *path, guint64 duration, guint64 dvr_duration, gchar *start, gchar *end); 43 | 44 | #endif /* __M3U8PLAYLIST_H__ */ 45 | -------------------------------------------------------------------------------- /tools/preload.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | pip install m3u8 first, please 4 | """ 5 | 6 | import sys 7 | import m3u8 8 | import time 9 | import urllib2 10 | 11 | class Player: 12 | def __init__(self, url): 13 | self.url = url 14 | 15 | def play(self): 16 | try: 17 | print "preloading", self.url 18 | playlist = m3u8.load(self.url) 19 | if playlist.is_variant: # multiple bitrates videos 20 | print "multiple bitrates video found" 21 | for subplaylist in playlist.playlists: 22 | print "preloading", playlist.base_uri + subplaylist.uri 23 | p = m3u8.load(playlist.base_uri + subplaylist.uri) 24 | for segment in p.segments: 25 | seg_url = "%s%s" % (p.base_uri, segment.uri) 26 | response = urllib2.urlopen(seg_url) 27 | buf = response.read() 28 | print "download segment:", segment.uri 29 | else: 30 | for segment in p.segments: 31 | seg_url = "%s%s" % (playlist.base_uri, segment.uri) 32 | response = urllib2.urlopen(seg_url) 33 | buf = response.read() 34 | print "download segment: ", segment.uri, ", size ", len(buf) 35 | 36 | except: 37 | print "Error load m3u8 playlist: %s" % self.url 38 | 39 | if __name__ == "__main__": 40 | url = sys.argv[1] 41 | try: 42 | player = Player(url) 43 | except: 44 | print "Open %s error" % url 45 | player.play() 46 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/schemas/transcode_encoder_pad_m3u8.json: -------------------------------------------------------------------------------- 1 | { 2 | "elements" : { 3 | "appsrc" : { 4 | "property" : { 5 | "is-live" : true, 6 | "format" : 3 7 | } 8 | }, 9 | "videoscale" : { 10 | "caps" : "video/x-raw,width=720,height=576" 11 | }, 12 | "x264enc" : { 13 | "property" : { 14 | "name" : "x264enc", 15 | "bframes" : 3, 16 | "threads" : 4, 17 | "bitrate" : 800, 18 | "byte-stream" : "TRUE", 19 | "pass" : 0, 20 | "mb-tree" : true, 21 | "dct8x8" : true, 22 | "speed-preset" : 0, 23 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:b-pyramid=2:direct=spatial:nal-hrd=vbr" 24 | } 25 | }, 26 | "voaacenc" : { 27 | "property" : { 28 | "bitrate" : 64000 29 | } 30 | }, 31 | "hlssink" : { 32 | "property" : { 33 | "playlist-location" : "", 34 | "location" : "", 35 | "max-files" : 9999, 36 | "playlist-length" : 9999, 37 | "target-duration" : 10 38 | } 39 | } 40 | }, 41 | "bins" : [ 42 | "appsrc name=video ! queue ! videoconvert ! queue ! videoscale ! queue ! x264enc ! queue ! muxer.", 43 | "appsrc name=audio ! queue ! audioconvert ! voaacenc ! queue ! aacparse ! queue ! muxer.", 44 | "mpegtsmux name=muxer ! queue ! hlssink" 45 | ] 46 | } 47 | 48 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # Process this file with autoconf to produce a configure script. 2 | 3 | AC_PREREQ([2.63]) 4 | AC_INIT([gstreamill], [1.0.0], [BUG-REPORT-ADDRESS]) 5 | AC_CONFIG_SRCDIR([src/main.c]) 6 | AC_CONFIG_HEADER([config.h]) 7 | AM_INIT_AUTOMAKE([foreign]) 8 | AC_CONFIG_MACRO_DIR([m4]) 9 | 10 | # check whether to build with debugging enabled 11 | AC_MSG_CHECKING([whether to do a debug build]) 12 | AC_ARG_ENABLE(debug, 13 | AC_HELP_STRING([--enable-debug], 14 | [turn on debugging [default=no]]), 15 | [],[enable_debug="no"]) 16 | AC_MSG_RESULT([$enable_debug]) 17 | 18 | # Add '-g' flag to gcc if it's debug build 19 | if test "$enable_debug" = "yes"; then 20 | CXXFLAGS="$CXXFLAGS -g" 21 | CFLAGS="$CFLAGS -g -Wall" 22 | AC_DEFINE([DEBUG],[1],[enable debug message]) 23 | else 24 | AC_DEFINE([DEBUG],[0],[disable debug message]) 25 | fi 26 | 27 | # Checks for programs. 28 | AC_PROG_CC 29 | AC_PROG_CXX 30 | AC_PROG_LIBTOOL 31 | 32 | # Checks for libraries. 33 | # AC_CHECK_LIB([pthread], [pthread_create], HAVE_PTHREAD=yes, HAVE_PTHREAD=no) 34 | 35 | PKG_CHECK_MODULES([gstreamer], [gstreamer-1.0]) 36 | PKG_CHECK_MODULES([gstreamerapp], [gstreamer-app-1.0]) 37 | PKG_CHECK_MODULES([gstreamerpluginsbase], [gstreamer-plugins-base-1.0]) 38 | PKG_CHECK_MODULES([gstreamerpluginsbad], [gstreamer-plugins-bad-1.0]) 39 | PKG_CHECK_MODULES([gio], [gio-2.0]) 40 | 41 | PKG_CHECK_MODULES([augeas], [augeas]) 42 | 43 | # Checks for header files. 44 | 45 | 46 | # Checks for library functions. 47 | 48 | AC_CONFIG_FILES([ 49 | Makefile 50 | src/Makefile 51 | etc/Makefile 52 | usr/Makefile 53 | gstreamill.spec 54 | ]) 55 | 56 | AC_OUTPUT 57 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/schemas/live_encoder_hdtv.json: -------------------------------------------------------------------------------- 1 | { 2 | "elements": { 3 | "appsrc": { 4 | "property": { 5 | "is-live": true, 6 | "format": 3 7 | } 8 | }, 9 | "videoscale" : { 10 | "caps" : "video/x-raw,format=I420,width=1280,height=720" 11 | }, 12 | "x264enc" : { 13 | "property" : { 14 | "bframes" : 3, 15 | "threads" : 8, 16 | "qp-min" : 1, 17 | "qp-max" : 51, 18 | "qp-step" : 10, 19 | "vbv-buf-capacity" : 10000, 20 | "bitrate" : 1500, 21 | "byte-stream" : "TRUE", 22 | "rc-lookahead" : 250, 23 | "pass" : 0, 24 | "mb-tree" : true, 25 | "dct8x8" : true, 26 | "speed-preset" : 0, 27 | "option-string" : ":ref=3:me=hex:subme=8:merange=24:nf=1:deblock=1,-2:weightp=1:b-pyramid=2:direct=spatial:nal-hrd=vbr" 28 | } 29 | }, 30 | "voaacenc": { 31 | "property": { 32 | "bitrate": 64000 33 | } 34 | }, 35 | "appsink": { 36 | "property": { 37 | "sync": false 38 | } 39 | } 40 | }, 41 | "bins": [ 42 | "appsrc name=video ! queue ! videoconvert ! queue ! videoscale ! queue ! x264enc ! queue ! muxer.", 43 | "appsrc name=audio ! queue ! audioconvert ! voaacenc ! queue ! aacparse ! queue ! muxer.", 44 | "mpegtsmux name=muxer ! queue ! appsink" 45 | ], 46 | "udpstreaming": "225.0.0.1:10000" 47 | } 48 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/schemas/transcode_encoder_tv_mp4.json: -------------------------------------------------------------------------------- 1 | { 2 | "elements" : { 3 | "appsrc" : { 4 | "property" : { 5 | "is-live" : true, 6 | "format" : 3 7 | } 8 | }, 9 | "videoscale" : { 10 | "caps" : "video/x-raw,width=720,height=576" 11 | }, 12 | "x264enc" : { 13 | "property" : { 14 | "name" : "x264enc", 15 | "bframes" : 3, 16 | "threads" : 4, 17 | "qp-min" : 1, 18 | "qp-max" : 51, 19 | "qp-step" : 10, 20 | "vbv-buf-capacity" : 10000, 21 | "bitrate" : 1000, 22 | "byte-stream" : "TRUE", 23 | "rc-lookahead" : 1000, 24 | "pass" : 0, 25 | "mb-tree" : true, 26 | "dct8x8" : true, 27 | "speed-preset" : 0, 28 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:b-pyramid=2:direct=spatial:nal-hrd=vbr" 29 | } 30 | }, 31 | "voaacenc" : { 32 | "property" : { 33 | "bitrate" : 64000 34 | } 35 | }, 36 | "filesink" : { 37 | "property" : { 38 | "location" : "" 39 | } 40 | } 41 | }, 42 | "bins" : [ 43 | "appsrc name=video ! queue ! videoconvert ! queue ! videoscale ! queue ! x264enc ! queue ! muxer.", 44 | "appsrc name=audio ! queue ! audioconvert ! voaacenc ! queue ! aacparse ! queue ! muxer.", 45 | "mp4mux name=muxer ! queue ! filesink" 46 | ] 47 | } 48 | 49 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/schemas/transcode_encoder_hdtv_mp4.json: -------------------------------------------------------------------------------- 1 | { 2 | "elements" : { 3 | "appsrc" : { 4 | "property" : { 5 | "is-live" : true, 6 | "format" : 3 7 | } 8 | }, 9 | "videoscale" : { 10 | "caps" : "video/x-raw,width=1280,height=720" 11 | }, 12 | "x264enc" : { 13 | "property" : { 14 | "name" : "x264enc", 15 | "bframes" : 3, 16 | "threads" : 0, 17 | "qp-min" : 1, 18 | "qp-max" : 51, 19 | "qp-step" : 10, 20 | "vbv-buf-capacity" : 10000, 21 | "bitrate" : 1500, 22 | "byte-stream" : "TRUE", 23 | "rc-lookahead" : 200, 24 | "pass" : 0, 25 | "mb-tree" : true, 26 | "dct8x8" : true, 27 | "speed-preset" : 0, 28 | "option-string" : ":ref=3:me=hex:subme=8:merange=24:nf=1:deblock=1,-2:weightp=1:b-pyramid=2:direct=spatial:nal-hrd=vbr" 29 | } 30 | }, 31 | "voaacenc" : { 32 | "property" : { 33 | "bitrate" : 64000 34 | } 35 | }, 36 | "filesink" : { 37 | "property" : { 38 | "location" : "" 39 | } 40 | } 41 | }, 42 | "bins" : [ 43 | "appsrc name=video ! queue ! videoconvert ! queue ! videoscale ! queue ! x264enc ! queue ! muxer.", 44 | "appsrc name=audio ! queue ! audioconvert ! voaacenc ! queue ! aacparse ! queue ! muxer.", 45 | "mp4mux name=muxer ! queue ! filesink" 46 | ] 47 | } 48 | 49 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/schemas/transcode_encoder_phone_m3u8.json: -------------------------------------------------------------------------------- 1 | { 2 | "elements" : { 3 | "appsrc" : { 4 | "property" : { 5 | "is-live" : true, 6 | "format" : 3 7 | } 8 | }, 9 | "videoscale" : { 10 | "caps" : "video/x-raw,width=640,height=360" 11 | }, 12 | "x264enc" : { 13 | "property" : { 14 | "name" : "x264enc", 15 | "bframes" : 3, 16 | "threads" : 4, 17 | "bitrate" : 300, 18 | "byte-stream" : "TRUE", 19 | "rc-lookahead" : 50, 20 | "pass" : 0, 21 | "mb-tree" : true, 22 | "dct8x8" : true, 23 | "speed-preset" : 0, 24 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:b-pyramid=2:direct=spatial:nal-hrd=vbr" 25 | } 26 | }, 27 | "voaacenc" : { 28 | "property" : { 29 | "bitrate" : 64000 30 | } 31 | }, 32 | "hlssink" : { 33 | "property" : { 34 | "playlist-location" : "", 35 | "location" : "", 36 | "max-files" : 9999, 37 | "playlist-length" : 9999, 38 | "target-duration" : 10 39 | } 40 | } 41 | }, 42 | "bins" : [ 43 | "appsrc name=video ! queue ! videoconvert ! queue ! videoscale ! queue ! x264enc ! queue ! muxer.", 44 | "appsrc name=audio ! queue ! audioconvert ! voaacenc ! queue ! aacparse ! queue ! muxer.", 45 | "mpegtsmux name=muxer ! queue ! hlssink" 46 | ] 47 | } 48 | 49 | -------------------------------------------------------------------------------- /examples/copy.264.trans.aac.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "iptv5", 3 | "is-live" : true, 4 | "source" : { 5 | "elements" : { 6 | "udpsrc" : { 7 | "property" : { 8 | "uri" : "udp://238.123.43.101:4120" 9 | } 10 | }, 11 | "appsink" : { 12 | "property" : { 13 | "sync" : false, 14 | "drop" : true 15 | } 16 | } 17 | }, 18 | "bins" : [ 19 | "udpsrc ! queue ! tsdemux name=demuxer", 20 | "demuxer.video ! queue ! h264parse ! queue ! appsink name = video", 21 | "demuxer.audio ! mpegaudioparse ! queue ! mad ! queue ! appsink name = audio" 22 | ] 23 | }, 24 | "encoders" : [ 25 | { 26 | "elements" : { 27 | "appsrc" : { 28 | "property" : { 29 | "is-live" : true, 30 | "format" : 3 31 | } 32 | }, 33 | "appsink": { 34 | "property": { 35 | "sync": false 36 | } 37 | } 38 | }, 39 | "bins" : [ 40 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc0 ! aacparse ! queue ! mpegts.", 41 | "appsrc name=video ! queue ! mpegts.", 42 | "mpegtsmux name=mpegts ! queue ! appsink sync=FALSE" 43 | ] 44 | } 45 | ], 46 | "m3u8streaming" : { 47 | "version" : 3, 48 | "window-size" : 4, 49 | "segment-duration" : 10.00 50 | }, 51 | "dvr_duration": 86400 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/httpmgmt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * managment api over http 3 | * 4 | * Copyright (C) Zhang Ping 5 | * 6 | */ 7 | 8 | #ifndef __HTTPMGMT_H__ 9 | #define __HTTPMGMT_H__ 10 | 11 | #include 12 | 13 | #include "config.h" 14 | #include "gstreamill.h" 15 | #include "httpserver.h" 16 | 17 | #define CONF_FILE "/etc/gstreamill.d/gstreamill.conf" 18 | #define JOBS_DIR "/etc/gstreamill.d/jobs.d/" 19 | #define ADMIN_LOCATION "/usr/share/gstreamill" 20 | 21 | typedef struct _HTTPMgmtPrivateData { 22 | gint fd; 23 | gchar *p; 24 | gchar *buf; 25 | gsize buf_size; 26 | gint64 send_position; 27 | } HTTPMgmtPrivateData; 28 | 29 | typedef struct _HTTPMgmt HTTPMgmt; 30 | typedef struct _HTTPMgmtClass HTTPMgmtClass; 31 | 32 | struct _HTTPMgmt { 33 | GObject parent; 34 | 35 | gchar *address; 36 | GstClock *system_clock; 37 | Gstreamill *gstreamill; 38 | HTTPServer *httpserver; /* management via http */ 39 | }; 40 | 41 | struct _HTTPMgmtClass { 42 | GObjectClass parent; 43 | }; 44 | 45 | #define TYPE_HTTPMGMT (httpmgmt_get_type()) 46 | #define HTTPMGMT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_HTTPMGMT, HTTPMgmt)) 47 | #define HTTPMGMT_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), TYPE_HTTPMGMT, HTTPMgmtClass)) 48 | #define IS_HTTPMGMT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_HTTPMGMT)) 49 | #define IS_HTTPMGMT_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE ((cls), TYPE_HTTPMGMT)) 50 | #define HTTPMGMT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_HTTPMGMT, HTTPMgmtClass)) 51 | #define httpmgmt_new(...) (g_object_new(TYPE_HTTPMGMT, ## __VA_ARGS__, NULL)) 52 | 53 | GType httpmgmt_get_type (void); 54 | gint httpmgmt_start (HTTPMgmt *httpmgmt); 55 | 56 | #endif /* __HTTPMGMT_H__ */ 57 | -------------------------------------------------------------------------------- /examples/v4l2.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "v4l2", 3 | "debug" : "mpegtsmux:2", 4 | "source" : { 5 | "elements" : { 6 | "alsasrc" : { 7 | "property" : { 8 | "blocksize" : 8192 9 | }, 10 | "caps" : "audio/x-raw,width=16,depth=16,rate=48000,channels=2,endianness=1234" 11 | }, 12 | "v4l2src" : { 13 | "property" : { 14 | "device" : "/dev/video0", 15 | "io-mode" : 1, 16 | "do-timestamp" : true 17 | } 18 | } 19 | }, 20 | "bins" : [ 21 | "alsasrc device=hw:0 ! queue ! appsink name=audio", 22 | "v4l2src ! queue ! appsink name=video" 23 | ] 24 | }, 25 | "encoders" : [ 26 | { 27 | "elements" : { 28 | "appsrc" : { 29 | "property" : { 30 | "format" : 3, 31 | "is-live" : true 32 | } 33 | }, 34 | "x264enc" : { 35 | "property" : { 36 | "bitrate" : 1000, 37 | "byte-stream" : true, 38 | "bframes" :3 39 | } 40 | }, 41 | "appsink" : { 42 | "property" : { 43 | "sync" : false 44 | } 45 | } 46 | }, 47 | "bins" : [ 48 | "appsrc name=audio ! audioresample ! audioconvert ! voaacenc ! muxer.", 49 | "appsrc name=video ! videoconvert ! queue ! x264enc ! queue ! muxer.", 50 | "mpegtsmux name=muxer ! queue ! appsink" 51 | ] 52 | } 53 | ] 54 | } 55 | 56 | -------------------------------------------------------------------------------- /examples/test.job: -------------------------------------------------------------------------------- 1 | /* 2 | * test job, videotestsrc and audiotestsrc 3 | */ 4 | { 5 | "name" : "test", 6 | "debug" : "gstreamill:3", 7 | "source" : { 8 | "elements" : { 9 | "videotestsrc" : { 10 | "caps" : "video/x-raw,width=720,height=576,framerate=25/1" 11 | }, 12 | "audiotestsrc" : { 13 | "property" : { 14 | "wave" : 8 15 | } 16 | } 17 | }, 18 | "bins" : [ 19 | "videotestsrc ! appsink name=video", 20 | "audiotestsrc ! appsink name=audio" 21 | ] 22 | }, 23 | "encoders" : [ 24 | { 25 | "elements": { 26 | "appsrc" : { 27 | "property" : { 28 | "format" : 3, 29 | "is-live" : true 30 | } 31 | }, 32 | "x264enc" : { 33 | "property" : { 34 | "bitrate" : 1000, 35 | "byte-stream" : true, 36 | "bframes" : 3 37 | } 38 | }, 39 | "appsink" : { 40 | "property" : { 41 | "sync" : false 42 | } 43 | } 44 | }, 45 | "bins" : [ 46 | "appsrc name=video ! queue ! clockoverlay ! queue ! x264enc ! queue ! muxer.", 47 | "appsrc name=audio ! audioconvert ! audioresample ! voaacenc ! aacparse ! muxer.", 48 | "mpegtsmux name=muxer ! queue ! appsink" 49 | ] 50 | } 51 | ], 52 | "m3u8streaming" : { 53 | "version" : 3, 54 | "window-size" : 10, 55 | "segment-duration" : 3.00 56 | }, 57 | "dvr_duration": 86400 58 | } 59 | 60 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/schemas/transcode_encoder_hdtv_m3u8.json: -------------------------------------------------------------------------------- 1 | { 2 | "elements" : { 3 | "appsrc" : { 4 | "property" : { 5 | "is-live" : true, 6 | "format" : 3 7 | } 8 | }, 9 | "videoscale" : { 10 | "caps" : "video/x-raw,width=1280,height=720" 11 | }, 12 | "x264enc" : { 13 | "property" : { 14 | "name" : "x264enc", 15 | "bframes" : 3, 16 | "threads" : 0, 17 | "qp-min" : 1, 18 | "qp-max" : 51, 19 | "qp-step" : 10, 20 | "vbv-buf-capacity" : 10000, 21 | "bitrate" : 1500, 22 | "byte-stream" : "TRUE", 23 | "rc-lookahead" : 200, 24 | "pass" : 0, 25 | "mb-tree" : true, 26 | "dct8x8" : true, 27 | "speed-preset" : 0, 28 | "option-string" : ":ref=3:me=hex:subme=8:merange=24:nf=1:deblock=1,-2:weightp=1:b-pyramid=2:direct=spatial:nal-hrd=vbr" 29 | } 30 | }, 31 | "voaacenc" : { 32 | "property" : { 33 | "bitrate" : 64000 34 | } 35 | }, 36 | "hlssink" : { 37 | "property" : { 38 | "playlist-location" : "", 39 | "location" : "", 40 | "max-files" : 9999, 41 | "playlist-length" : 9999, 42 | "target-duration" : 10 43 | } 44 | } 45 | }, 46 | "bins" : [ 47 | "appsrc name=video ! queue ! videoconvert ! queue ! videoscale ! queue ! x264enc ! queue ! muxer.", 48 | "appsrc name=audio ! queue ! audioconvert ! voaacenc ! queue ! aacparse ! queue ! muxer.", 49 | "mpegtsmux name=muxer ! queue ! hlssink" 50 | ] 51 | } 52 | 53 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/schemas/transcode_encoder_tv_m3u8.json: -------------------------------------------------------------------------------- 1 | { 2 | "elements" : { 3 | "appsrc" : { 4 | "property" : { 5 | "is-live" : true, 6 | "format" : 3 7 | } 8 | }, 9 | "videoscale" : { 10 | "caps" : "video/x-raw,width=720,height=576" 11 | }, 12 | "x264enc" : { 13 | "property" : { 14 | "name" : "x264enc", 15 | "bframes" : 3, 16 | "threads" : 4, 17 | "qp-min" : 1, 18 | "qp-max" : 51, 19 | "qp-step" : 10, 20 | "vbv-buf-capacity" : 10000, 21 | "bitrate" : 1000, 22 | "byte-stream" : "TRUE", 23 | "rc-lookahead" : 1000, 24 | "pass" : 0, 25 | "mb-tree" : true, 26 | "dct8x8" : true, 27 | "speed-preset" : 0, 28 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:b-pyramid=2:direct=spatial:nal-hrd=vbr" 29 | } 30 | }, 31 | "voaacenc" : { 32 | "property" : { 33 | "bitrate" : 64000 34 | } 35 | }, 36 | "hlssink" : { 37 | "property" : { 38 | "playlist-location" : "", 39 | "location" : "", 40 | "max-files" : 9999, 41 | "playlist-length" : 9999, 42 | "target-duration" : 10 43 | } 44 | } 45 | }, 46 | "bins" : [ 47 | "appsrc name=video ! queue ! videoconvert ! queue ! videoscale ! queue ! x264enc ! queue ! muxer.", 48 | "appsrc name=audio ! queue ! audioconvert ! voaacenc ! queue ! aacparse ! queue ! muxer.", 49 | "mpegtsmux name=muxer ! queue ! hlssink" 50 | ] 51 | } 52 | 53 | -------------------------------------------------------------------------------- /examples/x265.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "x265", 3 | "debug" : "gstreamill:3", 4 | "source" : { 5 | "elements" : { 6 | "videotestsrc" : { 7 | "caps" : "video/x-raw,width=720,height=576,framerate=25/1" 8 | }, 9 | "audiotestsrc" : { 10 | "property" : { 11 | "wave" : 8 12 | } 13 | } 14 | }, 15 | "bins" : [ 16 | "videotestsrc ! appsink name=video", 17 | "audiotestsrc ! appsink name=audio" 18 | ] 19 | }, 20 | "encoders" : [ 21 | { 22 | "elements": { 23 | "appsrc" : { 24 | "property" : { 25 | "format" : 3, 26 | "is-live" : true 27 | } 28 | }, 29 | "x265enc" : { 30 | "property" : { 31 | "bitrate" : 1000 32 | } 33 | }, 34 | "faac" : { 35 | "property" : { 36 | "name" : "faac", 37 | "outputformat" : 1 38 | } 39 | }, 40 | "appsink" : { 41 | "property" : { 42 | "sync" : false 43 | } 44 | } 45 | }, 46 | "bins" : [ 47 | "appsrc name=video ! queue ! timeoverlay ! queue ! x265enc ! queue ! muxer.", 48 | "appsrc name=audio ! audioconvert ! audioresample ! voaacenc ! muxer.", 49 | "mpegtsmux name=muxer ! queue ! appsink" 50 | ] 51 | } 52 | ], 53 | "m3u8streaming" : { 54 | "version" : 3, 55 | "window-size" : 10, 56 | "segment-duration" : 3.00 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /tools/segment.temp: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "%s", 3 | "debug" : "3", 4 | "is-live" : true, 5 | "source" : { 6 | "elements" : { 7 | "udpsrc" : { 8 | "property" : { 9 | "uri" : "%s" 10 | } 11 | }, 12 | "appsink" : { 13 | "property" : { 14 | "sync" : false, 15 | "drop" : true 16 | } 17 | } 18 | }, 19 | "bins" : [ 20 | "udpsrc ! queue ! tsdemux name=demuxer", 21 | "demuxer.video ! queue ! appsink name = video", 22 | "demuxer.audio ! mpegaudioparse ! queue ! mad ! queue ! appsink name = audio" 23 | ] 24 | }, 25 | "encoders" : [ 26 | { 27 | "elements" : { 28 | "appsrc" : { 29 | "property" : { 30 | "is-live" : true, 31 | "format" : 3 32 | } 33 | }, 34 | "appsink": { 35 | "property": { 36 | "sync": false 37 | } 38 | }, 39 | "tssegment": { 40 | "property": { 41 | "bitrate": 1200 42 | } 43 | } 44 | }, 45 | "bins" : [ 46 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc0 ! aacparse ! queue ! mpegts.", 47 | "appsrc name=video ! queue ! h264parse ! queue ! mpegts.", 48 | "mpegtsmux name=mpegts ! queue ! tssegment ! queue ! appsink sync=FALSE" 49 | ] 50 | } 51 | ], 52 | "m3u8streaming" : { 53 | "version" : 3, 54 | "window-size" : 4, 55 | "segment-duration" : 10.00 56 | }, 57 | "dvr_duration": 86400 58 | } 59 | 60 | -------------------------------------------------------------------------------- /test/httpclient.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import httplib 3 | import time 4 | import string 5 | 6 | ReadSize = 190000 7 | 8 | def patch_http_response_read(func): 9 | def inner(*args): 10 | try: 11 | return func(*args) 12 | except httplib.IncompleteRead, e: 13 | return e.partial 14 | 15 | return inner 16 | 17 | httplib.HTTPResponse.read = patch_http_response_read(httplib.HTTPResponse.read) 18 | 19 | def fetch_data(server, port, location, size): 20 | readsize = 0 21 | attempts = 5 22 | while True: 23 | try: 24 | t1 = time.time() 25 | conn = httplib.HTTPConnection(server, port) 26 | #conn.set_debuglevel(1) 27 | conn.request("GET", location) 28 | resp = conn.getresponse() 29 | if resp.status == 200: 30 | data = resp.read (ReadSize) 31 | t2 = time.time() 32 | conn.close() 33 | if not (len(data) == ReadSize): 34 | print time.strftime("%b %d - %H:%M:%S", time.gmtime()), "http://%s%s" % (server, location), resp.status, resp.reason, "size", len(data), "time", t2-t1 35 | exit (0) 36 | else: 37 | print "Got",len(data) 38 | break 39 | except socket.error, msg: 40 | print "socket error %s" % msg 41 | break 42 | except httplib.HTTPException, msg: 43 | print time.strftime("%b %d - %H:%M:%S", time.gmtime()), "read data http://%s:%d%s, http error %s" % (server, port, location, msg) 44 | exit(0) 45 | attempts -= 1 46 | if attempts == 0: 47 | raise 48 | else: 49 | print "try %d times" % (5-attempts) 50 | time.sleep(1) 51 | continue 52 | 53 | while True: 54 | fetch_data("localhost", 20119, "/live/test/encoder/0", ReadSize) 55 | -------------------------------------------------------------------------------- /examples/easycap.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "v4l2", 3 | "debug" : "mpegtsmux:2", 4 | "source" : { 5 | "elements" : { 6 | "alsasrc" : { 7 | "property" : { 8 | "blocksize" : 8192 9 | }, 10 | "caps" : "audio/x-raw,width=16,depth=16,rate=48000,channels=2,endianness=1234" 11 | }, 12 | "v4l2src" : { 13 | "property" : { 14 | "device" : "/dev/video0", 15 | "io-mode" : 1, 16 | "do-timestamp" : true, 17 | "norm" : "PAL" 18 | } 19 | } 20 | }, 21 | "bins" : [ 22 | "alsasrc device=hw:0 ! queue ! appsink name=audio", 23 | "v4l2src ! queue ! appsink name=video" 24 | ] 25 | }, 26 | "encoders" : [ 27 | { 28 | "elements" : { 29 | "appsrc" : { 30 | "property" : { 31 | "format" : 3, 32 | "is-live" : true 33 | } 34 | }, 35 | "x264enc" : { 36 | "property" : { 37 | "bitrate" : 100, 38 | "byte-stream" : true, 39 | "bframes" :3 40 | } 41 | }, 42 | "appsink" : { 43 | "property" : { 44 | "sync" : false 45 | } 46 | } 47 | }, 48 | "bins" : [ 49 | "appsrc name=audio ! audioresample ! audioconvert ! voaacenc ! muxer.", 50 | "appsrc name=video ! videoconvert ! queue ! x264enc ! queue ! muxer.", 51 | "mpegtsmux name=muxer ! queue ! appsink" 52 | ] 53 | } 54 | ], 55 | "m3u8streaming" : { 56 | "version" : 3, 57 | "window-size" : 10, 58 | "segment-duration" : 3.00 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /examples/rtspsrc.audio.trans.video.copy.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "audioonly", 3 | "debug" : "3", 4 | "is-live" : true, 5 | "source" : { 6 | "elements" : { 7 | "rtspsrc" : { 8 | "property" : { 9 | // rtsp src on DS-2DE5174A 10 | "location" : "rtsp://admin:12345@172.10.2.140/h264/ch33/main/av_stream" 11 | } 12 | }, 13 | "appsink" : { 14 | "property" : { 15 | "sync" : false, 16 | "drop" : true 17 | } 18 | } 19 | }, 20 | "bins" : [ 21 | "rtspsrc name=src", 22 | "src.recv_rtp_src_0 ! queue ! rtph264depay ! queue ! appsink name=video", 23 | "src.recv_rtp_src_1 ! queue ! rtppcmudepay ! queue ! mulawdec ! queue ! appsink name=audio" 24 | ] 25 | }, 26 | "encoders" : [ 27 | { 28 | "elements" : { 29 | "appsrc" : { 30 | "property" : { 31 | "is-live" : true, 32 | "format" : 3 33 | } 34 | }, 35 | "tssegment": { 36 | "property": { 37 | "bitrate": 1200 38 | } 39 | }, 40 | "appsink": { 41 | "property": { 42 | "sync": false 43 | } 44 | } 45 | }, 46 | "bins" : [ 47 | "appsrc name=video ! queue ! h264parse ! queue ! muxer.", 48 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc ! queue ! muxer.", 49 | "mpegtsmux name=muxer ! queue ! appsink sync=FALSE" 50 | ] 51 | } 52 | ], 53 | "m3u8streaming" : { 54 | "version" : 3, 55 | "window-size" : 4, 56 | "segment-duration" : 10.00 57 | }, 58 | "dvr_duration": 86400 59 | } 60 | 61 | -------------------------------------------------------------------------------- /src/httpstreaming.h: -------------------------------------------------------------------------------- 1 | /* 2 | * streaming over http. 3 | * 4 | * Copyright (C) Zhang Ping 5 | * 6 | */ 7 | 8 | #ifndef __HTTPSTREAMING_H__ 9 | #define __HTTPSTREAMING_H__ 10 | 11 | #include 12 | 13 | #include "config.h" 14 | #include "httpserver.h" 15 | #include "gstreamill.h" 16 | 17 | typedef struct _HTTPStreamingPrivateData { 18 | Job *job; 19 | gint64 livejob_age; 20 | gint64 rap_addr; 21 | gint64 send_position; 22 | gint chunk_size; 23 | gchar *chunk_size_str; 24 | gint chunk_size_str_len; 25 | gint send_count; 26 | gpointer encoder_output; 27 | gchar *buf; 28 | gsize buf_size; 29 | GSList *segment_list; 30 | gint64 dvr_download_size; 31 | guint list_index; 32 | gchar *segment; 33 | gsize segment_size; 34 | gint64 segment_position; 35 | } HTTPStreamingPrivateData; 36 | 37 | typedef struct _HTTPStreaming HTTPStreaming; 38 | typedef struct _HTTPStreamingClass HTTPStreamingClass; 39 | 40 | struct _HTTPStreaming { 41 | GObject parent; 42 | 43 | gchar *address; 44 | Gstreamill *gstreamill; 45 | HTTPServer *httpserver; /* streaming via http */ 46 | }; 47 | 48 | struct _HTTPStreamingClass { 49 | GObjectClass parent; 50 | }; 51 | 52 | #define TYPE_HTTPSTREAMING (httpstreaming_get_type()) 53 | #define HTTPSTREAMING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_HTTPSTREAMING, HTTPStreaming)) 54 | #define HTTPSTREAMING_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), TYPE_HTTPSTREAMING, HTTPStreamingClass)) 55 | #define IS_HTTPSTREAMING(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_HTTPSTREAMING)) 56 | #define IS_HTTPSTREAMING_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE ((cls), TYPE_HTTPSTREAMING)) 57 | #define HTTPSTREAMING_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_HTTPSTREAMING, HTTPStreamingClass)) 58 | #define httpstreaming_new(...) (g_object_new(TYPE_HTTPSTREAMING, ## __VA_ARGS__, NULL)) 59 | 60 | GType httpstreaming_get_type (void); 61 | gint httpstreaming_start (HTTPStreaming *httpstreaming, gint maxthreads); 62 | 63 | #endif /* __HTTPSTREAMING_H__ */ 64 | -------------------------------------------------------------------------------- /tools/segmentjobs.txt: -------------------------------------------------------------------------------- 1 | CCTV1 udp://238.123.45.1:8000 segment 2 | CCTV2 udp://238.123.45.2:8004 segment 3 | CCTV3 udp://238.123.45.101:9004 segment 4 | CCTV4 udp://238.123.45.55:8216 segment 5 | CCTV5 udp://238.123.45.102:9008 segment 6 | CCTV6 udp://238.123.45.104:9016 segment 7 | CCTV7 udp://238.123.45.7:8024 segment 8 | CCTV8 udp://238.123.45.19:8072 segment 9 | CCTV9 udp://238.123.45.9:8032 segment 10 | CCTV10 udp://238.123.45.58:8228 segment 11 | CCTV11 udp://238.123.45.11:8040 segment 12 | CCTV12 udp://238.123.45.59:8232 segment 13 | CCTV13 udp://238.123.45.13:8048 segment 14 | CCTV14 udp://238.123.45.60:8236 segment 15 | CCTV15 udp://238.123.45.14:8052 segment 16 | CCTV16 udp://238.123.45.15:8056 segment 17 | DFWS udp://238.123.45.49:8192 segment 18 | JSWS udp://238.123.45.70:8276 segment 19 | BJWS udp://238.123.45.61:8240 segment 20 | ZJWS udp://238.123.45.72:8284 segment 21 | LNWS udp://238.123.45.30:8116 segment 22 | AHWS udp://238.123.45.69:8272 segment 23 | SDWS udp://238.123.45.38:8148 segment 24 | HNWS udp://238.123.45.68:8268 segment 25 | GDWS udp://238.123.45.34:8132 segment 26 | GXWS udp://238.123.45.76:8300 segment 27 | LYWS udp://238.123.45.77:8304 segment 28 | CQWS udp://238.123.45.75:8296 segment 29 | SCWS udp://238.123.45.73:8288 segment 30 | HENANWS udp://238.123.45.33:8128 segment 31 | DNWS udp://238.123.45.29:8112 segment 32 | TJWS udp://238.123.45.37:8144 segment 33 | JXWS udp://238.123.45.71:8280 segment 34 | HEBWS udp://238.123.45.35:8136 segment 35 | JLWS udp://238.123.45.51:8200 segment 36 | HLJWS udp://238.123.45.50:8196 segment 37 | SAXWS udp://238.123.45.44:8172 segment 38 | GZWS udp://238.123.45.31:8120 segment 39 | QHWS udp://238.123.45.47:8184 segment 40 | YNWS udp://238.123.45.28:8108 segment 41 | NXWS udp://238.123.45.32:8124 segment 42 | SXWS udp://238.123.45.36:8140 segment 43 | XJWS udp://238.123.45.41:8160 segment 44 | XZWS udp://238.123.45.42:8164 segment 45 | NMGWS udp://238.123.45.45:8176 segment 46 | GSWS udp://238.123.45.48:8188 segment 47 | HBWS udp://238.123.45.74:8292 segment 48 | SZWS udp://238.123.45.161:9244 segment 49 | XMWS udp://238.123.47.50:4120 segment 50 | BTWS udp://238.123.47.52:4120 segment 51 | -------------------------------------------------------------------------------- /examples/rtspsrc.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "rtspsrc", 3 | "is-live" : true, 4 | "source" : { 5 | "elements" : { 6 | "uridecodebin" : { 7 | "property" : { 8 | "uri" : "rtsp://58.248.254.11:80/tvlive/cctv13-h.stream" 9 | } 10 | }, 11 | "appsink" : { 12 | "property" : { 13 | "sync" : false, 14 | "drop" : true 15 | } 16 | } 17 | }, 18 | "bins" : [ 19 | "uridecodebin name=rtspsrc", 20 | "rtspsrc.src_0 ! queue ! appsink name = video", 21 | "rtspsrc.src_1 ! queue ! appsink name = audio" 22 | ] 23 | }, 24 | "encoders" : [ 25 | { 26 | "elements" : { 27 | "appsrc" : { 28 | "property" : { 29 | "is-live" : true, 30 | "format" : 3 31 | } 32 | }, 33 | "x264enc" : { 34 | "property" : { 35 | "name" : "x264enc", 36 | "bitrate" : 1000, 37 | "bframes" : 3, 38 | "byte-stream" : "TRUE", 39 | "rc-lookahead" : 25, 40 | "key-int-max" : 100, 41 | "pass" : 0, 42 | "mb-tree" : false, 43 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:b-pyramid=2:direct=spatial" 44 | } 45 | } 46 | }, 47 | "bins" : [ 48 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc0! queue ! muxer.", 49 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 50 | "mpegtsmux name=muxer ! queue ! appsink sync=FALSE" 51 | ] 52 | } 53 | ], 54 | "m3u8streaming" : { 55 | "version" : 3, 56 | "window-size" : 4, 57 | "segment-duration" : 10.00 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /docs/_templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% set title = _('Overview') %} 3 | {% block body %} 4 |

{{ _('Welcome') }}

5 | 6 |

{%trans%} 7 | Gstreamill is a live encoder and transcoder based on gstreamer, written by Zhang Ping and licensed under the GPL license. 8 | The following features should be highlighted: 9 | {%endtrans%}

10 |
    11 |
  • {%trans%}Based on gstreamer and easy to extend:GStreamer processes media by connecting a number of elements into a pipeline, it can be extended by writing new element.{%endtrans%}
  • 12 |
  • {%trans%}HLS, HTTP progressive streaming, UDP output:{%endtrans%}
  • 13 |
  • {%trans%}Multi-Rate with GOP alignment:{%endtrans%}
  • 14 |
  • {%trans%}RESTful management interface: allowing easy integration into operator environment{%endtrans%}
  • 15 |
  • {%trans%}Live encoder or transcoder job run in subprocess, and auto restart on error: {%endtrans%}
  • 16 |
  • {%trans%}Native embedded B/S Management and RESTful management interface: {%endtrans%}
  • 17 |
  • {%trans%}DVR support for Live encoder: DVR can be used for Time Shift TV and Catchup TV{%endtrans%}
  • 18 |
19 | 20 |
21 | 32 | 33 | blog comments powered by Disqus 34 | 35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /examples/multiaudio.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "multiaudio", 3 | "debug" : "3", 4 | "source" : { 5 | "elements" : { 6 | "udpsrc" : { 7 | "property" : { 8 | "uri" : "udp://127.0.0.1:12345" 9 | } 10 | }, 11 | "appsink" : { 12 | "property" : { 13 | "sync" : false, 14 | "drop" : true 15 | } 16 | } 17 | }, 18 | "bins" : [ 19 | "udpsrc ! queue ! tsdemux name=demuxer", 20 | "demuxer.video ! queue ! mpeg2dec ! queue ! appsink name = video", 21 | "demuxer.audio_0_0352 ! queue ! mpegaudioparse ! queue ! avdec_mp2float ! queue ! appsink name = audio_0_0352", 22 | "demuxer.audio_0_035b ! queue ! mpegaudioparse ! queue ! avdec_mp2float ! queue ! appsink name = audio_0_035b" 23 | ] 24 | }, 25 | "encoders" : [ 26 | { 27 | "elements" : { 28 | "appsrc" : { 29 | "property" : { 30 | "format" : 3, 31 | "is-live" : true 32 | } 33 | }, 34 | "x264enc" : { 35 | "property" : { 36 | "name" : "x264enc", 37 | "bitrate" : 1500, 38 | "bframes" : 3 39 | } 40 | }, 41 | "appsink" : { 42 | "property" : { 43 | "sync" : false 44 | } 45 | } 46 | }, 47 | "bins" : [ 48 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 49 | "appsrc name=audio_0_0352 ! queue ! audioconvert ! queue ! voaacenc ! queue ! aacparse ! queue ! muxer.", 50 | "appsrc name=audio_0_035b ! queue ! audioconvert ! queue ! voaacenc ! queue ! aacparse ! queue ! muxer.", 51 | "mpegtsmux name=muxer ! queue ! appsink" 52 | ] 53 | } 54 | ], 55 | "m3u8streaming" : { 56 | "version" : 3, 57 | "window-size" : 10, 58 | "segment-duration" : 3.00 59 | }, 60 | "dvr_duration": 86400 61 | } 62 | 63 | -------------------------------------------------------------------------------- /examples/subtitleoverlay.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "cctv2", 3 | "debug" : "3", 4 | "source" : { 5 | "elements" : { 6 | "udpsrc" : { 7 | "property" : { 8 | "uri" : "udp://127.0.0.1:6003" 9 | } 10 | }, 11 | "appsink" : { 12 | "property" : { 13 | "sync" : false, 14 | "drop" : true 15 | } 16 | } 17 | }, 18 | "bins" : [ 19 | "udpsrc ! queue ! mpegtsdemux name=demuxer", 20 | "demuxer.video ! queue ! ffdec_h264 ! queue ! appsink name = video", 21 | "demuxer.audio ! queue ! mad ! queue ! appsink name = audio", 22 | "demuxer.private ! appsink name = subtitle" 23 | ] 24 | }, 25 | "encoders" : [ 26 | { 27 | "elements" : { 28 | "videoscale" : { 29 | "caps" : "video/x-raw-yuv,width=720,height=576" 30 | }, 31 | "x264enc" : { 32 | "property" : { 33 | "name" : "x264enc", 34 | "bitrate" : 1500, 35 | "bframes" : 3, 36 | "profile" : "high", 37 | "byte-stream" : "TRUE", 38 | "rc-lookahead" : 25, 39 | "pass" : 0, 40 | "mb-tree" : false, 41 | "stats-file" : "/tmp/x264.log", 42 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:lookahead-threads=4:sliced-threads=false:b-pyramid=2:direct=spatial" 43 | } 44 | } 45 | }, 46 | "bins" : [ 47 | "appsrc name=subtitle ! overlay.", 48 | "appsrc name=video ! overlay.", 49 | "dvbsuboverlay name=overlay ! queue ! videoscale ! queue ! ffmpegcolorspace ! queue ! x264enc ! queue ! muxer.", 50 | "appsrc name=audio ! audioconvert ! faac name=faac1 outputformat=1 ! queue ! muxer.", 51 | "mpegtsmux name=muxer ! queue ! appsink sync=FALSE" 52 | ] 53 | } 54 | ] 55 | } 56 | 57 | -------------------------------------------------------------------------------- /examples/transcoder.264.aac.output.ts.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "transcode", 3 | "debug" : "4", 4 | "is-live" : false, 5 | "log-path" : "/home/zhangping/tmp/cctv2", 6 | "source" : { 7 | "elements" : { 8 | "filesrc" : { 9 | "property" : { 10 | "location" : "/home/encoder/cctv/CCTV-HD-720P-1M.ts" 11 | } 12 | }, 13 | "appsink" : { 14 | "property" : { 15 | "sync" : false, 16 | "drop" : true 17 | } 18 | } 19 | }, 20 | "bins" : [ 21 | "filesrc ! queue ! tsdemux name=demuxer", 22 | "demuxer.video ! queue ! h264parse ! queue ! avdec_h264 ! queue ! appsink name = video", 23 | "demuxer.audio ! queue ! aacparse ! queue ! avdec_aac ! queue ! appsink name = audio" 24 | ] 25 | }, 26 | "encoders" : [ 27 | { 28 | "elements" : { 29 | "appsrc" : { 30 | "property" : { 31 | "is-live" : true, 32 | "format" : 3 33 | } 34 | }, 35 | "x264enc" : { 36 | "property" : { 37 | "name" : "x264enc", 38 | "bitrate" : 1000, 39 | "bframes" : 3, 40 | "threads" : 4, 41 | "byte-stream" : "TRUE", 42 | "rc-lookahead" : 25, 43 | "key-int-max" : 100, 44 | "pass" : 17, 45 | "mb-tree" : true, 46 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:b-pyramid=2:direct=spatial" 47 | } 48 | }, 49 | "filesink" : { 50 | "property" : { 51 | "location" : "/home/encoder/cctv/cctv.ts" 52 | } 53 | } 54 | }, 55 | "bins" : [ 56 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc1 ! queue ! muxer.", 57 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 58 | "mpegtsmux name=muxer ! queue ! filesink" 59 | ] 60 | } 61 | ] 62 | } 63 | 64 | -------------------------------------------------------------------------------- /examples/transcoder.cbr.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "transcode", 3 | "debug" : "4", 4 | "is-live" : false, 5 | "log-path" : "/home/encoder/aa", 6 | "source" : { 7 | "elements" : { 8 | "filesrc" : { 9 | "property" : { 10 | "location" : "/home/encoder/aa/aa.ts" 11 | } 12 | }, 13 | "appsink" : { 14 | "property" : { 15 | "sync" : false, 16 | "drop" : true 17 | } 18 | } 19 | }, 20 | "bins" : [ 21 | "filesrc ! queue ! tsdemux name=demuxer", 22 | "demuxer.video ! queue ! h264parse ! queue ! avdec_h264 ! queue ! appsink name = video", 23 | "demuxer.audio ! mpegaudioparse ! queue ! mad ! queue ! appsink name = audio" 24 | ] 25 | }, 26 | "encoders" : [ 27 | { 28 | "elements" : { 29 | "appsrc" : { 30 | "property" : { 31 | "is-live" : true, 32 | "format" : 3 33 | } 34 | }, 35 | "x264enc" : { 36 | "property" : { 37 | "name" : "x264enc", 38 | "bitrate" : 3000, 39 | "bframes" : 3, 40 | "threads" : 4, 41 | "byte-stream" : "TRUE", 42 | "rc-lookahead" : 250, 43 | "key-int-max" : 100, 44 | "pass" : 0, 45 | "mb-tree" : true, 46 | "dct8x8" : true, 47 | "speed-preset" : 0, 48 | "qp-min" : 1, 49 | "qp-step" : 20, 50 | "option-string" : ":ref=3:me=umh:subme=16:merange=64:nf=1:deblock=1,-2:weightp=1:b-pyramid=2:direct=spatial:qcomp=0.2:nal-hrd=cbr:vbv-bufsize=300:vbv-maxrate=3000" 51 | } 52 | }, 53 | "filesink" : { 54 | "property" : { 55 | "location" : "/home/encoder/aa/aa_encoder.ts" 56 | } 57 | } 58 | }, 59 | "bins" : [ 60 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc1 ! queue ! muxer.", 61 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 62 | "mpegtsmux name=muxer ! queue ! filesink" 63 | ] 64 | } 65 | ] 66 | } 67 | 68 | -------------------------------------------------------------------------------- /examples/subtitle.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "subtitile", 3 | "debug" : "3", 4 | "source" : { 5 | "elements" : { 6 | "udpsrc" : { 7 | "property" : { 8 | "uri" : "udp://127.0.0.1:6003" 9 | } 10 | }, 11 | "appsink" : { 12 | "property" : { 13 | "sync" : false, 14 | "drop" : true 15 | } 16 | } 17 | }, 18 | "bins" : [ 19 | "udpsrc ! queue ! mpegtsdemux name=demuxer", 20 | "demuxer.video_0201 ! queue ! h264dec ! queue ! appsink name = video", 21 | "demuxer.audio_026d ! queue ! mad ! queue ! appsink name = audio_eng", 22 | "demuxer.audio_0271 ! queue ! mad ! queue ! appsink name = audio_tha", 23 | "demuxer.private_044f ! queue ! appsink name = subtitle_eng" 24 | ] 25 | }, 26 | "encoders" : [ 27 | { 28 | "elements" : { 29 | "videoscale" : { 30 | "caps" : "video/x-raw-yuv,width=1280,height=720" 31 | }, 32 | "x264enc" : { 33 | "property" : { 34 | "name" : "x264enc", 35 | "bitrate" : 1500, 36 | "bframes" : 3, 37 | "profile" : "high", 38 | "byte-stream" : "TRUE", 39 | "rc-lookahead" : 25, 40 | "key-int-max" : 300, 41 | "key-int-min" : 25, 42 | "pass" : 0, 43 | "mb-tree": false, 44 | "level" : 31, 45 | "mb-tree" : true, 46 | "stats-file" : "/tmp/x264.log", 47 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:lookahead-threads=4:sliced-threads=false:b-pyramid=2:direct=spatial" 48 | } 49 | } 50 | }, 51 | "bins" : [ 52 | "appsrc name=video ! queue ! videoscale ! queue ! x264enc ! queue ! muxer.", 53 | "appsrc name=audio_eng is-live=TRUE ! queue ! audioconvert ! queue ! faac name=faac1 outputformat=1 ! queue ! muxer.", 54 | "appsrc name=audio_tha is-live=TRUE ! queue ! audioconvert ! queue ! faac name=faac2 outputformat=1 ! queue ! muxer.", 55 | "appsrc name=subtitle_eng ! queue ! muxer.", 56 | "mpegtsmux name=muxer ! queue ! appsink sync=FALSE" 57 | ] 58 | } 59 | ] 60 | } 61 | 62 | -------------------------------------------------------------------------------- /tools/template.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "yangnv-%d", 3 | "debug" : "4", 4 | "is-live" : false, 5 | "log-path" : "/home/encoder/movie/yangnv-%d", 6 | "source" : { 7 | "elements" : { 8 | "filesrc" : { 9 | "property" : { 10 | "location" : "/home/encoder/movie/电视剧/养女01-35/%s.ts" 11 | } 12 | }, 13 | "videoscale" : { 14 | "caps" : "video/x-raw,width=1024,height=576" 15 | }, 16 | "appsink" : { 17 | "property" : { 18 | "sync" : false, 19 | "drop" : true 20 | } 21 | } 22 | }, 23 | "bins" : [ 24 | "filesrc ! queue ! tsdemux name=demuxer", 25 | "demuxer.video ! queue ! h264parse ! avdec_h264 ! queue ! videoscale ! queue ! appsink name = video", 26 | "demuxer.audio ! mpegaudioparse ! queue ! mad ! queue ! appsink name = audio" 27 | ] 28 | }, 29 | "encoders" : [ 30 | { 31 | "elements" : { 32 | "appsrc" : { 33 | "property" : { 34 | "is-live" : true, 35 | "format" : 3 36 | } 37 | }, 38 | "deinterlace" : { 39 | "property" : { 40 | "method" : 4, 41 | "fields" : 1 42 | } 43 | }, 44 | "x264enc" : { 45 | "property" : { 46 | "name" : "x264enc", 47 | "bitrate" : 800, 48 | "bframes" : 3, 49 | "byte-stream" : "TRUE", 50 | "pass" : 5, 51 | "mb-tree" : true, 52 | "dct8x8" : true, 53 | "speed-preset" : 0, 54 | "option-string" : ":ref=3:me=dia:subme=16:merange=16:nf=1:deblock=1,-2:weightp=1:b-pyramid=2:direct=spatial" 55 | } 56 | }, 57 | "filesink" : { 58 | "property" : { 59 | "location" : "/home/encoder/movie/电视剧/养女01-35/%s.1024x576.mp4" 60 | } 61 | } 62 | }, 63 | "bins" : [ 64 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc1 ! queue ! muxer.", 65 | "appsrc name=video ! queue ! deinterlace ! queue ! x264enc ! queue ! muxer.", 66 | "mp4mux name=muxer ! queue ! filesink" 67 | ] 68 | } 69 | ] 70 | } 71 | 72 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Gstreamill Encoder Managment 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /test/m3u8client.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | pip install m3u8 first, please 4 | """ 5 | 6 | import sys 7 | import m3u8 8 | import time 9 | import urllib2 10 | import os 11 | 12 | class Player: 13 | def __init__(self, url): 14 | self.url = url 15 | try: 16 | playlist = m3u8.load(url) 17 | while playlist.is_variant: # multiple bitrates videos 18 | self.url = playlist.base_uri + playlist.playlists[0].uri 19 | playlist = m3u8.load(self.url) 20 | except: 21 | raise NameError("OpenError") 22 | else: 23 | self.media_seq = playlist.media_sequence 24 | self.current_seq = playlist.media_sequence 25 | for segment in playlist.segments: 26 | print self.current_seq, segment.uri 27 | self.current_seq += 1 28 | 29 | def play(self): 30 | try: 31 | while True: 32 | playlist = m3u8.load(self.url) 33 | if playlist.media_sequence == self.media_seq: 34 | time.sleep(1) 35 | continue 36 | self.media_seq = playlist.media_sequence 37 | target_duration = playlist.target_duration 38 | 39 | if self.current_seq < self.media_seq: 40 | print "ERROR : missing segment" 41 | self.current_seq = self.media_seq 42 | 43 | index = -1 44 | for segment in playlist.segments: 45 | index += 1 46 | if self.media_seq + index < self.current_seq: 47 | continue 48 | seg_url = "%s%s" % (playlist.base_uri, segment.uri) 49 | response = urllib2.urlopen(seg_url) 50 | buf = response.read() 51 | if not os.path.isdir(os.path.dirname(segment.uri)): 52 | os.mkdir(os.path.dirname(segment.uri)) 53 | f = open(segment.uri, "w") 54 | f.write(buf) 55 | f.close 56 | print "index: ", self.media_seq + index, ", uri: ", segment.uri, ", size ", len(buf) 57 | self.current_seq += 1 58 | 59 | time.sleep(target_duration - 1) 60 | except: 61 | print "Error load m3u8 playlist: %s" % self.url 62 | 63 | if __name__ == "__main__": 64 | url = sys.argv[1] 65 | n = 0 66 | while True: 67 | n += 1 68 | print "replay %d times" % n 69 | try: 70 | player = Player(url) 71 | except: 72 | print "Open %s error" % url 73 | time.sleep(5) 74 | continue 75 | player.play() 76 | time.sleep(1) 77 | -------------------------------------------------------------------------------- /examples/XI006AEPRO.CARD.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "v4l2", 3 | "debug" : "mpegtsmux:2", 4 | "source" : { 5 | "elements" : { 6 | "alsasrc" : { 7 | "property" : { 8 | "blocksize" : 8192 9 | } 10 | }, 11 | "v4l2src" : { 12 | "property" : { 13 | "device" : "/dev/video0", 14 | "io-mode" : 1, 15 | "do-timestamp" : true 16 | }, 17 | "caps" : "video/x-raw,width=720,height=576" 18 | } 19 | }, 20 | "bins" : [ 21 | "alsasrc device=hw:1,0 ! queue ! appsink name=audio", 22 | "v4l2src ! queue ! appsink name=video" 23 | ] 24 | }, 25 | "encoders" : [ 26 | { 27 | "elements" : { 28 | "appsrc" : { 29 | "property" : { 30 | "format" : 3, 31 | "is-live" : true 32 | } 33 | }, 34 | "videoconvert" : { 35 | "caps" : "video/x-raw,format=I420" 36 | }, 37 | "x264enc" : { 38 | "property" : { 39 | "bitrate" : 1200, 40 | "byte-stream" : true, 41 | "bframes" :3, 42 | "qp-min" : 1, 43 | "qp-max" : 51, 44 | "qp-step" : 10, 45 | "vbv-buf-capacity" : 5000, 46 | "rc-lookahead" : 50, 47 | "mb-tree" : false, 48 | "dct8x8" : true, 49 | "speed-preset" : 0, 50 | "option-string" : ":ref=3:me=umh:subme=16:merange=64:nf=1:deblock=1,-2:weightp=1:b-pyramid=2:direct=spatial:nal-hrd=vbr" 51 | } 52 | }, 53 | "voaacenc" : { 54 | "property" : { 55 | "bitrate" : 64000 56 | } 57 | }, 58 | "appsink" : { 59 | "property" : { 60 | "sync" : false 61 | } 62 | } 63 | }, 64 | "bins" : [ 65 | "appsrc name=audio ! audioresample ! audioconvert ! voaacenc ! muxer.", 66 | "appsrc name=video ! queue ! videoconvert ! queue ! x264enc ! queue ! muxer.", 67 | "mpegtsmux name=muxer ! queue ! appsink" 68 | ] 69 | } 70 | ], 71 | "m3u8streaming" : { 72 | "version" : 3, 73 | "window-size" : 10, 74 | "segment-duration" : 3.00 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /examples/decodebin.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "CCTV", 3 | "debug" : "3", 4 | "is-live" : true, 5 | "source": { 6 | "elements": { 7 | "appsink": { 8 | "property": { 9 | "drop": "true", 10 | "sync": "false" 11 | } 12 | }, 13 | "udpsrc": { 14 | "property": { 15 | "uri": "udp://238.123.45.1:8000" 16 | } 17 | } 18 | }, 19 | "bins": [ 20 | "udpsrc ! queue ! decodebin name=decodebin", 21 | "decodebin.src_1 ! queue ! appsink name = audio", 22 | "decodebin.src_0 ! queue ! appsink name = video" 23 | ] 24 | }, 25 | "encoders" : [ 26 | { 27 | "elements" : { 28 | "appsrc" : { 29 | "property" : { 30 | "is-live" : true, 31 | "format" : 3 32 | } 33 | }, 34 | "x264enc" : { 35 | "property" : { 36 | "speed-preset": "superfast", 37 | "byte-stream" : "TRUE", 38 | "pass" : 0, 39 | "bitrate" : 1500 40 | } 41 | } 42 | }, 43 | "bins" : [ 44 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc0 ! queue ! muxer.", 45 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 46 | "mpegtsmux name=muxer ! queue ! appsink sync=FALSE" 47 | ] 48 | }, 49 | { 50 | "elements" : { 51 | "appsrc" : { 52 | "property" : { 53 | "is-live" : true, 54 | "format" : 3 55 | } 56 | }, 57 | "x264enc" : { 58 | "property" : { 59 | "speed-preset": "superfast", 60 | "byte-stream" : "TRUE", 61 | "pass" : 0, 62 | "bitrate" : 700 63 | } 64 | } 65 | }, 66 | "bins" : [ 67 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc1 ! queue ! muxer.", 68 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 69 | "mpegtsmux name=muxer ! queue ! appsink sync=FALSE" 70 | ] 71 | } 72 | ], 73 | "m3u8streaming" : { 74 | "version" : 3, 75 | "window-size" : 4, 76 | "segment-duration" : 5.00 77 | }, 78 | "dvr_duration": 86400 79 | } 80 | 81 | -------------------------------------------------------------------------------- /examples/rtmp.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "rtmp", 3 | "debug" : "3", 4 | "is-live" : true, 5 | "source": { 6 | "elements": { 7 | "appsink": { 8 | "property": { 9 | "drop": "true", 10 | "sync": "false" 11 | } 12 | }, 13 | "rtmpsrc": { 14 | "property": { 15 | "location": "rtmp://106.39.160.190/live/citchannel" 16 | } 17 | } 18 | }, 19 | "bins": [ 20 | "rtmpsrc ! queue ! decodebin name=decodebin", 21 | "decodebin.src ! queue ! appsink name = audio", 22 | "decodebin.src ! queue ! appsink name = video" 23 | ] 24 | }, 25 | "encoders" : [ 26 | { 27 | "elements" : { 28 | "appsrc" : { 29 | "property" : { 30 | "is-live" : true, 31 | "format" : 3 32 | } 33 | }, 34 | "x264enc" : { 35 | "property" : { 36 | "speed-preset": "superfast", 37 | "byte-stream" : "TRUE", 38 | "pass" : 0, 39 | "bitrate" : 1500 40 | } 41 | } 42 | }, 43 | "bins" : [ 44 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc0 ! queue ! muxer.", 45 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 46 | "mpegtsmux name=muxer ! queue ! appsink sync=FALSE" 47 | ] 48 | }, 49 | { 50 | "elements" : { 51 | "appsrc" : { 52 | "property" : { 53 | "is-live" : true, 54 | "format" : 3 55 | } 56 | }, 57 | "x264enc" : { 58 | "property" : { 59 | "speed-preset": "superfast", 60 | "byte-stream" : "TRUE", 61 | "pass" : 0, 62 | "bitrate" : 700 63 | } 64 | } 65 | }, 66 | "bins" : [ 67 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc1 ! queue ! muxer.", 68 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 69 | "mpegtsmux name=muxer ! queue ! appsink sync=FALSE" 70 | ] 71 | } 72 | ], 73 | "m3u8streaming" : { 74 | "version" : 3, 75 | "window-size" : 4, 76 | "segment-duration" : 5.00 77 | }, 78 | "dvr_duration": 86400 79 | } 80 | 81 | -------------------------------------------------------------------------------- /docs/_themes/sphinx13/layout.html: -------------------------------------------------------------------------------- 1 | {# 2 | sphinxdoc/layout.html 3 | ~~~~~~~~~~~~~~~~~~~~~ 4 | 5 | Sphinx layout template for the sphinxdoc theme. 6 | 7 | :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 | {%- extends "basic/layout.html" %} 11 | 12 | {# put the sidebar before the body #} 13 | {% block sidebar1 %}{{ sidebar() }}{% endblock %} 14 | {% block sidebar2 %}{% endblock %} 15 | 16 | {% block extrahead %} 17 | 19 | {{ super() }} 20 | {%- if not embedded %} 21 | 28 | 56 | {%- endif %} 57 | {% endblock %} 58 | 59 | {% block rootrellink %} 60 |
  • Gstreamill home |
  • 61 |
  • Documentation »
  • 62 | {% endblock %} 63 | 64 | {% block header %} 65 | 77 | {% endblock %} 78 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | MAINTAINER Zhang Ping, dqzhangp@163.com 4 | 5 | # install required packages 6 | RUN apt-get update && \ 7 | apt-get install -y git autoconf automake libtool pkg-config autopoint gettext liborc-0.4-dev make libglib2.0-dev flex bison && \ 8 | git clone git://anongit.freedesktop.org/gstreamer/gstreamer && \ 9 | cd gstreamer && \ 10 | git checkout 1.16.2 && \ 11 | ./autogen.sh --prefix=/usr --disable-gtk-doc-pdf --disable-gtk-doc && \ 12 | make && \ 13 | make install && \ 14 | cd / && rm -rf gstreamer 15 | RUN apt-get install -y libpango1.0-dev libasound2-dev && \ 16 | git clone git://anongit.freedesktop.org/gstreamer/gst-plugins-base && \ 17 | cd gst-plugins-base && \ 18 | git checkout 1.16.2 && \ 19 | ./autogen.sh --prefix=/usr --disable-gtk-doc-pdf --disable-gtk-doc && \ 20 | make && \ 21 | make install && \ 22 | cd / && rm -rf gst-plugins-base 23 | RUN apt-get install -y libsoup2.4-dev && \ 24 | RUN git clone git://anongit.freedesktop.org/gstreamer/gst-plugins-good && \ 25 | cd gst-plugins-good && \ 26 | git checkout 1.16.2 && \ 27 | ./autogen.sh --prefix=/usr --disable-gtk-doc-pdf --disable-gtk-doc && \ 28 | make && \ 29 | make install && \ 30 | cd / && rm -rf gst-plugins-good 31 | RUN apt-get install -y libmpeg2-4-dev libmad-ocaml-dev libmp3lame-dev liba52-0.7.4-dev libx264-dev && \ 32 | git clone git://anongit.freedesktop.org/gstreamer/gst-plugins-ugly && \ 33 | cd gst-plugins-ugly && \ 34 | git checkout 1.16.2 && \ 35 | ./autogen.sh --prefix=/usr --disable-gtk-doc-pdf --disable-gtk-doc --disable-fatal-warnings && \ 36 | make && \ 37 | make install && \ 38 | cd / && rm -rf gst-plugins-ugly 39 | RUN apt-get install -y libvoaacenc-ocaml-dev librtmp-dev libx265-dev openssl libssl-dev --fix-missing && \ 40 | git clone git://anongit.freedesktop.org/gstreamer/gst-plugins-bad && \ 41 | cd gst-plugins-bad && \ 42 | git checkout 1.16.2 && \ 43 | ./autogen.sh --prefix=/usr --disable-gtk-doc-pdf --disable-gtk-doc --disable-gl && \ 44 | make && \ 45 | make install && \ 46 | cd / && rm -rf gst-plugins-bad 47 | RUN apt-get install -y yasm && \ 48 | git clone git://anongit.freedesktop.org/gstreamer/gst-ffmpeg && \ 49 | cd gst-ffmpeg && \ 50 | git checkout 1.16.2 && \ 51 | ./autogen.sh --prefix=/usr --disable-gtk-doc-pdf --disable-gtk-doc --disable-fatal-warnings && \ 52 | make && \ 53 | make install && \ 54 | cd / && rm -rf gst-libav 55 | 56 | # install gsreamill 57 | RUN apt-get install -y libaugeas-dev && \ 58 | git clone https://github.com/i4tv/gstreamill.git 59 | RUN cd gstreamill && \ 60 | git pull && \ 61 | git checkout v1.0.0 && \ 62 | ./autogen.sh && \ 63 | ./configure --prefix=/usr && \ 64 | make && \ 65 | make install && \ 66 | cd / && rm -rf gstreamill 67 | 68 | CMD mount -o remount -o size=10240M /dev/shm && gstreamill -d 69 | 70 | EXPOSE 20118 71 | EXPOSE 20119 72 | -------------------------------------------------------------------------------- /src/gstreamill.h: -------------------------------------------------------------------------------- 1 | /* 2 | * gstreamill scheduler 3 | * 4 | * Copyright (C) Zhang Ping 5 | * 6 | */ 7 | 8 | #ifndef __GSTREAMILL_H__ 9 | #define __GSTREAMILL_H__ 10 | 11 | #include 12 | 13 | #include "config.h" 14 | #include "job.h" 15 | #include "log.h" 16 | 17 | #define SYNC_THRESHHOLD 3000000000 /* 1000ms */ 18 | #define HEARTBEAT_THRESHHOLD 7000000000 /* 7000ms */ 19 | #define NONLIVE_HEARTBEAT_THRESHHOLD 60000000000 /* 60s */ 20 | #define ENCODER_OUTPUT_HEARTBEAT_THRESHHOLD 30000000000 /* 30s */ 21 | 22 | #define LOG_SIZE 40*1024*1024 23 | #define LOG_ROTATE 100 24 | 25 | typedef struct _Gstreamill Gstreamill; 26 | typedef struct _GstreamillClass GstreamillClass; 27 | 28 | typedef struct _RecordData { 29 | gchar *dir, *file, *buf; 30 | gsize segment_size; 31 | } RecordData; 32 | 33 | struct _Gstreamill { 34 | GObject parent; 35 | 36 | gchar *exe_path; 37 | gboolean stop; /* gstreamill exit if stop == TURE */ 38 | gint mode; /* running mode */ 39 | GstClock *system_clock; 40 | gchar *start_time; 41 | 42 | /* message receive thread */ 43 | Log *log; 44 | gchar *log_dir; 45 | GThread *msg_thread; 46 | guint64 last_dvr_clean_time; 47 | 48 | /* segment record thread */ 49 | GMutex record_queue_mutex; 50 | GCond record_queue_cond; 51 | GQueue *record_queue; 52 | GThread *record_thread; 53 | 54 | /* remove dvr thread */ 55 | GMutex remove_dvr_queue_mutex; 56 | GCond remove_dvr_queue_cond; 57 | GQueue *remove_dvr_queue; 58 | GThread *remove_dvr_thread; 59 | 60 | gdouble cpu_average; 61 | gdouble cpu_current; 62 | 63 | GMutex job_list_mutex; 64 | GSList *job_list; 65 | }; 66 | 67 | struct _GstreamillClass { 68 | GObjectClass parent; 69 | }; 70 | 71 | #define TYPE_GSTREAMILL (gstreamill_get_type()) 72 | #define GSTREAMILL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_GSTREAMILL, Gstreamill)) 73 | #define GSTREAMILL_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), TYPE_GSTREAMILL, GstreamillClass)) 74 | #define IS_GSTREAMILL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_GSTREAMILL)) 75 | #define IS_GSTREAMILL_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE ((cls), TYPE_GSTREAMILL)) 76 | #define GSTREAMILL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_GSTREAMILL, GstreamillClass)) 77 | #define gstreamill_new(...) (g_object_new(TYPE_GSTREAMILL, ## __VA_ARGS__, NULL)) 78 | 79 | GType gstreamill_get_type (void); 80 | gint gstreamill_start (Gstreamill *gstreamill); 81 | void gstreamill_stop (Gstreamill *gstreamill); 82 | gchar * gstreamill_get_start_time (Gstreamill *gstreamill); 83 | gchar * gstreamill_job_start (Gstreamill *gstreamill, gchar *job_desc); 84 | gchar * gstreamill_job_stop (Gstreamill *gstreamill, gchar *name); 85 | gchar * gstreamill_stat (Gstreamill *gstreamill); 86 | gchar * gstreamill_list_jobs (Gstreamill *gstreamill); 87 | gchar * gstreamill_job_stat (Gstreamill *gstreamill, gchar *name); 88 | gchar * gstreamill_gstreamer_stat (Gstreamill *gstreamill, gchar *uri); 89 | void gstreamill_unaccess (Gstreamill *gstreamill, gchar *uri); 90 | Job * gstreamill_get_job (Gstreamill *gstreamill, gchar *uri); 91 | gint gstreamill_job_number (Gstreamill *gstreamill); 92 | EncoderOutput * gstreamill_get_encoder_output (Gstreamill *gstreamill, gchar *uri); 93 | gchar * gstreamill_get_master_m3u8playlist (Gstreamill *gstreamill, gchar *uri); 94 | 95 | #endif /* __GSTREAMILL_H__ */ 96 | -------------------------------------------------------------------------------- /tools/sd.temp: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "%s", 3 | "debug" : "3", 4 | "is-live" : true, 5 | "source": { 6 | "elements": { 7 | "appsink": { 8 | "property": { 9 | "drop": "true", 10 | "sync": "false" 11 | } 12 | }, 13 | "udpsrc": { 14 | "property": { 15 | "uri": "%s" 16 | } 17 | } 18 | }, 19 | "bins": [ 20 | "udpsrc ! queue ! tsdemux name=demuxer", 21 | "demuxer.video ! queue ! h264parse ! queue ! avdec_h264 ! queue ! appsink name = video", 22 | "demuxer.audio ! queue ! mpegaudioparse ! queue ! mad ! queue ! appsink name = audio" 23 | ] 24 | }, 25 | "encoders" : [ 26 | { 27 | "elements" : { 28 | "appsrc" : { 29 | "property" : { 30 | "is-live" : true, 31 | "format" : 3 32 | } 33 | }, 34 | "x264enc" : { 35 | "property" : { 36 | "name" : "x264enc", 37 | "bitrate" : 1500, 38 | "bframes" : 3, 39 | "byte-stream" : "TRUE", 40 | "rc-lookahead" : 25, 41 | "key-int-max" : 100, 42 | "pass" : 0, 43 | "mb-tree" : false, 44 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:b-pyramid=2:direct=spatial" 45 | } 46 | } 47 | }, 48 | "bins" : [ 49 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc0! queue ! muxer.", 50 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 51 | "mpegtsmux name=muxer ! queue ! appsink sync=FALSE" 52 | ] 53 | }, 54 | { 55 | "elements" : { 56 | "appsrc" : { 57 | "property" : { 58 | "is-live" : true, 59 | "format" : 3 60 | } 61 | }, 62 | "x264enc" : { 63 | "property" : { 64 | "name" : "x264enc", 65 | "bitrate" : 700, 66 | "bframes" : 3, 67 | "byte-stream" : "TRUE", 68 | "rc-lookahead" : 25, 69 | "key-int-max" : 100, 70 | "pass" : 0, 71 | "mb-tree" : false, 72 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:b-pyramid=2:direct=spatial" 73 | } 74 | } 75 | }, 76 | "bins" : [ 77 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc1 ! queue ! muxer.", 78 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 79 | "mpegtsmux name=muxer ! queue ! appsink sync=FALSE" 80 | ] 81 | } 82 | ], 83 | "m3u8streaming" : { 84 | "version" : 3, 85 | "window-size" : 4, 86 | "segment-duration" : 5.00 87 | }, 88 | "dvr_duration": 86400 89 | } 90 | 91 | -------------------------------------------------------------------------------- /src/source.h: -------------------------------------------------------------------------------- 1 | /* 2 | * source 3 | * 4 | * Copyright (C) Zhang Ping 5 | */ 6 | 7 | #ifndef __SOURCE_H__ 8 | #define __SOURCE_H__ 9 | 10 | #include 11 | 12 | #include "log.h" 13 | #include "m3u8playlist.h" 14 | 15 | #define SOURCE_RING_SIZE 512 16 | #define STREAM_NAME_LEN 1024 17 | #define DELTA 30000000 /* 30ms */ 18 | 19 | typedef struct _Source Source; 20 | typedef struct _SourceClass SourceClass; 21 | 22 | typedef struct _Link { 23 | GstElement *src; 24 | GstElement *sink; 25 | gchar *src_name; 26 | gchar *sink_name; 27 | gchar *src_pad_name; 28 | gchar *sink_pad_name; 29 | gchar *caps; 30 | } Link; 31 | 32 | typedef struct _Bin { 33 | gchar *name; 34 | GSList *elements; 35 | GstElement *first; 36 | GstElement *last; 37 | GSList *links; 38 | Link *previous; 39 | Link *next; 40 | gulong signal_id; 41 | } Bin; 42 | 43 | typedef struct _SourceStreamState { 44 | gchar name[STREAM_NAME_LEN]; 45 | GstClockTime current_timestamp; 46 | GstClockTime last_heartbeat; 47 | } SourceStreamState; 48 | 49 | typedef struct _SourceState { 50 | /* 51 | * sync error cause sync_error_times inc, 52 | * sync normal cause sync_error_times reset to zero, 53 | * sync_error_times == 5 cause the livejob restart. 54 | */ 55 | gint64 *duration; 56 | guint64 sync_error_times; 57 | gint64 stream_count; 58 | SourceStreamState *streams; 59 | } SourceState; 60 | 61 | typedef struct _RingBuffer { 62 | gboolean is_rap; 63 | GstClockTime timestamp; 64 | GstClockTime duration; 65 | GstSample *sample; 66 | } RingBuffer; 67 | 68 | typedef struct _SourceStream { 69 | gchar *name; 70 | gchar *codec; 71 | gboolean is_live; 72 | gboolean eos; 73 | RingBuffer *ring[SOURCE_RING_SIZE]; 74 | gint current_position; /* current source output position */ 75 | GstClock *system_clock; 76 | GstClockTime next_segment_timestamp; 77 | GstClockTime current_segment_duration; 78 | GstClockTime segment_duration; 79 | GstClockTime segment_duration_minus_delta; 80 | GstClockTime last_segment_pts; 81 | GArray *encoders; 82 | 83 | SourceStreamState *state; 84 | } SourceStream; 85 | 86 | struct _Source { 87 | GObject parent; 88 | 89 | gchar *name; 90 | GstClock *system_clock; 91 | GSList *bins; 92 | GstElement *pipeline; 93 | 94 | GArray *streams; 95 | }; 96 | 97 | struct _SourceClass { 98 | GObjectClass parent; 99 | }; 100 | 101 | #define TYPE_SOURCE (source_get_type()) 102 | #define SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_SOURCE, Source)) 103 | #define SOURCE_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), TYPE_SOURCE, SourceClass)) 104 | #define IS_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_SOURCE)) 105 | #define IS_SOURCE_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE ((cls), TYPE_SOURCE)) 106 | #define SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_SOURCE, SourceClass)) 107 | #define source_new(...) (g_object_new(TYPE_SOURCE, ## __VA_ARGS__, NULL)) 108 | 109 | GType source_get_type (void); 110 | 111 | gboolean bus_callback (GstBus *bus, GstMessage *msg, gpointer user_data); 112 | GSList * bins_parse (gchar *job, gchar *pipeline); 113 | Source * source_initialize (gchar *job, SourceState *source_stat); 114 | 115 | #endif /* __SOURCE_H__ */ 116 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " pickle to make pickle files" 22 | @echo " json to make JSON files" 23 | @echo " htmlhelp to make HTML files and a HTML help project" 24 | @echo " qthelp to make HTML files and a qthelp project" 25 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 26 | @echo " changes to make an overview of all changed/added/deprecated items" 27 | @echo " linkcheck to check all external links for integrity" 28 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 29 | 30 | clean: 31 | -rm -rf $(BUILDDIR)/* 32 | 33 | html: 34 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 35 | @echo 36 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 37 | 38 | dirhtml: 39 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 40 | @echo 41 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 42 | 43 | pickle: 44 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 45 | @echo 46 | @echo "Build finished; now you can process the pickle files." 47 | 48 | json: 49 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 50 | @echo 51 | @echo "Build finished; now you can process the JSON files." 52 | 53 | htmlhelp: 54 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 55 | @echo 56 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 57 | ".hhp project file in $(BUILDDIR)/htmlhelp." 58 | 59 | qthelp: 60 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 61 | @echo 62 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 63 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 64 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/GstreamillTutorial.qhcp" 65 | @echo "To view the help file:" 66 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/GstreamillTutorial.qhc" 67 | 68 | latex: 69 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 70 | @echo 71 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 72 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ 73 | "run these through (pdf)latex." 74 | 75 | changes: 76 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 77 | @echo 78 | @echo "The overview file is in $(BUILDDIR)/changes." 79 | 80 | linkcheck: 81 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 82 | @echo 83 | @echo "Link check complete; look for any errors in the above output " \ 84 | "or in $(BUILDDIR)/linkcheck/output.txt." 85 | 86 | doctest: 87 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 88 | @echo "Testing of doctests in the sources finished, look at the " \ 89 | "results in $(BUILDDIR)/doctest/output.txt." 90 | -------------------------------------------------------------------------------- /examples/ip.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "cctv2", 3 | "debug" : "3", 4 | "is-live" : true, 5 | "source" : { 6 | "elements" : { 7 | "udpsrc" : { 8 | "property" : { 9 | "uri" : "udp://127.0.0.1:6003" 10 | } 11 | }, 12 | "appsink" : { 13 | "property" : { 14 | "sync" : false, 15 | "drop" : true 16 | } 17 | } 18 | }, 19 | "bins" : [ 20 | "udpsrc ! queue ! tsdemux name=demuxer", 21 | "demuxer.video ! queue ! mpeg2dec ! queue ! appsink name = video", 22 | "demuxer.audio ! mpegaudioparse ! queue ! mad ! queue ! appsink name = audio" 23 | ] 24 | }, 25 | "encoders" : [ 26 | { 27 | "elements" : { 28 | "appsrc" : { 29 | "property" : { 30 | "is-live" : true, 31 | "format" : 3 32 | } 33 | }, 34 | "x264enc" : { 35 | "property" : { 36 | "name" : "x264enc", 37 | "bitrate" : 1500, 38 | "bframes" : 3, 39 | "byte-stream" : "TRUE", 40 | "rc-lookahead" : 25, 41 | "key-int-max" : 100, 42 | "pass" : 17, 43 | "mb-tree" : true, 44 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:b-pyramid=2:direct=spatial" 45 | } 46 | } 47 | }, 48 | "bins" : [ 49 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc0! queue ! muxer.", 50 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 51 | "mpegtsmux name=muxer ! queue ! appsink sync=FALSE" 52 | ], 53 | "udpstreaming" : "127.0.0.1:12345" 54 | }, 55 | { 56 | "elements" : { 57 | "appsrc" : { 58 | "property" : { 59 | "is-live" : true, 60 | "format" : 3 61 | } 62 | }, 63 | "x264enc" : { 64 | "property" : { 65 | "name" : "x264enc", 66 | "bitrate" : 1000, 67 | "bframes" : 3, 68 | "byte-stream" : "TRUE", 69 | "rc-lookahead" : 25, 70 | "key-int-max" : 100, 71 | "pass" : 0, 72 | "mb-tree" : false, 73 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:b-pyramid=2:direct=spatial" 74 | } 75 | } 76 | }, 77 | "bins" : [ 78 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc1 ! queue ! muxer.", 79 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 80 | "mpegtsmux name=muxer ! queue ! appsink sync=FALSE" 81 | ], 82 | "udpstreaming" : "127.0.0.1:22345" 83 | } 84 | ], 85 | "m3u8streaming" : { 86 | "version" : 3, 87 | "window-size" : 4, 88 | "segment-duration" : 10.00 89 | } 90 | } 91 | 92 | -------------------------------------------------------------------------------- /src/job.h: -------------------------------------------------------------------------------- 1 | /* 2 | * job 3 | * 4 | * Copyright (C) Zhang Ping 5 | */ 6 | 7 | #ifndef __JOB_H__ 8 | #define __JOB_H__ 9 | 10 | #include "config.h" 11 | #include "source.h" 12 | #include "encoder.h" 13 | 14 | #define SHM_SIZE 64*1024*1024 15 | 16 | #define MEDIA_LOCATION "/var/lib/gstreamill" 17 | 18 | /* running mode */ 19 | #define DAEMON_MODE 0 20 | #define DEBUG_MODE 1 21 | #define SINGLE_JOB_MODE 2 22 | 23 | typedef struct _Job Job; 24 | typedef struct _JobClass JobClass; 25 | 26 | /** job state 27 | * JOB_STATE_VOID_PENDING: no pending state. 28 | * JOB_STATE_READY: creating job process, subsequent state is JOB_STATE_PLAYLING or JOB_STATE_START_FAILURE 29 | * JOB_STATE_PLAYLING: playing state, subprocess running. 30 | * JOB_STATE_START_FAILURE: subprocess start failure. 31 | * JOB_STATE_STOPING: stoping state, subprocess is being stop. 32 | * JOB_STATE_STOPED: stoped state, subprocess finished. 33 | */ 34 | typedef enum { 35 | JOB_STATE_VOID_PENDING = 0, 36 | JOB_STATE_READY = 1, 37 | JOB_STATE_PLAYING = 2, 38 | JOB_STATE_START_FAILURE = 3, 39 | JOB_STATE_STOPED = 4 40 | } JobState; 41 | 42 | typedef struct _JobOutput { 43 | gchar *job_description; 44 | gchar *semaphore_name; 45 | sem_t *semaphore; /* access of job output should be exclusive */ 46 | guint64 *state; 47 | SourceState source; 48 | gint64 encoder_count; 49 | EncoderOutput *encoders; 50 | 51 | gchar *master_m3u8_playlist; 52 | } JobOutput; 53 | 54 | struct _Job { 55 | GObject parent; 56 | 57 | gchar *description; 58 | gchar *exe_path; 59 | gchar *name; /* same as the name in job config file */ 60 | gboolean is_live; 61 | gboolean stoping; 62 | gboolean eos; 63 | gint id; 64 | gchar *log_dir; 65 | GstClock *system_clock; 66 | gsize output_size; 67 | gint output_fd; 68 | JobOutput *output; /* Interface for producing */ 69 | gint64 age; /* (re)start times of the job */ 70 | gchar *last_start_time; /* last start up time */ 71 | pid_t worker_pid; 72 | 73 | GMutex access_mutex; /* current_access access should be mutex */ 74 | gint current_access; /* number of current access client */ 75 | 76 | guint64 last_utime; /* last process user time */ 77 | guint64 last_stime; /* last process system time */ 78 | guint64 last_ctime; /* last process cpu time */ 79 | guint64 start_ctime; /* cpu time at process start */ 80 | gdouble cpu_average; 81 | gdouble cpu_current; 82 | guint64 memory; 83 | 84 | Source *source; 85 | GArray *encoder_array; 86 | }; 87 | 88 | struct _JobClass { 89 | GObjectClass parent; 90 | }; 91 | 92 | #define TYPE_JOB (job_get_type()) 93 | #define JOB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_JOB, Job)) 94 | #define JOB_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), TYPE_JOB, JobClass)) 95 | #define IS_JOB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_JOB)) 96 | #define IS_JOB_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE ((cls), TYPE_JOB)) 97 | #define JOB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_JOB, JobClass)) 98 | #define job_new(...) (g_object_new(TYPE_JOB, ## __VA_ARGS__, NULL)) 99 | 100 | GType job_get_type (void); 101 | 102 | gchar * job_state_get_name (guint64 state); 103 | gint job_initialize (Job *job, gint mode, gint shm_fd, gchar *shm_p); 104 | gint job_output_initialize (Job *job); 105 | gint job_encoders_output_initialize (Job *job); 106 | void job_reset (Job *job); 107 | gint job_stat_update (Job *job); 108 | gint job_start (Job *job); 109 | gint job_stop (Job *job, gint sig); 110 | void job_render_master_m3u8_playlist (Job *job); 111 | 112 | #endif /* __JOB_H__ */ 113 | -------------------------------------------------------------------------------- /usr/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | admindir = /usr/share/gstreamill/admin 3 | admin_DATA = \ 4 | share/gstreamill/admin/index.html \ 5 | share/gstreamill/admin/header.html \ 6 | share/gstreamill/admin/footer.html \ 7 | share/gstreamill/admin/newlivejob.html \ 8 | share/gstreamill/admin/joblist.html \ 9 | share/gstreamill/admin/newtranscodejob.html \ 10 | share/gstreamill/admin/jobmanage.html \ 11 | share/gstreamill/admin/jobstatus.html \ 12 | share/gstreamill/admin/system.html \ 13 | share/gstreamill/admin/gstreamill.html \ 14 | share/gstreamill/admin/setnetwork.html \ 15 | share/gstreamill/admin/mediaman.html 16 | 17 | admincssdir = /usr/share/gstreamill/admin/css 18 | admincss_DATA = \ 19 | share/gstreamill/admin/css/bootstrap.css \ 20 | share/gstreamill/admin/css/bootstrap-theme.css \ 21 | share/gstreamill/admin/css/gstreamill.css 22 | 23 | adminjsdir = /usr/share/gstreamill/admin/js 24 | adminjs_DATA = \ 25 | share/gstreamill/admin/js/bootstrap.js \ 26 | share/gstreamill/admin/js/bootbox.js \ 27 | share/gstreamill/admin/js/jquery.js \ 28 | share/gstreamill/admin/js/i18next.js \ 29 | share/gstreamill/admin/js/gstreamill.js \ 30 | share/gstreamill/admin/js/resumable.js 31 | 32 | adminfontsdir = /usr/share/gstreamill/admin/fonts 33 | adminfonts_DATA = \ 34 | share/gstreamill/admin/fonts/glyphicons-halflings-regular.eot \ 35 | share/gstreamill/admin/fonts/glyphicons-halflings-regular.svg \ 36 | share/gstreamill/admin/fonts/glyphicons-halflings-regular.ttf \ 37 | share/gstreamill/admin/fonts/glyphicons-halflings-regular.woff 38 | 39 | adminimgdir = /usr/share/gstreamill/admin/img 40 | adminimg_DATA = \ 41 | share/gstreamill/admin/img/logo.png \ 42 | share/gstreamill/admin/img/waiting.gif 43 | 44 | adminschemasdir = /usr/share/gstreamill/admin/schemas 45 | adminschemas_DATA = \ 46 | share/gstreamill/admin/schemas/live_source_test.json \ 47 | share/gstreamill/admin/schemas/live_source_v4l2.json \ 48 | share/gstreamill/admin/schemas/live_source_network.json \ 49 | share/gstreamill/admin/schemas/live_encoder_phone.json \ 50 | share/gstreamill/admin/schemas/live_encoder_sdtv.json \ 51 | share/gstreamill/admin/schemas/live_encoder_hdtv.json \ 52 | share/gstreamill/admin/schemas/transcode_source_decodebin.json \ 53 | share/gstreamill/admin/schemas/transcode_encoder_phone_mp4.json \ 54 | share/gstreamill/admin/schemas/transcode_encoder_pad_mp4.json \ 55 | share/gstreamill/admin/schemas/transcode_encoder_tv_mp4.json \ 56 | share/gstreamill/admin/schemas/transcode_encoder_hdtv_mp4.json \ 57 | share/gstreamill/admin/schemas/transcode_encoder_phone_m3u8.json \ 58 | share/gstreamill/admin/schemas/transcode_encoder_pad_m3u8.json \ 59 | share/gstreamill/admin/schemas/transcode_encoder_tv_m3u8.json \ 60 | share/gstreamill/admin/schemas/transcode_encoder_hdtv_m3u8.json 61 | 62 | localesdir = /usr/share/gstreamill/admin/locales/dev 63 | locales_DATA = \ 64 | share/gstreamill/admin/locales/dev/translation.json 65 | 66 | localesendir = /usr/share/gstreamill/admin/locales/en 67 | localesen_DATA = \ 68 | share/gstreamill/admin/locales/en/translation.json 69 | 70 | localeszh_CNdir = /usr/share/gstreamill/admin/locales/zh_CN 71 | localeszh_CN_DATA = \ 72 | share/gstreamill/admin/locales/zh_CN/translation.json 73 | 74 | EXTRA_DIST = lib local 75 | 76 | install-exec-hook: 77 | ps -p1 | grep systemd && { \ 78 | mkdir -p /usr/lib/systemd/system; \ 79 | sed -e 's:$$(bindir):$(bindir):' lib/systemd/system/gstreamill.service.in > lib/systemd/system/gstreamill.service; \ 80 | install -c -m 644 lib/systemd/system/gstreamill.service '/usr/lib/systemd/system'; \ 81 | } || true 82 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * utils 4 | * 5 | * Copyright (C) Zhang Ping 6 | */ 7 | 8 | #define _XOPEN_SOURCE 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "utils.h" 19 | 20 | gchar * unicode_file_name_2_shm_name (gchar *filename) 21 | { 22 | const guchar *data; 23 | gsize len; 24 | gchar *shm_name; 25 | gint i; 26 | 27 | data = (guchar *)filename; 28 | len = strlen (filename); 29 | shm_name = g_base64_encode (data, len); 30 | for (i = 0; i < strlen (shm_name); i++) { 31 | if (shm_name[i] == '+') { 32 | shm_name[i] = '='; 33 | } 34 | if (shm_name[i] == '/') { 35 | shm_name[i] = '_'; 36 | } 37 | } 38 | 39 | return shm_name; 40 | } 41 | 42 | gchar * get_address (struct sockaddr in_addr) 43 | { 44 | struct sockaddr_in *addr; 45 | 46 | addr = (struct sockaddr_in *)&in_addr; 47 | return inet_ntoa (addr->sin_addr); 48 | } 49 | 50 | gushort get_port (struct sockaddr in_addr) 51 | { 52 | struct sockaddr_in *addr; 53 | 54 | addr = (struct sockaddr_in *)&in_addr; 55 | return ntohs (addr->sin_port); 56 | } 57 | 58 | /** 59 | * timestamp_to_segment_dir: 60 | * @timestamp: (in): timestamp to be converted segment dir 61 | * 62 | * Convert timestamp to segment dir: yyyymmddhh. 63 | * 64 | * Returns: char type of segment dir 65 | */ 66 | gchar *timestamp_to_segment_dir (time_t timestamp) 67 | { 68 | struct tm tm; 69 | gchar *seg_path; 70 | 71 | if (NULL == localtime_r (×tamp, &tm)) { 72 | GST_ERROR ("timestamp to segment dir error: %lu", timestamp); 73 | return NULL; 74 | } 75 | seg_path = g_strdup_printf ("%04d%02d%02d%02d", 76 | tm.tm_year + 1900, 77 | tm.tm_mon + 1, 78 | tm.tm_mday, 79 | tm.tm_hour); 80 | 81 | return seg_path; 82 | } 83 | 84 | gint segment_dir_to_timestamp (gchar *dir, time_t *timestamp) 85 | { 86 | gint number, year, month, mday, hour; 87 | struct tm tm; 88 | gchar date[20]; 89 | 90 | number = sscanf (dir, "%04d%02d%02d%02d", &year, &month, &mday, &hour); 91 | if (number != 4) { 92 | GST_WARNING ("segment dir to timestamp sscanf error: %s", dir); 93 | return -1; 94 | } 95 | 96 | sprintf (date, "%04d-%02d-%02d %02d:00:00", year, month, mday, hour); 97 | memset (&tm, 0, sizeof (struct tm)); 98 | if (strptime (date, "%Y-%m-%d %H:%M:%S", &tm) == NULL) { 99 | GST_WARNING ("segment dir to timestamp strptime error: %s", dir); 100 | return -2; 101 | } 102 | *timestamp = mktime (&tm); 103 | if (*timestamp == -1) { 104 | GST_WARNING ("segment dir to timestamp mktime error: %s", dir); 105 | return -3; 106 | } 107 | 108 | return 0; 109 | } 110 | 111 | gint remove_dir (gchar *dir) 112 | { 113 | gint ret = 0, i; 114 | glob_t pglob; 115 | gchar *pattern; 116 | 117 | if (g_file_test (dir, G_FILE_TEST_IS_DIR)) { 118 | pattern = g_strdup_printf ("%s/*", dir); 119 | glob (pattern, 0, NULL, &pglob); 120 | g_free (pattern); 121 | if (pglob.gl_pathc == 0) { 122 | ret = g_remove (dir); 123 | 124 | } else { 125 | /* remove subdirectory */ 126 | for (i = 0; i < pglob.gl_pathc; i++) { 127 | ret = remove_dir (pglob.gl_pathv[i]); 128 | 129 | if (ret != 0) { 130 | break; 131 | } 132 | } 133 | if (ret == 0) { 134 | ret = g_remove (dir); 135 | } 136 | } 137 | globfree (&pglob); 138 | 139 | } else { 140 | ret = g_remove (dir); 141 | } 142 | 143 | return ret; 144 | } 145 | -------------------------------------------------------------------------------- /src/encoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * encoder 3 | * 4 | * Copyright (C) Zhang Ping 5 | */ 6 | 7 | #ifndef __ENCODER_H__ 8 | #define __ENCODER_H__ 9 | 10 | #include 11 | #include 12 | 13 | #define MSG_SOCK_PATH "/tmp/millsock" 14 | 15 | typedef struct _Encoder Encoder; 16 | typedef struct _EncoderClass EncoderClass; 17 | 18 | typedef struct _EncoderStreamState { 19 | gchar name[STREAM_NAME_LEN]; 20 | GstClockTime current_timestamp; 21 | GstClockTime last_heartbeat; 22 | } EncoderStreamState; 23 | 24 | typedef struct _EncoderOutput { 25 | gchar name[STREAM_NAME_LEN]; 26 | sem_t *semaphore; /* pointer to job semaphore */ 27 | gchar *codec; 28 | GstClockTime *heartbeat; 29 | gboolean *eos; 30 | gchar *cache_addr; 31 | guint64 cache_size; 32 | guint64 *total_count; /* total output packet counts */ 33 | guint64 *head_addr; 34 | guint64 *tail_addr; 35 | guint64 *last_rap_addr; /* last random access point address */ 36 | gint64 stream_count; 37 | EncoderStreamState *streams; 38 | 39 | /* m3u8 streaming */ 40 | M3U8Playlist *m3u8_playlist; 41 | GstClockTime segment_duration; 42 | guint version, playlist_window_size; 43 | GstClockTime last_timestamp; /* last segment timestamp */ 44 | GstClock *system_clock; 45 | 46 | /* timeshift and dvr */ 47 | gchar *record_path; 48 | guint64 dvr_duration; 49 | } EncoderOutput; 50 | 51 | typedef struct _EncoderStream { 52 | gchar *name; 53 | gboolean is_segment_reference; 54 | SourceStream *source; 55 | GstClock *system_clock; 56 | gint current_position; /* encoder position */ 57 | EncoderStreamState *state; 58 | Encoder *encoder; 59 | } EncoderStream; 60 | 61 | struct _Encoder { 62 | GObject parent; 63 | 64 | gchar *job_name; 65 | gchar *name; 66 | gint id; 67 | GstClock *system_clock; 68 | GSList *bins; 69 | GstElement *pipeline; 70 | GArray *streams; 71 | EncoderOutput *output; 72 | 73 | /* udp streaming */ 74 | GstElement *udpstreaming; 75 | GstElement *appsrc; 76 | GstBuffer *cache_7x188; 77 | gsize cache_size; 78 | 79 | /* gop size */ 80 | guint force_key_count; /* downstream force key unit count */ 81 | GstClockTime segment_timestamp; /* segment timestamp */ 82 | GstClockTime segment_duration; /* force key interval */ 83 | 84 | /* m3u8 playlist */ 85 | gboolean has_video; 86 | gboolean has_audio_only; 87 | gboolean has_tssegment; 88 | gboolean has_m3u8_output; 89 | gboolean is_first_key; 90 | struct sockaddr_un msg_sock_addr; 91 | gint msg_sock; 92 | GstClockTime last_video_buffer_pts; 93 | GstClockTime last_running_time; 94 | GstClockTime last_segment_duration; 95 | }; 96 | 97 | struct _EncoderClass { 98 | GObjectClass parent; 99 | }; 100 | 101 | #define TYPE_ENCODER (encoder_get_type()) 102 | #define ENCODER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_ENCODER, Encoder)) 103 | #define ENCODER_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), TYPE_ENCODER, EncoderClass)) 104 | #define IS_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_ENCODER)) 105 | #define IS_ENCODER_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE ((cls), TYPE_ENCODER)) 106 | #define ENCODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_ENCODER, EncoderClass)) 107 | #define encoder_new(...) (g_object_new(TYPE_ENCODER, ## __VA_ARGS__, NULL)) 108 | 109 | GType encoder_get_type (void); 110 | 111 | guint encoder_initialize (GArray *earray, gchar *job, EncoderOutput *encoders, Source *source); 112 | gboolean is_encoder_output_ready (EncoderOutput *encoder_output); 113 | GstClockTime encoder_output_rap_timestamp (EncoderOutput *encoder_output, guint64 rap_addr); 114 | guint64 encoder_output_gop_seek (EncoderOutput *encoder_output, GstClockTime timestamp); 115 | guint64 encoder_output_gop_size (EncoderOutput *encoder_output, guint64 rap_addr); 116 | 117 | #endif /* __ENCODER_H__ */ 118 | -------------------------------------------------------------------------------- /examples/mpeg2_1080p_ac3.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "bgctv", 3 | "debug" : "gstreamill:4", 4 | "source" : { 5 | "elements" : { 6 | "udpsrc" : { 7 | "property" : { 8 | "uri" : "udp://225.0.0.14:10000" 9 | } 10 | }, 11 | "tsdemux" : { 12 | "property" : { 13 | "program-number" : 619 14 | } 15 | }, 16 | "appsink" : { 17 | "property" : { 18 | "sync" : false, 19 | "drop" : true 20 | } 21 | } 22 | }, 23 | "bins" : [ 24 | "udpsrc ! queue ! tsdemux name=demuxer", 25 | "demuxer.video ! queue ! mpegvideoparse ! queue ! mpeg2dec ! queue ! appsink name = video", 26 | "demuxer.audio ! ac3parse ! queue ! avdec_ac3 ! queue ! appsink name = audio" 27 | ] 28 | }, 29 | "encoders" : [ 30 | { 31 | "elements" : { 32 | "appsrc" : { 33 | "property" : { 34 | "is-live" : true, 35 | "format" : 3 36 | } 37 | }, 38 | "x264enc" : { 39 | "property" : { 40 | "name" : "x264enc", 41 | "bitrate" : 2500, 42 | "bframes" : 3, 43 | "byte-stream" : "TRUE", 44 | "rc-lookahead" : 25, 45 | "key-int-max" : 100, 46 | "pass" : 0, 47 | "mb-tree" : false, 48 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:b-pyramid=2:direct=spatial" 49 | } 50 | }, 51 | "voaacenc": { 52 | "property": { 53 | "bitrate": 64000 54 | } 55 | }, 56 | "appsink": { 57 | "property": { 58 | "sync": false 59 | } 60 | } 61 | }, 62 | "bins" : [ 63 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc0 ! queue ! aacparse ! queue ! muxer.", 64 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 65 | "mpegtsmux name=muxer ! queue ! appsink" 66 | ] 67 | }, 68 | { 69 | "elements" : { 70 | "appsrc" : { 71 | "property" : { 72 | "is-live" : true, 73 | "format" : 3 74 | } 75 | }, 76 | "videoscale" : { 77 | "caps" : "video/x-raw-yuv,width=1280,height=720" 78 | }, 79 | "x264enc" : { 80 | "property" : { 81 | "name" : "x264enc", 82 | "bitrate" : 2000, 83 | "bframes" : 3, 84 | "byte-stream" : "TRUE", 85 | "rc-lookahead" : 25, 86 | "key-int-max" : 100, 87 | "pass" : 17, 88 | "mb-tree" : true, 89 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:b-pyramid=2:direct=spatial" 90 | } 91 | }, 92 | "voaacenc": { 93 | "property": { 94 | "bitrate": 64000 95 | } 96 | }, 97 | "appsink": { 98 | "property": { 99 | "sync": false 100 | } 101 | } 102 | }, 103 | "bins" : [ 104 | /*"appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc ! queue ! aacparse ! queue ! muxer.",*/ 105 | "appsrc name=video ! queue ! videoconvert ! queue ! x264enc ! queue ! muxer.", 106 | "mpegtsmux name=muxer ! queue ! appsink" 107 | ] 108 | } 109 | ], 110 | "m3u8streaming": { 111 | "version" : 3, 112 | "window-size": 10, 113 | "segment-duration": 3 114 | } 115 | } 116 | 117 | -------------------------------------------------------------------------------- /examples/transcoder.264.aac.output.m3u8.job: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "transcode", 3 | "debug" : "4", 4 | "is-live" : false, 5 | "log-path" : "/home/zhangping/tmp/cctv2", 6 | "source" : { 7 | "elements" : { 8 | "filesrc" : { 9 | "property" : { 10 | "location" : "/home/zhangping/cctv2.ts" 11 | } 12 | }, 13 | "tsdemux" : { 14 | "property" : { 15 | "program-number" : 302 16 | } 17 | }, 18 | "appsink" : { 19 | "property" : { 20 | "sync" : false, 21 | "drop" : true 22 | } 23 | } 24 | }, 25 | "bins" : [ 26 | "filesrc ! queue ! tsdemux name=demuxer", 27 | "demuxer.video ! queue ! mpeg2dec ! queue ! appsink name = video", 28 | "demuxer.audio ! mpegaudioparse ! queue ! mad ! queue ! appsink name = audio" 29 | ] 30 | }, 31 | "encoders" : [ 32 | { 33 | "elements" : { 34 | "appsrc" : { 35 | "property" : { 36 | "is-live" : true, 37 | "format" : 3 38 | } 39 | }, 40 | "x264enc" : { 41 | "property" : { 42 | "name" : "x264enc", 43 | "bitrate" : 1500, 44 | "bframes" : 3, 45 | "threads" : 4, 46 | "byte-stream" : "TRUE", 47 | "rc-lookahead" : 25, 48 | "key-int-max" : 100, 49 | "pass" : 17, 50 | "mb-tree" : true, 51 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:b-pyramid=2:direct=spatial" 52 | } 53 | }, 54 | "hlssink" : { 55 | "property" : { 56 | "playlist-location" : "/home/zhangping/tmp/cctv2/800/playlist.m3u8", 57 | "location" : "/home/zhangping/tmp/cctv2/800/segment%05d.ts", 58 | "max-files" : 1000, 59 | "playlist-length" : 1000, 60 | "target-duration" : 10 61 | } 62 | } 63 | }, 64 | "bins" : [ 65 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc0! queue ! muxer.", 66 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 67 | "mpegtsmux name=muxer ! queue ! hlssink" 68 | ] 69 | }, 70 | { 71 | "elements" : { 72 | "appsrc" : { 73 | "property" : { 74 | "is-live" : true, 75 | "format" : 3 76 | } 77 | }, 78 | "x264enc" : { 79 | "property" : { 80 | "name" : "x264enc", 81 | "bitrate" : 1000, 82 | "bframes" : 3, 83 | "threads" : 4, 84 | "byte-stream" : "TRUE", 85 | "rc-lookahead" : 25, 86 | "key-int-max" : 100, 87 | "pass" : 17, 88 | "mb-tree" : true, 89 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:b-pyramid=2:direct=spatial" 90 | } 91 | }, 92 | "hlssink" : { 93 | "property" : { 94 | "playlist-location" : "/home/zhangping/tmp/cctv2/1000/playlist.m3u8", 95 | "location" : "/home/zhangping/tmp/cctv2/1000/segment%05d.ts", 96 | "max-files" : 1000, 97 | "playlist-length" : 1000, 98 | "target-duration" : 10 99 | } 100 | } 101 | }, 102 | "bins" : [ 103 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc1 ! queue ! muxer.", 104 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 105 | "mpegtsmux name=muxer ! queue ! hlssink" 106 | ] 107 | } 108 | ] 109 | } 110 | 111 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/system.html: -------------------------------------------------------------------------------- 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 | 103 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | releases are sorted from youngest to oldest. 2 | 3 | v-1.0.0 2019-04-14 4 | - generate segment ahead clock time bug fixed 5 | 6 | v-1.0.0-rc6 2018-09-13 7 | - #53 if ((size == -1) && (errno == EAGAIN)) segment fault fixed 8 | 9 | v-1.0.0-rc5 2018-08-06 10 | - #53 lock job mutex when reset job 11 | 12 | v-1.0.0-rc4 2018-07-31 13 | - #53 lock sempore when clean_job_list 14 | 15 | v-1.0.0-rc3 2018-07-30 16 | - #108 check fopen return 17 | - #109 check mmap return 18 | 19 | v-1.0.0-rc2 2018-03-15 20 | - add signal number to log message when subprocess terminated by signal 21 | 22 | v-1.0.0-rc1 2018 23 | - log rotate bug across year 24 | - timeshift exceed live bug fix 25 | - bandwidth for multi audio tracks 26 | 27 | v-0.9.2 2017-10-20 28 | - stream_id 0xea cause tssegment failure bug #103 fix 29 | 30 | v-0.9.1 2017-06-26 31 | - removing dvr directory bug fix 32 | 33 | v-0.9.0 2017-06-03 34 | - synchronization of master and slave encoder 35 | - codec and bandwidth parameter in m3u8 master playlist 36 | - request time in access log 37 | - memory leak bug fix 38 | - removing dvr directory in a seperate thread 39 | 40 | v-0.8.7 2017-04-13 41 | - #97 memory leak bug fix 42 | 43 | v-0.8.6 2017-04-09 44 | - gstreamill -s deadlock bugfix 45 | - #97 memory leak bug fix 46 | 47 | v-0.8.5 2017-02-24 48 | - log file size 40M 49 | - remove useless log message 50 | 51 | v-0.8.4 2017-02-21 52 | - add web style access log file 53 | - use live playlist window size as time shift playlist window size 54 | - time shift position is last segment in time shift playlist 55 | - update parson 56 | - calculate segment duration use buffer pts if no fix_frame_rate eq 0 57 | - dvr download 58 | - position parameter, absolute time shift 59 | 60 | v-0.8.3 61 | - check first slice of a picture 62 | - check fps_num zero 63 | - gstreamill -s bug fix 64 | - semaphore deadlock problem 65 | 66 | v-0.8.2 67 | - check uri 68 | - h.265 segment support 69 | - record segment in another thread, avoid send segment message fail 70 | 71 | v-0.8.1 72 | - tssegment can't segment sichuan local channels 73 | 74 | v-0.8.0 75 | - version 0.8.0 release 76 | 77 | v-0.8-rc2 78 | - segment only process start failure on gstreamer 1.8.1 fix 79 | 80 | v-0.8-RC1 81 | - More meaningful log message 82 | - Calculate segment duration use frame accumulate 83 | - Stop job failure fix 84 | 85 | v-0.7.12 2016-01-26 86 | - Add DVR directory managment API 87 | - Deny streaming service bug fix, #76 88 | - Check callback parameters: start & end 89 | 90 | v-0.7.11 2015-12-14 91 | - endless loop problem fix: 21bb0df 92 | - remove x264_2pass.log.temp and x264_2pass.log.mbtree.temp file: 892966f5c 93 | - decodebin source example: a91a5f2a0 94 | - response 501 for not impletmented method, for example HEAD 95 | - start job failure bug, due to job semaphore 96 | 97 | v-0.7.10 2015-11-20 98 | - segment fault bug fix 99 | 100 | v-0.7.9 2015-11-16 101 | - zero size segment 102 | 103 | v-0.7.8 2015-11-11 104 | - log rotate in debug mode 105 | - main process log to file in debug mode 106 | 107 | v-0.7.7 2015-11-09 108 | - unref job problem 109 | - access job->output->stat in child_watch_cb, cause main crash problem 110 | - -j mode, 0 size segment problem 111 | 112 | v-0.7.6 2015-11-05 113 | - increase precision of cpu usage in job stat 114 | - add gstreamill cpu usage in gstreamill stat 115 | 116 | v-0.7.5 2015-11-02 117 | - segment only bandwidth parameter in master m3u8 playlist 118 | - add -d command line option, run in foreground for docker compliance 119 | 120 | v-0.7.4 2015-10-08 121 | - multi bitrate support 122 | 123 | v-0.7.3 2015-09-24 124 | - crash bug fix, #53 125 | - subprocess restart make mistake dvr sequence, #71 126 | 127 | v-0.7.2 2015-08-18 128 | - callback parameters buf fix 129 | 130 | v-0.7.1 2015-08-14 131 | - dvr bug fix 132 | 133 | v-0.7.0 2015-08-04 134 | - add tssegment element 135 | - timeshift parameter: timeshift=nnnn 136 | - callback parameter: start=yyyymmddhhmmss&end=yyyymmddhhmmss 137 | - segment store path: /path/to/dvr/channel_name/encoder_id/yyyymmddhh/ 138 | - live hls segment name: yyyymmddhh/us_sequenc_duration.ts 139 | - Dockerfile for container image 140 | - update parson 141 | 142 | v-0.6.3 2015-03-04 143 | - bugfix #51 144 | 145 | v-0.6.2 2015-02-01 146 | - use autoreconf instead gnome-autogen.sh. 147 | - installation document. 148 | 149 | v-0.6.1 2015-01-26 150 | - audio only support. 151 | - vitamio on android player problem at meilisishui.com fix. 152 | 153 | v-0.6.0 2015-01-14 154 | - setuid gstreamill, don't run under root now. 155 | - complete media upload, support large media. 156 | - bugfix #11, #12, #30, #31, #33, #35, #36, #38. 157 | - running on CentOS7. 158 | - catch SIGINT and SIGQUIT. 159 | - use unix domain socket instead mqueue #45. 160 | 161 | v-0.5.0 2014-09-17 zhangping 162 | - B/S Managment 163 | - Transcode support 164 | 165 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/setnetwork.html: -------------------------------------------------------------------------------- 1 | 2 |
    3 |
    4 |
    5 |
    6 |
    7 |
    8 |
    9 |
    10 |
    11 |
    12 | 13 | 14 | 113 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/locales/zh_CN/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "nav": { 3 | "System": "系统", 4 | "System Manage": "系统管理", 5 | "Gstreamill Manage": "Gstreamill管理", 6 | "Set Network": "网络设置", 7 | "Jobs": "任务", 8 | "Media": "媒体", 9 | "New Job": "创建任务", 10 | "New Live Job": "创建直播任务", 11 | "New Transcode Job": "创建转码任务", 12 | "job management": "任务管理", 13 | "job status": "任务状态", 14 | "job delete": "任务删除", 15 | "Media": "媒体" 16 | }, 17 | "gstreamill": { 18 | "Gstreamill Status": "Gstreamill 状态", 19 | "Gstreamill Version": "Gstreamill 版本", 20 | "Compile Date": "编译日期", 21 | "Compile Time": "编译时间", 22 | "Start Time": "启动时间", 23 | "Jobs Count" : "任务个数", 24 | "Set Gstreamill": "设置Gstreamill", 25 | "Language": "语言", 26 | "Transcode Directory": "转码目录", 27 | "Set Gstreamill Success": "设置Gstreamill成功", 28 | "Set Gstreamill Failure": "设置Gstreamill失败" 29 | }, 30 | "system": { 31 | "System Manage": "系统管理", 32 | "Danger Zone": "危险区", 33 | "Restart Device": "重启设备", 34 | "Restart OS, all jobs will stoped": "重启操作系统, 所有任务会停止", 35 | "Set Network Interfaces": "设置网络接口", 36 | "System Status": "System状态", 37 | "CPU Model": "CPU型号", 38 | "CPU Count": "CPU个数", 39 | "Threads per Core": "每核心线程数", 40 | "Core per Socket": "每CPU核心数", 41 | "Socket Count": "CPU个数", 42 | "CPU MHz": "CPU频率", 43 | "Really restart device": "当真要重新启动设备" 44 | }, 45 | "setnetwork": { 46 | "Network Configure": "网络配置", 47 | "Save Network Configure": "存储网络配置", 48 | "method": "接入方法", 49 | "address": "IP地址", 50 | "netmask": "子网掩码", 51 | "network": "网络", 52 | "broadcast": "广播域", 53 | "gateway": "网关", 54 | "No Change": "无变化", 55 | "Set Network Success": "设置网络成功", 56 | "Set Network Failure": "设置网络失败" 57 | }, 58 | "newtranscodejob": { 59 | "New Transcode Job": "创建转码任务", 60 | "Select Encoder Output": "选择编码输出", 61 | "Select Encapsulation Type": "选择封装格式", 62 | "Select Encoder Output Type": "选择编码输出类型", 63 | "Phone": "手机", 64 | "Pad": "平板电脑", 65 | "TV": "电视", 66 | "HDTV": "高清电视", 67 | "Cancel": "取消", 68 | "OK": "确定", 69 | "Select Media Files Please": "请选择媒体文件" 70 | }, 71 | "newlivejob": { 72 | "New Live Job": "创建直播编码任务", 73 | "Job Name": "任务名称", 74 | "Source Type": "源类型", 75 | "Video Capture Device": "视频采集设备", 76 | "Audio Capture Device": "音频采集设备", 77 | "Source Uri": "源地址", 78 | "Program Number": "节目号", 79 | "Encoder Type": "编码类型", 80 | "Phone": "手机", 81 | "SDTV": "标清电视", 82 | "HDTV": "高清电视", 83 | "Output Type": "输出类型", 84 | "New Live Job Success": "创建直播任务成功", 85 | "New Live Job Failure": "创建直播任务失败", 86 | "Select Source Type": "选择源类型", 87 | "Set Name Please": "请输入名字" 88 | }, 89 | "jobmanage": { 90 | "Job Setting": "任务设置", 91 | "Save Job": "存储任务", 92 | "Video Resolution": "视频分辨率", 93 | "Video Capture Width": "视频采集宽度", 94 | "Video Capture Height": "视频采集高度", 95 | "Video Bitrate": "视频码率", 96 | "UDP Output": "UDP输出", 97 | "Window Size": "窗口尺寸", 98 | "Segment Duration": "切片时长", 99 | "DVR Duration": "DVR时长", 100 | "Source Uri": "源 Uri", 101 | "Program Number": "节目号", 102 | "Video Capture Device": "视频采集设备", 103 | "Video Capture Resolution": "视频采集分辨率", 104 | "Audio Capture Device": "音频采集设备", 105 | "Danger Zone": "危险区", 106 | "Remove this Job": "移除这个任务", 107 | "Realy remove this Job? can't be restore!": "真的要移除任务? 移除后无法恢复!", 108 | "Stop and remove a running Job? can't be restore!": "停止并删除一个运行中的任务?移除后无法恢复!", 109 | "Stop and Remove Job": "停止并移除任务", 110 | "Remove Job": "移除任务", 111 | "Get Job Failure": "获取任务失败", 112 | "Set Job Success": "设置任务成功", 113 | "Set Job Failure": "设置任务失败", 114 | "Get Job Stat Failure": "获取任务状态失败", 115 | "Stop Job Failure": "停止任务失败", 116 | "Remove Job Failure": "移除任务失败", 117 | "Remove Job Success": "移除任务成功" 118 | }, 119 | "jobstatus": { 120 | "Job Status": "Job状态", 121 | "Restart Times": "重新启动次数", 122 | "Last Restart Time": "最后一次启动时间", 123 | "Current Concurrent Count": "当前播放并发数", 124 | "Average CPU Usage": "平均CPU使用", 125 | "Current CPU Usage": "当前CPU使用", 126 | "Memory Usage": "内存使用", 127 | "Sync Error Times": "同步错误次数", 128 | "Streams Count": "流个数", 129 | "Name": "名字", 130 | "Last Time Stamps": "时间戳", 131 | "Last Heartbeat Time": "心跳时间", 132 | "Output Heartbeat": "输出心跳", 133 | "Total Output Bytes": "输出字节数" 134 | }, 135 | "joblist": { 136 | "Job List": "任务列表", 137 | "New Live Job": "创建直播任务", 138 | "New Transcode Job": "创建转码任务", 139 | "Restart times": "重新启动次数", 140 | "Start Time": "启动时间", 141 | "Last time started": "最后一次启动时间", 142 | "Current Position / Duration": "当前位置 / 影片时长", 143 | "Stop Job": "停止任务", 144 | "Encoder counts": "编码数", 145 | "Average CPU usage": "平均CPU使用", 146 | "Memory usage": "内存使用", 147 | "Start Job": "启动任务", 148 | "The job is not started": "任务未启动", 149 | "Cancel Job": "取消任务", 150 | "Get Job Failure": "获取任务失败", 151 | "Start Job Success": "启动任务成功", 152 | "Start Job Failure": "启动任务失败", 153 | "Stop Job Success": "停止任务成功", 154 | "Stop Job Failure": "停止任务失败", 155 | "Cancel Job Success": "取消任务成功", 156 | "Cancel Job Failure": "取消任务失败", 157 | "Realy cancel the job": "真的要取消任务" 158 | }, 159 | "bootbox": { 160 | "OK": "OK", 161 | "CANCEL": "取消", 162 | "CONFIRM": "确定" 163 | }, 164 | "mediaman": { 165 | "Media Managment": "媒体管理", 166 | "Remove Media": "删除媒体", 167 | "Media Download": "媒体下载", 168 | "Media Upload": "媒体上载", 169 | "Select Media Files Please": "请选择媒体文件" 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/mediaman.html: -------------------------------------------------------------------------------- 1 | 2 |
    3 |
    4 |
    5 |
    6 |
    7 | 8 | 9 | 10 | 11 | 12 |
    13 |

    Transcode in

    14 |
      15 |
        16 |

        Transcode out

        17 |
          18 | 19 |
          20 |
          21 |
          22 |
          23 | 24 | 25 | 119 | -------------------------------------------------------------------------------- /docs/usage.rst: -------------------------------------------------------------------------------- 1 | Using Gstreamill 2 | **************** 3 | 4 | Prerequisites 5 | ============= 6 | 7 | Install following package before using gstreamill:: 8 | 9 | sudo apt-get install gstreamer1.0-plugins-ugly gstreamer1.0-plugins-bad gstreamer1.0-plugins-good gstreamer1.0-plugins-base 10 | 11 | Command line 12 | ============ 13 | 14 | gstreamill 15 | ---------- 16 | 17 | **gstreamill -h** 18 | 19 | Print gstreamill help message:: 20 | 21 | $ gstreamill -h 22 | Usage: 23 | gstreamill [OPTION...] 24 | 25 | Help Options: 26 | -h, --help Show help options 27 | --help-all Show all help options 28 | --help-gst Show GStreamer Options 29 | 30 | Application Options: 31 | -j, --job -j /full/path/to/job.file: Specify a job file, full path is must. 32 | -l, --log -l /full/path/to/log: Specify log path, full path is must. 33 | -m, --httpmgmt -m http managment address, default is 0.0.0.0:20118. 34 | -a, --httpstreaming -a http streaming address, default is 0.0.0.0:20119. 35 | -s, --stop Stop gstreamill. 36 | -v, --version display version information and exit. 37 | 38 | **gstreamill -j** 39 | 40 | Debug mode, run in foreground, it is useful for job description debug:: 41 | 42 | $gstreamill -j examples/test.job 43 | 0:00:00.020376536 10938 0x1fb8e40 WARN gstreamill main.c:444:main: gstreamill started ... 44 | 0:00:00.024810273 10938 0x1fb8e40 ERROR gstreamill gstreamill.c:849:gstreamill_job_start: live job arrived: 45 | /* 46 | * test job, videotestsrc and audiotestsrc 47 | */ 48 | { 49 | "name" : "test", 50 | "debug" : "gstreamill:3", 51 | "source" : { 52 | 53 | ... ... ... 54 | 55 | "window-size" : 10, 56 | "segment-duration" : 3.00 57 | } 58 | } 59 | 0:00:00.025468997 10938 0x1fb8e40 WARN gstreamill job.c:304:job_initialize: lock output semaphore for initializing 60 | 0:00:00.025502887 10938 0x1fb8e40 WARN gstreamill job.c:306:job_initialize: locked output semaphore 61 | 0:00:00.025815092 10938 0x1fb8e40 WARN gstreamill job.c:390:job_initialize: initializing complete, unlock output semaphore 62 | 0:00:00.043864714 10938 0x202b140 WARN default gstvideopool.c:171:video_buffer_pool_set_config: no caps in config 63 | 0:00:00.046601404 10938 0x1fb8e40 WARN gstreamill job.c:807:job_start: Set source pipeline to play state ok 64 | 0:00:00.047702652 10938 0x1fb8e40 WARN gstreamill job.c:830:job_start: Set encoder encoder.0 to play state ok 65 | 0:00:00.047720656 10938 0x1fb8e40 WARN gstreamill job.c:833:job_start: Set job test to play state ok 66 | 0:00:00.047732718 10938 0x1fb8e40 WARN gstreamill main.c:493:main: start job result: success. 67 | 68 | Use as upstart job on Ubuntu 14.04 69 | --------------------------- 70 | 71 | **Start gstreamill** 72 | 73 | *sudo service gstreamill start* 74 | 75 | **Stop gstreamill** 76 | 77 | *sudo service gstreamill stop* 78 | 79 | **Restart gstreamill** 80 | 81 | *sudo service gstreamill restart* 82 | 83 | Accessing gstreamill output 84 | =========================== 85 | 86 | **http progressive streaming** 87 | 88 | *http://gstreamill.server.addr:20119/job-name/encoder/encoder_index* 89 | 90 | **hls** 91 | 92 | *http://host.name.or.ip:20119/job name/playlist.m3u8* 93 | 94 | *http://host.name.or.ip:20119/job name/encoder/encoder_index/playlist.m3u8* 95 | 96 | **hls time shift** 97 | 98 | *http://gstreamill.server.addr:20119/job_name/playlist.m3u8?timeshift=3600* 99 | *http://gstreamill.server.addr:20119/job_name/playlist.m3u8?timeshift=1486556428(Wed Feb 8 20:20:28 CST 2017)* 100 | 101 | **hls catchup** 102 | 103 | *http://gstreamill.server.addr:20119/job_name/playlist.m3u8?start=20150606060600&end=20150606070600* 104 | 105 | **udp** 106 | 107 | *udp://@ip:port* 108 | 109 | **dvr download** 110 | *http://gstreamill.server.addr:20119/job_name/encoder/0?start=20150606060600&end=20150606070600* 111 | 112 | Use Gstreamill through web managment 113 | ==================================== 114 | 115 | **Default web managment url** 116 | 117 | *http://gstreamill.ip:20118/admin/* 118 | 119 | New testsrc Job 120 | --------------- 121 | 122 | New network source Job 123 | ---------------------- 124 | 125 | New v4l2src Job 126 | --------------- 127 | 128 | Step 1: Click Jobs menu item 129 | 130 | .. image:: _static/jobs.png 131 | 132 | Step 2: Click New Live Job Button 133 | 134 | .. image:: _static/newlivejob.png 135 | 136 | Step 3: Input Job name 137 | 138 | .. image:: _static/newlivejob.v4l2-1.png 139 | 140 | Step 4: Select source type 141 | 142 | .. image:: _static/newlivejob.v4l2-2.png 143 | 144 | Step 5: Select Video capture device 145 | 146 | .. image:: _static/newlivejob.v4l2-3.png 147 | 148 | Setp 6: Select Audio Capture device 149 | 150 | .. image:: _static/newlivejob.v4l2-4.png 151 | 152 | Step 7: Select Encoder type 153 | 154 | .. image:: _static/newlivejob.v4l2-5.png 155 | 156 | Step 8: Select Output type 157 | 158 | .. image:: _static/newlivejob.v4l2-6.png 159 | 160 | Step 9: Click New Live Job Button 161 | 162 | .. image:: _static/newlivejob.v4l2-7.png 163 | 164 | Step 10: New Live Job success, click ok button 165 | 166 | .. image:: _static/newlivejob.v4l2-8.png 167 | 168 | Step 11: Jobs list 169 | 170 | .. image:: _static/newlivejob.v4l2-9.png 171 | -------------------------------------------------------------------------------- /usr/share/gstreamill/admin/gstreamill.html: -------------------------------------------------------------------------------- 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 | 55 |
          56 |
          57 | 58 |
          59 |
          60 |
          61 |
          62 |
          63 | 64 | 65 | 119 | -------------------------------------------------------------------------------- /src/tssegment.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MPEGTS Segment Element 3 | * 4 | * Mostly copy from /gst-plugins-bad/gst/mpegtsdemux 5 | */ 6 | 7 | #ifndef __TSSEGMENT_H__ 8 | #define __TSSEGMENT_H__ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define MPEGTS_NORMAL_PACKETSIZE 188 17 | #define MPEGTS_M2TS_PACKETSIZE 192 18 | #define CONTINUITY_UNSET 255 19 | #define VERSION_NUMBER_UNSET 255 20 | #define TABLE_ID_UNSET 0xFF 21 | #define PACKET_SYNC_BYTE 0x47 22 | #define MPEGTS_MIN_PACKETSIZE MPEGTS_NORMAL_PACKETSIZE 23 | #define MPEGTS_MAX_PACKETSIZE 208 24 | 25 | #define MPEGTS_BIT_SET(field, offs) ((field)[(offs) >> 3] |= (1 << ((offs) & 0x7))) 26 | #define MPEGTS_BIT_IS_SET(field, offs) ((field)[(offs) >> 3] & (1 << ((offs) & 0x7))) 27 | #define MPEGTS_BIT_UNSET(field, offs) ((field)[(offs) >> 3] &= ~(1 << ((offs) & 0x7))) 28 | #define FLAGS_CONTINUITY_COUNTER(f) (f & 0x0f) 29 | #define FLAGS_HAS_AFC(f) (f & 0x20) 30 | #define FLAGS_HAS_PAYLOAD(f) (f & 0x10) 31 | #define MPEGTS_AFC_PCR_FLAG 0x10 32 | #define MAX_MPEGTIME 8589934592 33 | 34 | typedef struct { 35 | gint16 pid; 36 | guint8 payload_unit_start_indicator; 37 | guint8 scram_afc_cc; 38 | const guint8 *payload; 39 | const guint8 *data_start; 40 | const guint8 *data_end; 41 | const guint8 *data; 42 | guint8 afc_flags; 43 | guint64 offset; 44 | } TSPacket; 45 | 46 | typedef enum { 47 | PES_FIELD_ID_TOP_ONLY = 0x00, /* Display from top field only */ 48 | PES_FIELD_ID_BOTTOM_ONLY = 0x01, /* Display from bottom field only */ 49 | PES_FIELD_ID_COMPLETE_FRAME = 0x10, /* Display complete frame */ 50 | PES_FIELD_ID_INVALID = 0x11 /* Reserved/Invalid */ 51 | } PESFieldID; 52 | 53 | typedef enum { 54 | PES_FLAG_PRIORITY = 1 << 3, /* PES_priority (present: high-priority) */ 55 | PES_FLAG_DATA_ALIGNMENT = 1 << 2, /* data_alignment_indicator */ 56 | PES_FLAG_COPYRIGHT = 1 << 1, /* copyright */ 57 | PES_FLAG_ORIGINAL_OR_COPY = 1 << 0 /* original_or_copy */ 58 | } PESHeaderFlags; 59 | 60 | typedef enum { 61 | PES_PARSING_OK = 0, /* Header fully parsed and valid */ 62 | PES_PARSING_BAD = 1, /* Header invalid (CRC error for ex) */ 63 | PES_PARSING_NEED_MORE = 2 /* Not enough data to parse header */ 64 | } PESParsingResult; 65 | 66 | typedef enum { 67 | NALU_DELIMITER = 2, 68 | NALU_SEI = 4, 69 | NALU_VPS = 8, 70 | NALU_SPS = 16, 71 | NALU_PPS = 32, 72 | NALU_PIC = 64, 73 | NALU_FRAME = 128, 74 | NALU_IDR = 256 75 | } NaluParsingResult; 76 | 77 | typedef struct { 78 | guint8 stream_id; /* See ID_* above */ 79 | guint32 packet_length; /* The size of the PES header and PES data (if 0 => unbounded packet) */ 80 | guint64 PTS; /* PTS (-1 if not present or invalid) */ 81 | guint64 DTS; /* DTS (-1 if not present or invalid) */ 82 | } PESHeader; 83 | 84 | /* MPEG_TO_GST calculation requires at least 17 extra bits (100000) 85 | * Since maximum PTS/DTS value is coded with 33bits, we are 86 | * safe to use direct calculation (17+33 < 63) */ 87 | #define MPEGTIME_TO_GSTTIME(t) ((t) * (guint64)100000 / 9) 88 | 89 | typedef struct _TsSegment { 90 | GObject parent; 91 | 92 | GstElement element; 93 | GstPad *sinkpad, *srcpad; 94 | 95 | gint64 bitrate; 96 | guint program_number; 97 | guint16 pmt_pid; 98 | const GstMpegtsPMT *pmt; 99 | /* arrays that say whether a pid is a known psi pid or a pes pid */ 100 | /* Use MPEGTS_BIT_* to set/unset/check the values */ 101 | guint8 *known_psi; 102 | /* Whether we saw a PAT yet */ 103 | gboolean seen_pat; 104 | gboolean seen_pmt; 105 | guint16 video_pid; 106 | guint8 video_cc; 107 | guint8 video_stream_type; 108 | /* Reference offset */ 109 | GPtrArray *pat; 110 | guint8 *data; 111 | /* Amount of bytes in current ->data */ 112 | guint current_size; 113 | /* Size of ->data */ 114 | guint allocated_size; 115 | 116 | /* current offset of the tip of the adapter */ 117 | GstAdapter *adapter; 118 | guint64 offset; 119 | guint16 packet_size; 120 | const guint8 *map_data; 121 | gsize map_offset; 122 | gsize map_size; 123 | gboolean need_sync; 124 | 125 | PESHeader pes_header; 126 | /* Current PTS for the stream (in running time) */ 127 | GstClockTime PTS; 128 | GstClockTime pre_pts; 129 | /* Current DTS for the stream (in running time) */ 130 | GstClockTime DTS; 131 | gsize pes_packet_size, pes_payload_size; 132 | guint8 *pes_packet, *pes_payload; 133 | GstClockTime pes_packet_duration; 134 | 135 | GstH264NalParser *h264parser; 136 | GstH264SPS sps; 137 | GstH264SEIMessage sei; 138 | GstH264SliceHdr slice_hdr, pre_slice_hdr; 139 | GstH264NalUnit h264_nalu, h264_pre_nalu; 140 | guint field_pic_flag; 141 | guint pic_struct; 142 | 143 | GstH265Parser *h265parser; 144 | GstH265NalUnit h265_nalu, h265_pre_nalu; 145 | 146 | gint fps_num; 147 | gint fps_den; 148 | GstClockTime frame_duration; 149 | GstClockTime frames_accumulate; 150 | /* whether we saw a idr */ 151 | gboolean seen_idr; 152 | 153 | GstTagList *tag; 154 | } TsSegment; 155 | 156 | typedef struct _TsSegmentClass { 157 | GstElementClass parent_class; 158 | } TsSegmentClass; 159 | 160 | #define TYPE_TS_SEGMENT (ts_segment_get_type()) 161 | #define TS_SEGMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),TYPE_TS_SEGMENT,TsSegment)) 162 | #define TS_SEGMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),TYPE_TS_SEGMENT,TsSegmentClass)) 163 | #define IS_TS_SEGMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),TYPE_TS_SEGMENT)) 164 | #define IS_TS_SEGMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),TYPE_TS_SEGMENT)) 165 | 166 | GType ts_segment_get_type (void); 167 | 168 | gboolean ts_segment_plugin_init (GstPlugin * plugin); 169 | 170 | #endif /* __TSSEGMENT_H__ */ 171 | -------------------------------------------------------------------------------- /tools/hd2.5M.temp: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "%s", 3 | "debug" : "gstreamill:4", 4 | "source": { 5 | "elements": { 6 | "appsink": { 7 | "property": { 8 | "drop": "true", 9 | "sync": "false" 10 | } 11 | }, 12 | "udpsrc": { 13 | "property": { 14 | "uri": "%s" 15 | } 16 | } 17 | }, 18 | "bins": [ 19 | "udpsrc ! queue ! tsdemux name=demuxer", 20 | "demuxer.video ! queue ! h264parse ! queue ! avdec_h264 ! queue ! appsink name = video", 21 | "demuxer.audio ! queue ! mpegaudioparse ! queue ! mad ! queue ! appsink name = audio" 22 | ] 23 | }, 24 | "encoders" : [ 25 | { 26 | "elements" : { 27 | "appsrc" : { 28 | "property" : { 29 | "is-live" : true, 30 | "format" : 3 31 | } 32 | }, 33 | "x264enc" : { 34 | "property" : { 35 | "name" : "x264enc", 36 | "bitrate" : 2500, 37 | "bframes" : 3, 38 | "threads" : 4, 39 | "byte-stream" : "TRUE", 40 | "rc-lookahead" : 25, 41 | "key-int-max" : 100, 42 | "pass" : 17, 43 | "mb-tree" : true, 44 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:b-pyramid=2:direct=spatial" 45 | } 46 | }, 47 | "voaacenc": { 48 | "property": { 49 | "bitrate": 64000 50 | } 51 | }, 52 | "appsink": { 53 | "property": { 54 | "sync": false 55 | } 56 | } 57 | }, 58 | "bins" : [ 59 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc name=voaacenc0 ! queue ! aacparse ! queue ! muxer.", 60 | "appsrc name=video ! queue ! x264enc ! queue ! muxer.", 61 | "mpegtsmux name=muxer ! queue ! appsink" 62 | ] 63 | }, 64 | { 65 | "elements" : { 66 | "appsrc" : { 67 | "property" : { 68 | "is-live" : true, 69 | "format" : 3 70 | } 71 | }, 72 | "videoscale" : { 73 | "caps" : "video/x-raw-yuv,width=1280,height=720" 74 | }, 75 | "x264enc" : { 76 | "property" : { 77 | "name" : "x264enc", 78 | "bitrate" : 1500, 79 | "bframes" : 3, 80 | "byte-stream" : "TRUE", 81 | "rc-lookahead" : 25, 82 | "key-int-max" : 100, 83 | "pass" : 17, 84 | "mb-tree" : true, 85 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:b-pyramid=2:direct=spatial" 86 | } 87 | }, 88 | "voaacenc": { 89 | "property": { 90 | "bitrate": 64000 91 | } 92 | }, 93 | "appsink": { 94 | "property": { 95 | "sync": false 96 | } 97 | } 98 | }, 99 | "bins" : [ 100 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc ! queue ! aacparse ! queue ! muxer.", 101 | "appsrc name=video ! queue ! videoconvert ! queue ! x264enc ! queue ! muxer.", 102 | "mpegtsmux name=muxer ! queue ! appsink" 103 | ] 104 | }, 105 | { 106 | "elements" : { 107 | "appsrc" : { 108 | "property" : { 109 | "is-live" : true, 110 | "format" : 3 111 | } 112 | }, 113 | "videoscale" : { 114 | "caps" : "video/x-raw-yuv,width=720,height=576" 115 | }, 116 | "x264enc" : { 117 | "property" : { 118 | "name" : "x264enc", 119 | "bitrate" : 700, 120 | "bframes" : 3, 121 | "byte-stream" : "TRUE", 122 | "rc-lookahead" : 25, 123 | "key-int-max" : 100, 124 | "pass" : 17, 125 | "mb-tree" : true, 126 | "option-string" : ":ref=3:me=hex:subme=8:merange=16:nf=1:deblock=1,-2:weightp=1:scenecut=0:b-pyramid=2:direct=spatial" 127 | } 128 | }, 129 | "voaacenc": { 130 | "property": { 131 | "bitrate": 64000 132 | } 133 | }, 134 | "appsink": { 135 | "property": { 136 | "sync": false 137 | } 138 | } 139 | }, 140 | "bins" : [ 141 | "appsrc name=audio ! queue ! audioconvert ! queue ! voaacenc ! queue ! aacparse ! queue ! muxer.", 142 | "appsrc name=video ! queue ! videoconvert ! queue ! x264enc ! queue ! muxer.", 143 | "mpegtsmux name=muxer ! queue ! appsink" 144 | ] 145 | } 146 | ], 147 | "m3u8streaming": { 148 | "version" : 3, 149 | "window-size": 10, 150 | "segment-duration": 5.00 151 | }, 152 | "dvr_duration": 86400 153 | } 154 | 155 | --------------------------------------------------------------------------------