mirror of
https://github.com/rofafor/vdr-plugin-iptv.git
synced 2023-10-10 13:37:03 +02:00
Updated for vdr-1.7.38 and added a new CURL protocol for HTTP/HTTPS.
This commit is contained in:
parent
29bc3d1493
commit
94461d6c08
5
HISTORY
5
HISTORY
@ -188,3 +188,8 @@ VDR Plugin 'iptv' Revision History
|
|||||||
- Added support for source-specific multicasts (SSM).
|
- Added support for source-specific multicasts (SSM).
|
||||||
- Changed default external script directory from the
|
- Changed default external script directory from the
|
||||||
configuration to the resource.
|
configuration to the resource.
|
||||||
|
|
||||||
|
2013-02-24: Version 1.2.0
|
||||||
|
|
||||||
|
- Updated for vdr-1.7.38.
|
||||||
|
- Added a new CURL protocol for HTTP/HTTPS.
|
||||||
|
101
Makefile
101
Makefile
@ -3,23 +3,21 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
# Debugging on/off
|
# Debugging on/off
|
||||||
|
|
||||||
#IPTV_DEBUG = 1
|
#IPTV_DEBUG = 1
|
||||||
|
|
||||||
# Default shell for EXT protocol
|
# Default shell for EXT protocol
|
||||||
|
|
||||||
#IPTV_EXTSHELL = /bin/bash
|
#IPTV_EXTSHELL = /bin/bash
|
||||||
|
|
||||||
# Strip debug symbols? Set eg. to /bin/true if not
|
# Strip debug symbols? Set eg. to /bin/true if not
|
||||||
STRIP = strip
|
|
||||||
|
|
||||||
# Install command
|
STRIP = strip
|
||||||
INSTALL = cp --remove-destination
|
|
||||||
|
|
||||||
# The official name of this plugin.
|
# The official name of this plugin.
|
||||||
# This name will be used in the '-P...' option of VDR to load the plugin.
|
# This name will be used in the '-P...' option of VDR to load the plugin.
|
||||||
# By default the main source file also carries this name.
|
# By default the main source file also carries this name.
|
||||||
# IMPORTANT: the presence of this macro is important for the Make.config
|
|
||||||
# file. So it must be defined, even if it is not used here!
|
|
||||||
#
|
|
||||||
PLUGIN = iptv
|
PLUGIN = iptv
|
||||||
|
|
||||||
### The version number of this plugin (taken from the main source file):
|
### The version number of this plugin (taken from the main source file):
|
||||||
@ -27,40 +25,47 @@ PLUGIN = iptv
|
|||||||
VERSION = $(shell grep 'const char VERSION\[\] *=' $(PLUGIN).c | awk '{ print $$5 }' | sed -e 's/[";]//g')
|
VERSION = $(shell grep 'const char VERSION\[\] *=' $(PLUGIN).c | awk '{ print $$5 }' | sed -e 's/[";]//g')
|
||||||
GITTAG = $(shell git describe --always 2>/dev/null)
|
GITTAG = $(shell git describe --always 2>/dev/null)
|
||||||
|
|
||||||
### The C++ compiler and options:
|
|
||||||
|
|
||||||
CXX ?= g++
|
|
||||||
CXXFLAGS ?= -fPIC -g -O3 -Wall -Wextra -Wswitch-default -Wfloat-equal -Wundef -Wpointer-arith -Wconversion -Wcast-align -Wredundant-decls -Wno-unused-parameter -Werror=overloaded-virtual -Wno-parentheses
|
|
||||||
LDFLAGS ?= -Wl,--as-needed
|
|
||||||
|
|
||||||
### The directory environment:
|
### The directory environment:
|
||||||
|
|
||||||
VDRDIR ?= ../../..
|
# Use package data if installed...otherwise assume we're under the VDR source directory:
|
||||||
LIBDIR ?= ../../lib
|
PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
|
||||||
|
LIBDIR = $(call PKGCFG,libdir)
|
||||||
|
LOCDIR = $(call PKGCFG,locdir)
|
||||||
|
PLGCFG = $(call PKGCFG,plgcfg)
|
||||||
|
#
|
||||||
TMPDIR ?= /tmp
|
TMPDIR ?= /tmp
|
||||||
|
|
||||||
### Make sure that necessary options are included:
|
### The compiler options:
|
||||||
|
|
||||||
-include $(VDRDIR)/Make.global
|
export CFLAGS = $(call PKGCFG,cflags)
|
||||||
|
export CXXFLAGS = $(call PKGCFG,cxxflags)
|
||||||
|
|
||||||
|
### The version number of VDR's plugin API:
|
||||||
|
|
||||||
|
APIVERSION = $(call PKGCFG,apiversion)
|
||||||
|
|
||||||
### Allow user defined options to overwrite defaults:
|
### Allow user defined options to overwrite defaults:
|
||||||
|
|
||||||
-include $(VDRDIR)/Make.config
|
-include $(PLGCFG)
|
||||||
|
|
||||||
### The version number of VDR's plugin API (taken from VDR's "config.h"):
|
|
||||||
|
|
||||||
APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h)
|
|
||||||
|
|
||||||
### The name of the distribution archive:
|
### The name of the distribution archive:
|
||||||
|
|
||||||
ARCHIVE = $(PLUGIN)-$(VERSION)
|
ARCHIVE = $(PLUGIN)-$(VERSION)
|
||||||
PACKAGE = vdr-$(ARCHIVE)
|
PACKAGE = vdr-$(ARCHIVE)
|
||||||
|
|
||||||
|
### The name of the shared object file:
|
||||||
|
|
||||||
|
SOFILE = libvdr-$(PLUGIN).so
|
||||||
|
|
||||||
|
### Libraries
|
||||||
|
|
||||||
|
LIBS = $(shell curl-config --libs)
|
||||||
|
|
||||||
### Includes and Defines (add further entries here):
|
### Includes and Defines (add further entries here):
|
||||||
|
|
||||||
INCLUDES += -I$(VDRDIR)/include
|
INCLUDES +=
|
||||||
|
|
||||||
DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
|
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
|
||||||
|
|
||||||
ifdef IPTV_DEBUG
|
ifdef IPTV_DEBUG
|
||||||
DEFINES += -DDEBUG
|
DEFINES += -DDEBUG
|
||||||
@ -79,61 +84,67 @@ all-redirect: all
|
|||||||
|
|
||||||
### The object files (add further files here):
|
### The object files (add further files here):
|
||||||
|
|
||||||
OBJS = $(PLUGIN).o config.o setup.o device.o streamer.o protocoludp.o \
|
OBJS = $(PLUGIN).o common.o config.o device.o pidscanner.o \
|
||||||
protocolhttp.o protocolfile.o protocolext.o sectionfilter.o \
|
protocolcurl.o protocolext.o protocolfile.o protocolhttp.o \
|
||||||
sidscanner.o pidscanner.o statistics.o common.o socket.o source.o
|
protocoludp.o sectionfilter.o setup.o sidscanner.o socket.o \
|
||||||
|
source.o statistics.o streamer.o
|
||||||
|
|
||||||
### The main target:
|
### The main target:
|
||||||
|
|
||||||
all: libvdr-$(PLUGIN).so i18n
|
all: $(SOFILE) i18n
|
||||||
|
|
||||||
### Implicit rules:
|
### Implicit rules:
|
||||||
|
|
||||||
%.o: %.c Makefile
|
%.o: %.c
|
||||||
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
|
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
|
||||||
|
|
||||||
### Dependencies:
|
### Dependencies:
|
||||||
|
|
||||||
MAKEDEP = $(CXX) -MM -MG
|
MAKEDEP = $(CXX) -MM -MG
|
||||||
DEPFILE = .dependencies
|
DEPFILE = .dependencies
|
||||||
$(DEPFILE): Makefile
|
$(DEPFILE): Makefile
|
||||||
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
|
@$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
|
||||||
|
|
||||||
-include $(DEPFILE)
|
-include $(DEPFILE)
|
||||||
|
|
||||||
### Internationalization (I18N):
|
### Internationalization (I18N):
|
||||||
|
|
||||||
PODIR = po
|
PODIR = po
|
||||||
LOCALEDIR = $(VDRDIR)/locale
|
|
||||||
I18Npo = $(wildcard $(PODIR)/*.po)
|
I18Npo = $(wildcard $(PODIR)/*.po)
|
||||||
I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
|
I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file))))
|
||||||
|
I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
|
||||||
I18Npot = $(PODIR)/$(PLUGIN).pot
|
I18Npot = $(PODIR)/$(PLUGIN).pot
|
||||||
|
|
||||||
%.mo: %.po
|
%.mo: %.po
|
||||||
msgfmt -c -o $@ $<
|
msgfmt -c -o $@ $<
|
||||||
|
|
||||||
$(I18Npot): $(wildcard *.c)
|
$(I18Npot): $(wildcard *.c)
|
||||||
xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name='vdr-$(PLUGIN)' --package-version='$(VERSION)' --msgid-bugs-address='<see README>' -o $@ `ls $^`
|
xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<see README>' -o $@ `ls $^`
|
||||||
|
|
||||||
%.po: $(I18Npot)
|
%.po: $(I18Npot)
|
||||||
msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
|
msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $<
|
||||||
@touch $@
|
@touch $@
|
||||||
|
|
||||||
$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
|
$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
|
||||||
@mkdir -p $(dir $@)
|
install -D -m644 $< $@
|
||||||
cp $< $@
|
|
||||||
|
|
||||||
.PHONY: i18n
|
.PHONY: i18n
|
||||||
i18n: $(I18Nmsgs) $(I18Npot)
|
i18n: $(I18Nmo) $(I18Npot)
|
||||||
|
|
||||||
|
install-i18n: $(I18Nmsgs)
|
||||||
|
|
||||||
### Targets:
|
### Targets:
|
||||||
|
|
||||||
libvdr-$(PLUGIN).so: $(OBJS)
|
$(SOFILE): $(OBJS)
|
||||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) -o $@
|
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) $(LIBS) -o $@
|
||||||
ifndef IPTV_DEBUG
|
ifndef IPTV_DEBUG
|
||||||
@$(STRIP) $@
|
@$(STRIP) $@
|
||||||
endif
|
endif
|
||||||
@$(INSTALL) $@ $(LIBDIR)/$@.$(APIVERSION)
|
|
||||||
|
install-lib: $(SOFILE)
|
||||||
|
install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
|
||||||
|
|
||||||
|
install: install-lib install-i18n
|
||||||
|
|
||||||
dist: $(I18Npo) clean
|
dist: $(I18Npo) clean
|
||||||
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
||||||
@ -144,7 +155,9 @@ dist: $(I18Npo) clean
|
|||||||
@echo Distribution package created as $(PACKAGE).tgz
|
@echo Distribution package created as $(PACKAGE).tgz
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ $(PODIR)/*.mo $(PODIR)/*.pot
|
@-rm -f $(PODIR)/*.mo $(PODIR)/*.pot
|
||||||
|
@-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~
|
||||||
|
|
||||||
cppcheck: $(OBJS)
|
.PHONY: cppcheck
|
||||||
@cppcheck --enable=information,style,unusedFunction -v -f $(OBJS:%.o=%.c)
|
cppcheck:
|
||||||
|
@cppcheck --language=c++ --enable=all -v -f $(OBJS:%.o=%.c)
|
||||||
|
28
README
28
README
@ -17,7 +17,8 @@ See the file COPYING for more information.
|
|||||||
|
|
||||||
Requirements:
|
Requirements:
|
||||||
|
|
||||||
DVB compatible MPEG1/2 or H.264 network video streams.
|
- Libcurl - the multiprotocol file transfer library
|
||||||
|
http://curl.haxx.se/libcurl/
|
||||||
|
|
||||||
Description:
|
Description:
|
||||||
|
|
||||||
@ -91,16 +92,24 @@ Configuration:
|
|||||||
TV4;IPTV:40:S=1|P=0|F=EXT|U=iptvstream.sh|A=0:I:0:0:680:0:0:4:0:0:0
|
TV4;IPTV:40:S=1|P=0|F=EXT|U=iptvstream.sh|A=0:I:0:0:680:0:0:4:0:0:0
|
||||||
TV3;IPTV:30:S=0|P=1|F=FILE|U=/video/stream.ts|A=5:I:0:514:670:2321:0:3:0:0:0
|
TV3;IPTV:30:S=0|P=1|F=FILE|U=/video/stream.ts|A=5:I:0:514:670:2321:0:3:0:0:0
|
||||||
TV2;IPTV:20:S=0|P=1|F=HTTP|U=127.0.0.1/TS/2|A=3000:I:0:513:660:2321:0:2:0:0:0
|
TV2;IPTV:20:S=0|P=1|F=HTTP|U=127.0.0.1/TS/2|A=3000:I:0:513:660:2321:0:2:0:0:0
|
||||||
|
TV1;IPTV:10:S=1|P=0|F=CURL|U=http%3A//foo%3Abar@127.0.0.1%3A3000/TS/2|A=0:I:0:512:650:2321:0:1:0:0:0
|
||||||
TV1;IPTV:10:S=1|P=0|F=UDP|U=127.0.0.1@127.0.0.1|A=1234:I:0:512:650:2321:0:1:0:0:0
|
TV1;IPTV:10:S=1|P=0|F=UDP|U=127.0.0.1@127.0.0.1|A=1234:I:0:512:650:2321:0:1:0:0:0
|
||||||
TV1;IPTV:10:S=1|P=0|F=UDP|U=127.0.0.1|A=1234:I:0:512:650:2321:0:1:0:0:0
|
TV1;IPTV:10:S=1|P=0|F=UDP|U=127.0.0.1|A=1234:I:0:512:650:2321:0:1:0:0:0
|
||||||
^ ^ ^ ^ ^ ^ ^
|
^ ^ ^ ^ ^ ^ ^
|
||||||
| | | | | | Source type ("I")
|
| | | | | | Source type ("I")
|
||||||
| | | | | Stream parameter (multicast port
|
| | | | | Stream parameter
|
||||||
| | | | | number, HTTP port number, file delay
|
| | | | | UDP: multicast port
|
||||||
| | | | | (ms), script parameter)
|
| | | | | CURL: <<to be defined>>
|
||||||
| | | | Stream address (multicast source@group address,
|
| | | | | HTTP: HTTP port number
|
||||||
| | | | URL, file location, script location)
|
| | | | | FILE: file delay (ms)
|
||||||
| | | Stream protocol ("UDP", "HTTP", "FILE", "EXT")
|
| | | | | EXT: script parameter
|
||||||
|
| | | | Stream address
|
||||||
|
| | | | UDP: multicast [source@]group address
|
||||||
|
| | | | CURL: HTTP/HTTPS URL; colons (%3A) and pipes (%7C) shall be URL encoded
|
||||||
|
| | | | HTTP: URL; missing the scheme name part and possible port declaration
|
||||||
|
| | | | FILE: file location
|
||||||
|
| | | | EXT: script location
|
||||||
|
| | | Stream protocol ("UDP", "CURL", "HTTP", "FILE", "EXT")
|
||||||
| | Pid scanner ("0" disable, "1" enable)
|
| | Pid scanner ("0" disable, "1" enable)
|
||||||
| Section id (Sid/Nid/Tid) scanner ("0" disable, "1" enable)
|
| Section id (Sid/Nid/Tid) scanner ("0" disable, "1" enable)
|
||||||
Unique enumeration
|
Unique enumeration
|
||||||
@ -173,6 +182,11 @@ Notes:
|
|||||||
IGMP v3 protocol:
|
IGMP v3 protocol:
|
||||||
"U=<source address>@<group address>"
|
"U=<source address>@<group address>"
|
||||||
|
|
||||||
|
- The CURL implementation supports only HTTP/HTTPS protocols and an optional
|
||||||
|
netrc configuration file for authentication:
|
||||||
|
$(CONFDIR)/iptv/netrc
|
||||||
|
|
||||||
|
- CURL implementation
|
||||||
Acknowledgements:
|
Acknowledgements:
|
||||||
|
|
||||||
- The IPTV section filtering code is derived from Linux kernel.
|
- The IPTV section filtering code is derived from Linux kernel.
|
||||||
|
16
common.c
16
common.c
@ -44,14 +44,18 @@ int select_single_desc(int descriptor, const int usecs, const bool selectWrite)
|
|||||||
tv.tv_sec = 0;
|
tv.tv_sec = 0;
|
||||||
tv.tv_usec = usecs;
|
tv.tv_usec = usecs;
|
||||||
// Use select
|
// Use select
|
||||||
fd_set fds;
|
fd_set infd;
|
||||||
FD_ZERO(&fds);
|
fd_set outfd;
|
||||||
FD_SET(descriptor, &fds);
|
fd_set errfd;
|
||||||
int retval = 0;
|
FD_ZERO(&infd);
|
||||||
|
FD_ZERO(&outfd);
|
||||||
|
FD_ZERO(&errfd);
|
||||||
|
FD_SET(descriptor, &errfd);
|
||||||
if (selectWrite)
|
if (selectWrite)
|
||||||
retval = select(descriptor + 1, NULL, &fds, NULL, &tv);
|
FD_SET(descriptor, &outfd);
|
||||||
else
|
else
|
||||||
retval = select(descriptor + 1, &fds, NULL, NULL, &tv);
|
FD_SET(descriptor, &infd);
|
||||||
|
int retval = select(descriptor + 1, &infd, &outfd, &errfd, &tv);
|
||||||
// Check if error
|
// Check if error
|
||||||
ERROR_IF_RET(retval < 0, "select()", return retval);
|
ERROR_IF_RET(retval < 0, "select()", return retval);
|
||||||
return retval;
|
return retval;
|
||||||
|
2
common.h
2
common.h
@ -14,9 +14,11 @@
|
|||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#define debug(x...) dsyslog("IPTV: " x);
|
#define debug(x...) dsyslog("IPTV: " x);
|
||||||
|
#define info(x...) isyslog("IPTV: " x);
|
||||||
#define error(x...) esyslog("ERROR: " x);
|
#define error(x...) esyslog("ERROR: " x);
|
||||||
#else
|
#else
|
||||||
#define debug(x...) ;
|
#define debug(x...) ;
|
||||||
|
#define info(x...) isyslog("IPTV: " x);
|
||||||
#define error(x...) esyslog("ERROR: " x);
|
#define error(x...) esyslog("ERROR: " x);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
49
device.c
49
device.c
@ -20,6 +20,7 @@ cIptvDevice::cIptvDevice(unsigned int Index)
|
|||||||
dvrFd(-1),
|
dvrFd(-1),
|
||||||
isPacketDelivered(false),
|
isPacketDelivered(false),
|
||||||
isOpenDvr(false),
|
isOpenDvr(false),
|
||||||
|
isBuffering(true),
|
||||||
sidScanEnabled(false),
|
sidScanEnabled(false),
|
||||||
pidScanEnabled(false),
|
pidScanEnabled(false),
|
||||||
channelId(tChannelID::InvalidID)
|
channelId(tChannelID::InvalidID)
|
||||||
@ -32,6 +33,7 @@ cIptvDevice::cIptvDevice(unsigned int Index)
|
|||||||
tsBuffer->SetTimeouts(10, 10);
|
tsBuffer->SetTimeouts(10, 10);
|
||||||
ResetBuffering();
|
ResetBuffering();
|
||||||
pUdpProtocol = new cIptvProtocolUdp();
|
pUdpProtocol = new cIptvProtocolUdp();
|
||||||
|
pCurlProtocol = new cIptvProtocolCurl();
|
||||||
pHttpProtocol = new cIptvProtocolHttp();
|
pHttpProtocol = new cIptvProtocolHttp();
|
||||||
pFileProtocol = new cIptvProtocolFile();
|
pFileProtocol = new cIptvProtocolFile();
|
||||||
pExtProtocol = new cIptvProtocolExt();
|
pExtProtocol = new cIptvProtocolExt();
|
||||||
@ -63,6 +65,7 @@ cIptvDevice::~cIptvDevice()
|
|||||||
StopSectionHandler();
|
StopSectionHandler();
|
||||||
DELETE_POINTER(pIptvStreamer);
|
DELETE_POINTER(pIptvStreamer);
|
||||||
DELETE_POINTER(pUdpProtocol);
|
DELETE_POINTER(pUdpProtocol);
|
||||||
|
DELETE_POINTER(pCurlProtocol);
|
||||||
DELETE_POINTER(pHttpProtocol);
|
DELETE_POINTER(pHttpProtocol);
|
||||||
DELETE_POINTER(pFileProtocol);
|
DELETE_POINTER(pFileProtocol);
|
||||||
DELETE_POINTER(pExtProtocol);
|
DELETE_POINTER(pExtProtocol);
|
||||||
@ -141,48 +144,48 @@ cString cIptvDevice::GetFiltersInformation(void)
|
|||||||
{
|
{
|
||||||
//debug("cIptvDevice::GetFiltersInformation(%d)\n", deviceIndex);
|
//debug("cIptvDevice::GetFiltersInformation(%d)\n", deviceIndex);
|
||||||
unsigned int count = 0;
|
unsigned int count = 0;
|
||||||
cString info("Active section filters:\n");
|
cString s("Active section filters:\n");
|
||||||
// loop through active section filters
|
// loop through active section filters
|
||||||
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
||||||
if (secfilters[i]) {
|
if (secfilters[i]) {
|
||||||
info = cString::sprintf("%sFilter %d: %s Pid=0x%02X (%s)\n", *info, i,
|
s = cString::sprintf("%sFilter %d: %s Pid=0x%02X (%s)\n", *s, i,
|
||||||
*secfilters[i]->GetSectionStatistic(), secfilters[i]->GetPid(),
|
*secfilters[i]->GetSectionStatistic(), secfilters[i]->GetPid(),
|
||||||
id_pid(secfilters[i]->GetPid()));
|
id_pid(secfilters[i]->GetPid()));
|
||||||
if (++count > IPTV_STATS_ACTIVE_FILTERS_COUNT)
|
if (++count > IPTV_STATS_ACTIVE_FILTERS_COUNT)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return info;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
cString cIptvDevice::GetInformation(unsigned int Page)
|
cString cIptvDevice::GetInformation(unsigned int Page)
|
||||||
{
|
{
|
||||||
// generate information string
|
// generate information string
|
||||||
cString info;
|
cString s;
|
||||||
switch (Page) {
|
switch (Page) {
|
||||||
case IPTV_DEVICE_INFO_GENERAL:
|
case IPTV_DEVICE_INFO_GENERAL:
|
||||||
info = GetGeneralInformation();
|
s = GetGeneralInformation();
|
||||||
break;
|
break;
|
||||||
case IPTV_DEVICE_INFO_PIDS:
|
case IPTV_DEVICE_INFO_PIDS:
|
||||||
info = GetPidsInformation();
|
s = GetPidsInformation();
|
||||||
break;
|
break;
|
||||||
case IPTV_DEVICE_INFO_FILTERS:
|
case IPTV_DEVICE_INFO_FILTERS:
|
||||||
info = GetFiltersInformation();
|
s = GetFiltersInformation();
|
||||||
break;
|
break;
|
||||||
case IPTV_DEVICE_INFO_PROTOCOL:
|
case IPTV_DEVICE_INFO_PROTOCOL:
|
||||||
info = pIptvStreamer ? *pIptvStreamer->GetInformation() : "";
|
s = pIptvStreamer ? *pIptvStreamer->GetInformation() : "";
|
||||||
break;
|
break;
|
||||||
case IPTV_DEVICE_INFO_BITRATE:
|
case IPTV_DEVICE_INFO_BITRATE:
|
||||||
info = pIptvStreamer ? *pIptvStreamer->GetStreamerStatistic() : "";
|
s = pIptvStreamer ? *pIptvStreamer->GetStreamerStatistic() : "";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
info = cString::sprintf("%s%s%s",
|
s = cString::sprintf("%s%s%s",
|
||||||
*GetGeneralInformation(),
|
*GetGeneralInformation(),
|
||||||
*GetPidsInformation(),
|
*GetPidsInformation(),
|
||||||
*GetFiltersInformation());
|
*GetFiltersInformation());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return info;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
cString cIptvDevice::DeviceType(void) const
|
cString cIptvDevice::DeviceType(void) const
|
||||||
@ -268,6 +271,9 @@ bool cIptvDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
|
|||||||
case cIptvTransponderParameters::eProtocolUDP:
|
case cIptvTransponderParameters::eProtocolUDP:
|
||||||
protocol = pUdpProtocol;
|
protocol = pUdpProtocol;
|
||||||
break;
|
break;
|
||||||
|
case cIptvTransponderParameters::eProtocolCURL:
|
||||||
|
protocol = pCurlProtocol;
|
||||||
|
break;
|
||||||
case cIptvTransponderParameters::eProtocolHTTP:
|
case cIptvTransponderParameters::eProtocolHTTP:
|
||||||
protocol = pHttpProtocol;
|
protocol = pHttpProtocol;
|
||||||
break;
|
break;
|
||||||
@ -400,10 +406,10 @@ void cIptvDevice::CloseDvr(void)
|
|||||||
isOpenDvr = false;
|
isOpenDvr = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cIptvDevice::HasLock(int TimeoutMs)
|
bool cIptvDevice::HasLock(int TimeoutMs) const
|
||||||
{
|
{
|
||||||
//debug("cIptvDevice::HasLock(%d): %d\n", deviceIndex, TimeoutMs);
|
//debug("cIptvDevice::HasLock(%d): %d\n", deviceIndex, TimeoutMs);
|
||||||
return (!IsBuffering());
|
return (!isBuffering);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cIptvDevice::HasInternalCam(void)
|
bool cIptvDevice::HasInternalCam(void)
|
||||||
@ -415,26 +421,29 @@ bool cIptvDevice::HasInternalCam(void)
|
|||||||
void cIptvDevice::ResetBuffering(void)
|
void cIptvDevice::ResetBuffering(void)
|
||||||
{
|
{
|
||||||
debug("cIptvDevice::ResetBuffering(%d)\n", deviceIndex);
|
debug("cIptvDevice::ResetBuffering(%d)\n", deviceIndex);
|
||||||
|
isBuffering = true;
|
||||||
// pad prefill to multiple of TS_SIZE
|
// pad prefill to multiple of TS_SIZE
|
||||||
tsBufferPrefill = (unsigned int)MEGABYTE(IptvConfig.GetTsBufferSize()) *
|
tsBufferPrefill = (unsigned int)MEGABYTE(IptvConfig.GetTsBufferSize()) *
|
||||||
IptvConfig.GetTsBufferPrefillRatio() / 100;
|
IptvConfig.GetTsBufferPrefillRatio() / 100;
|
||||||
tsBufferPrefill -= (tsBufferPrefill % TS_SIZE);
|
tsBufferPrefill -= (tsBufferPrefill % TS_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cIptvDevice::IsBuffering(void)
|
void cIptvDevice::CheckBuffering(void)
|
||||||
{
|
{
|
||||||
//debug("cIptvDevice::IsBuffering(%d): %d\n", deviceIndex);
|
//debug("cIptvDevice::CheckBuffering(%d): %d\n", deviceIndex);
|
||||||
if (tsBufferPrefill && tsBuffer->Available() < tsBufferPrefill)
|
if (tsBufferPrefill && tsBuffer && tsBuffer->Available() < tsBufferPrefill)
|
||||||
return true;
|
isBuffering = true;
|
||||||
else
|
else {
|
||||||
|
isBuffering = false;
|
||||||
tsBufferPrefill = 0;
|
tsBufferPrefill = 0;
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cIptvDevice::GetTSPacket(uchar *&Data)
|
bool cIptvDevice::GetTSPacket(uchar *&Data)
|
||||||
{
|
{
|
||||||
//debug("cIptvDevice::GetTSPacket(%d)\n", deviceIndex);
|
//debug("cIptvDevice::GetTSPacket(%d)\n", deviceIndex);
|
||||||
if (tsBuffer && !IsBuffering()) {
|
CheckBuffering();
|
||||||
|
if (tsBuffer && !isBuffering) {
|
||||||
if (isPacketDelivered) {
|
if (isPacketDelivered) {
|
||||||
tsBuffer->Del(TS_SIZE);
|
tsBuffer->Del(TS_SIZE);
|
||||||
isPacketDelivered = false;
|
isPacketDelivered = false;
|
||||||
|
7
device.h
7
device.h
@ -11,6 +11,7 @@
|
|||||||
#include <vdr/device.h>
|
#include <vdr/device.h>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "protocoludp.h"
|
#include "protocoludp.h"
|
||||||
|
#include "protocolcurl.h"
|
||||||
#include "protocolhttp.h"
|
#include "protocolhttp.h"
|
||||||
#include "protocolfile.h"
|
#include "protocolfile.h"
|
||||||
#include "protocolext.h"
|
#include "protocolext.h"
|
||||||
@ -37,12 +38,14 @@ private:
|
|||||||
int dvrFd;
|
int dvrFd;
|
||||||
bool isPacketDelivered;
|
bool isPacketDelivered;
|
||||||
bool isOpenDvr;
|
bool isOpenDvr;
|
||||||
|
bool isBuffering;
|
||||||
bool sidScanEnabled;
|
bool sidScanEnabled;
|
||||||
bool pidScanEnabled;
|
bool pidScanEnabled;
|
||||||
cRingBufferLinear *tsBuffer;
|
cRingBufferLinear *tsBuffer;
|
||||||
int tsBufferPrefill;
|
int tsBufferPrefill;
|
||||||
tChannelID channelId;
|
tChannelID channelId;
|
||||||
cIptvProtocolUdp *pUdpProtocol;
|
cIptvProtocolUdp *pUdpProtocol;
|
||||||
|
cIptvProtocolCurl *pCurlProtocol;
|
||||||
cIptvProtocolHttp *pHttpProtocol;
|
cIptvProtocolHttp *pHttpProtocol;
|
||||||
cIptvProtocolFile *pFileProtocol;
|
cIptvProtocolFile *pFileProtocol;
|
||||||
cIptvProtocolExt *pExtProtocol;
|
cIptvProtocolExt *pExtProtocol;
|
||||||
@ -71,7 +74,7 @@ private:
|
|||||||
// for channel parsing & buffering
|
// for channel parsing & buffering
|
||||||
private:
|
private:
|
||||||
void ResetBuffering(void);
|
void ResetBuffering(void);
|
||||||
bool IsBuffering(void);
|
void CheckBuffering(void);
|
||||||
bool DeleteFilter(unsigned int Index);
|
bool DeleteFilter(unsigned int Index);
|
||||||
bool IsBlackListed(u_short Pid, u_char Tid, u_char Mask) const;
|
bool IsBlackListed(u_short Pid, u_char Tid, u_char Mask) const;
|
||||||
|
|
||||||
@ -107,7 +110,7 @@ public:
|
|||||||
|
|
||||||
// for transponder lock
|
// for transponder lock
|
||||||
public:
|
public:
|
||||||
virtual bool HasLock(int);
|
virtual bool HasLock(int) const;
|
||||||
|
|
||||||
// for common interface
|
// for common interface
|
||||||
public:
|
public:
|
||||||
|
13
iptv.c
13
iptv.c
@ -13,15 +13,15 @@
|
|||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "iptvservice.h"
|
#include "iptvservice.h"
|
||||||
|
|
||||||
#if defined(APIVERSNUM) && APIVERSNUM < 10730
|
#if defined(APIVERSNUM) && APIVERSNUM < 10738
|
||||||
#error "VDR-1.7.30 API version or greater is required!"
|
#error "VDR-1.7.38 API version or greater is required!"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef GITVERSION
|
#ifndef GITVERSION
|
||||||
#define GITVERSION ""
|
#define GITVERSION ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const char VERSION[] = "1.1.0" GITVERSION;
|
const char VERSION[] = "1.2.0" GITVERSION;
|
||||||
static const char DESCRIPTION[] = trNOOP("Experience the IPTV");
|
static const char DESCRIPTION[] = trNOOP("Experience the IPTV");
|
||||||
|
|
||||||
class cPluginIptv : public cPlugin {
|
class cPluginIptv : public cPlugin {
|
||||||
@ -107,6 +107,10 @@ bool cPluginIptv::Start(void)
|
|||||||
{
|
{
|
||||||
debug("cPluginIptv::Start()\n");
|
debug("cPluginIptv::Start()\n");
|
||||||
// Start any background activities the plugin shall perform.
|
// Start any background activities the plugin shall perform.
|
||||||
|
if (curl_global_init(CURL_GLOBAL_ALL) == CURLE_OK) {
|
||||||
|
curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
|
||||||
|
info("Using CURL %s", data->version);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,6 +118,7 @@ void cPluginIptv::Stop(void)
|
|||||||
{
|
{
|
||||||
debug("cPluginIptv::Stop()\n");
|
debug("cPluginIptv::Stop()\n");
|
||||||
// Stop any background activities the plugin is performing.
|
// Stop any background activities the plugin is performing.
|
||||||
|
curl_global_cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cPluginIptv::Housekeeping(void)
|
void cPluginIptv::Housekeeping(void)
|
||||||
@ -204,7 +209,7 @@ bool cPluginIptv::Service(const char *Id, void *Data)
|
|||||||
debug("cPluginIptv::Service()\n");
|
debug("cPluginIptv::Service()\n");
|
||||||
if (strcmp(Id,"IptvService-v1.0") == 0) {
|
if (strcmp(Id,"IptvService-v1.0") == 0) {
|
||||||
if (Data) {
|
if (Data) {
|
||||||
IptvService_v1_0 *data = (IptvService_v1_0*)Data;
|
IptvService_v1_0 *data = reinterpret_cast<IptvService_v1_0*>(Data);
|
||||||
cIptvDevice *dev = cIptvDevice::GetIptvDevice(data->cardIndex);
|
cIptvDevice *dev = cIptvDevice::GetIptvDevice(data->cardIndex);
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return false;
|
return false;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: vdr-iptv 1.0.0\n"
|
"Project-Id-Version: vdr-iptv 1.2.0\n"
|
||||||
"Report-Msgid-Bugs-To: <see README>\n"
|
"Report-Msgid-Bugs-To: <see README>\n"
|
||||||
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
||||||
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
||||||
@ -118,6 +118,9 @@ msgstr "Hilfe"
|
|||||||
msgid "UDP"
|
msgid "UDP"
|
||||||
msgstr "UDP"
|
msgstr "UDP"
|
||||||
|
|
||||||
|
msgid "CURL"
|
||||||
|
msgstr "CURL"
|
||||||
|
|
||||||
msgid "HTTP"
|
msgid "HTTP"
|
||||||
msgstr "HTTP"
|
msgstr "HTTP"
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: vdr-iptv 1.0.0\n"
|
"Project-Id-Version: vdr-iptv 1.2.0\n"
|
||||||
"Report-Msgid-Bugs-To: <see README>\n"
|
"Report-Msgid-Bugs-To: <see README>\n"
|
||||||
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
||||||
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
||||||
@ -133,6 +133,9 @@ msgstr "Opaste"
|
|||||||
msgid "UDP"
|
msgid "UDP"
|
||||||
msgstr "UDP"
|
msgstr "UDP"
|
||||||
|
|
||||||
|
msgid "CURL"
|
||||||
|
msgstr "CURL"
|
||||||
|
|
||||||
msgid "HTTP"
|
msgid "HTTP"
|
||||||
msgstr "HTTP"
|
msgstr "HTTP"
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: vdr-iptv 1.0.0\n"
|
"Project-Id-Version: vdr-iptv 1.2.0\n"
|
||||||
"Report-Msgid-Bugs-To: <see README>\n"
|
"Report-Msgid-Bugs-To: <see README>\n"
|
||||||
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
||||||
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
||||||
@ -135,6 +135,9 @@ msgstr "Aide"
|
|||||||
msgid "UDP"
|
msgid "UDP"
|
||||||
msgstr "UDP"
|
msgstr "UDP"
|
||||||
|
|
||||||
|
msgid "CURL"
|
||||||
|
msgstr "CURL"
|
||||||
|
|
||||||
msgid "HTTP"
|
msgid "HTTP"
|
||||||
msgstr "HTTP"
|
msgstr "HTTP"
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: vdr-iptv 1.0.0\n"
|
"Project-Id-Version: vdr-iptv 1.2.0\n"
|
||||||
"Report-Msgid-Bugs-To: <see README>\n"
|
"Report-Msgid-Bugs-To: <see README>\n"
|
||||||
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
||||||
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
||||||
@ -134,6 +134,9 @@ msgstr "Aiuto"
|
|||||||
msgid "UDP"
|
msgid "UDP"
|
||||||
msgstr "UDP"
|
msgstr "UDP"
|
||||||
|
|
||||||
|
msgid "CURL"
|
||||||
|
msgstr "CURL"
|
||||||
|
|
||||||
msgid "HTTP"
|
msgid "HTTP"
|
||||||
msgstr "HTTP"
|
msgstr "HTTP"
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: vdr-iptv 1.0.0\n"
|
"Project-Id-Version: vdr-iptv 1.2.0\n"
|
||||||
"Report-Msgid-Bugs-To: <see README>\n"
|
"Report-Msgid-Bugs-To: <see README>\n"
|
||||||
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
||||||
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
||||||
@ -133,6 +133,9 @@ msgstr "Help"
|
|||||||
msgid "UDP"
|
msgid "UDP"
|
||||||
msgstr "UDP"
|
msgstr "UDP"
|
||||||
|
|
||||||
|
msgid "CURL"
|
||||||
|
msgstr "CURL"
|
||||||
|
|
||||||
msgid "HTTP"
|
msgid "HTTP"
|
||||||
msgstr "HTTP"
|
msgstr "HTTP"
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: vdr-iptv 1.0.0\n"
|
"Project-Id-Version: vdr-iptv 1.2.0\n"
|
||||||
"Report-Msgid-Bugs-To: <see README>\n"
|
"Report-Msgid-Bugs-To: <see README>\n"
|
||||||
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
||||||
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
||||||
@ -119,6 +119,9 @@ msgstr "Справка"
|
|||||||
msgid "UDP"
|
msgid "UDP"
|
||||||
msgstr "UDP"
|
msgstr "UDP"
|
||||||
|
|
||||||
|
msgid "CURL"
|
||||||
|
msgstr "CURL"
|
||||||
|
|
||||||
msgid "HTTP"
|
msgid "HTTP"
|
||||||
msgstr "HTTP"
|
msgstr "HTTP"
|
||||||
|
|
||||||
|
512
protocolcurl.c
Normal file
512
protocolcurl.c
Normal file
@ -0,0 +1,512 @@
|
|||||||
|
/*
|
||||||
|
* protocolcurl.c: IPTV plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "protocolcurl.h"
|
||||||
|
|
||||||
|
#define iptv_curl_easy_setopt(X, Y, Z) \
|
||||||
|
if ((res = curl_easy_setopt((X), (Y), (Z))) != CURLE_OK) { error("curl_easy_setopt(%s, %s, %s) failed: %d\n", #X, #Y, #Z, res); }
|
||||||
|
|
||||||
|
#define iptv_curl_easy_perform(X) \
|
||||||
|
if ((res = curl_easy_perform((X))) != CURLE_OK) { error("curl_easy_perform(%s) failed: %d\n", #X, res); }
|
||||||
|
|
||||||
|
cIptvProtocolCurl::cIptvProtocolCurl()
|
||||||
|
: streamUrlM(""),
|
||||||
|
streamParamM(0),
|
||||||
|
mutexM(),
|
||||||
|
handleM(NULL),
|
||||||
|
multiM(NULL),
|
||||||
|
headerListM(NULL),
|
||||||
|
ringBufferM(new cRingBufferLinear(MEGABYTE(IptvConfig.GetTsBufferSize()), 7 * TS_SIZE)),
|
||||||
|
rtspControlM(),
|
||||||
|
modeM(eModeUnknown),
|
||||||
|
connectedM(false),
|
||||||
|
pausedM(false)
|
||||||
|
{
|
||||||
|
debug("cIptvProtocolCurl::cIptvProtocolCurl()\n");
|
||||||
|
if (ringBufferM)
|
||||||
|
ringBufferM->SetTimeouts(100, 0);
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
cIptvProtocolCurl::~cIptvProtocolCurl()
|
||||||
|
{
|
||||||
|
debug("cIptvProtocolCurl::~cIptvProtocolCurl()\n");
|
||||||
|
Disconnect();
|
||||||
|
// Free allocated memory
|
||||||
|
DELETE_POINTER(ringBufferM);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cIptvProtocolCurl::WriteCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP)
|
||||||
|
{
|
||||||
|
cIptvProtocolCurl *obj = reinterpret_cast<cIptvProtocolCurl *>(dataP);
|
||||||
|
size_t len = sizeP * nmembP;
|
||||||
|
//debug("cIptvProtocolCurl::WriteCallback(%zu)\n", len);
|
||||||
|
|
||||||
|
if (obj && !obj->PutData((unsigned char *)ptrP, (int)len))
|
||||||
|
return CURL_WRITEFUNC_PAUSE;
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cIptvProtocolCurl::WriteRtspCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP)
|
||||||
|
{
|
||||||
|
cIptvProtocolCurl *obj = reinterpret_cast<cIptvProtocolCurl *>(dataP);
|
||||||
|
size_t len = sizeP * nmembP;
|
||||||
|
unsigned char *p = (unsigned char *)ptrP;
|
||||||
|
//debug("cIptvProtocolCurl::WriteRtspCallback(%zu)\n", len);
|
||||||
|
|
||||||
|
// validate packet header ('$') and channel (0)
|
||||||
|
if (obj && (p[0] == 0x24 ) && (p[1] == 0)) {
|
||||||
|
int length = (p[2] << 8) | p[3];
|
||||||
|
if (length > 3) {
|
||||||
|
// skip interleave header
|
||||||
|
p += 4;
|
||||||
|
// http://tools.ietf.org/html/rfc3550
|
||||||
|
// http://tools.ietf.org/html/rfc2250
|
||||||
|
// version
|
||||||
|
unsigned int v = (p[0] >> 6) & 0x03;
|
||||||
|
// extension bit
|
||||||
|
unsigned int x = (p[0] >> 4) & 0x01;
|
||||||
|
// cscr count
|
||||||
|
unsigned int cc = p[0] & 0x0F;
|
||||||
|
// payload type: MPEG2 TS = 33
|
||||||
|
//unsigned int pt = p[1] & 0x7F;
|
||||||
|
// header lenght
|
||||||
|
unsigned int headerlen = (3 + cc) * (unsigned int)sizeof(uint32_t);
|
||||||
|
// check if extension
|
||||||
|
if (x) {
|
||||||
|
// extension header length
|
||||||
|
unsigned int ehl = (((p[headerlen + 2] & 0xFF) << 8) |(p[headerlen + 3] & 0xFF));
|
||||||
|
// update header length
|
||||||
|
headerlen += (ehl + 1) * (unsigned int)sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
// Check that rtp is version 2 and payload contains multiple of TS packet data
|
||||||
|
if ((v == 2) && (((length - headerlen) % TS_SIZE) == 0) && (p[headerlen] == TS_SYNC_BYTE)) {
|
||||||
|
// Set argument point to payload in read buffer
|
||||||
|
obj->PutData(&p[headerlen], (length - headerlen));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cIptvProtocolCurl::DescribeCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP)
|
||||||
|
{
|
||||||
|
cIptvProtocolCurl *obj = reinterpret_cast<cIptvProtocolCurl *>(dataP);
|
||||||
|
size_t len = sizeP * nmembP;
|
||||||
|
//debug("cIptvProtocolCurl::DescribeCallback(%zu)\n", len);
|
||||||
|
|
||||||
|
cString control = "";
|
||||||
|
char *p = (char *)ptrP;
|
||||||
|
char *r = strtok(p, "\r\n");
|
||||||
|
|
||||||
|
while (r) {
|
||||||
|
//debug("cIptvProtocolCurl::DescribeCallback(%zu): %s\n", len, r);
|
||||||
|
if (strstr(r, "a=control")) {
|
||||||
|
char *s = NULL;
|
||||||
|
if (sscanf(r, "a=control:%64ms", &s) == 1)
|
||||||
|
control = compactspace(s);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
r = strtok(NULL, "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isempty(*control) && obj)
|
||||||
|
obj->SetRtspControl(*control);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cIptvProtocolCurl::HeaderCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP)
|
||||||
|
{
|
||||||
|
//cIptvProtocolCurl *obj = reinterpret_cast<cIptvProtocolCurl *>(dataP);
|
||||||
|
size_t len = sizeP * nmembP;
|
||||||
|
//debug("cIptvProtocolCurl::HeaderCallback(%zu)\n", len);
|
||||||
|
|
||||||
|
char *p = (char *)ptrP;
|
||||||
|
char *r = strtok(p, "\r\n");
|
||||||
|
|
||||||
|
while (r) {
|
||||||
|
//debug("cIptvProtocolCurl::HeaderCallback(%zu): %s\n", len, r);
|
||||||
|
r = strtok(NULL, "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cIptvProtocolCurl::SetRtspControl(const char *controlP)
|
||||||
|
{
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
debug("cIptvProtocolCurl::SetRtspControl('%s')\n", controlP);
|
||||||
|
rtspControlM = controlP;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cIptvProtocolCurl::PutData(unsigned char *dataP, int lenP)
|
||||||
|
{
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
//debug("cIptvProtocolCurl::PutData(%d)\n", lenP);
|
||||||
|
if (pausedM)
|
||||||
|
return false;
|
||||||
|
if (ringBufferM && (lenP >= 0)) {
|
||||||
|
// should be pause the transfer?
|
||||||
|
if (ringBufferM->Free() < (2 * CURL_MAX_WRITE_SIZE)) {
|
||||||
|
debug("cIptvProtocolCurl::PutData(pause): free=%d available=%d len=%d", ringBufferM->Free(), ringBufferM->Available(), lenP);
|
||||||
|
pausedM = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int p = ringBufferM->Put(dataP, lenP);
|
||||||
|
if (p != lenP)
|
||||||
|
ringBufferM->ReportOverflow(lenP - p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cIptvProtocolCurl::DelData(int lenP)
|
||||||
|
{
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
//debug("cIptvProtocolCurl::DelData()\n");
|
||||||
|
if (ringBufferM && (lenP >= 0))
|
||||||
|
ringBufferM->Del(lenP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cIptvProtocolCurl::ClearData()
|
||||||
|
{
|
||||||
|
//debug("cIptvProtocolCurl::ClearData()\n");
|
||||||
|
if (ringBufferM)
|
||||||
|
ringBufferM->Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *cIptvProtocolCurl::GetData(unsigned int *lenP)
|
||||||
|
{
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
//debug("cIptvProtocolCurl::GetData()\n");
|
||||||
|
unsigned char *p = NULL;
|
||||||
|
*lenP = 0;
|
||||||
|
if (ringBufferM) {
|
||||||
|
int count = 0;
|
||||||
|
p = ringBufferM->Get(count);
|
||||||
|
#if 0
|
||||||
|
if (p && count >= TS_SIZE) {
|
||||||
|
if (*p != TS_SYNC_BYTE) {
|
||||||
|
for (int i = 1; i < count; ++i) {
|
||||||
|
if (p[i] == TS_SYNC_BYTE) {
|
||||||
|
count = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error("IPTV skipped %d bytes to sync on TS packet\n", count);
|
||||||
|
ringBufferM->Del(count);
|
||||||
|
*lenP = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
count -= (count % TS_SIZE);
|
||||||
|
*lenP = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cIptvProtocolCurl::Connect()
|
||||||
|
{
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
debug("cIptvProtocolCurl::Connect()\n");
|
||||||
|
if (connectedM)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// initialize the curl session
|
||||||
|
if (!handleM)
|
||||||
|
handleM = curl_easy_init();
|
||||||
|
if (!multiM)
|
||||||
|
multiM = curl_multi_init();
|
||||||
|
|
||||||
|
if (handleM && multiM && !isempty(*streamUrlM)) {
|
||||||
|
CURLcode res = CURLE_OK;
|
||||||
|
cString netrc = cString::sprintf("%s/netrc", IptvConfig.GetConfigDirectory());
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// verbose output
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_VERBOSE, 1L);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// set callbacks
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEFUNCTION, cIptvProtocolCurl::WriteCallback);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, this);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_HEADERFUNCTION, cIptvProtocolCurl::HeaderCallback);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEHEADER, this);
|
||||||
|
|
||||||
|
// no progress meter and no signaling
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_NOPROGRESS, 1L);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_NOSIGNAL, 1L);
|
||||||
|
|
||||||
|
// support netrc
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_NETRC, (long)CURL_NETRC_OPTIONAL);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_NETRC_FILE, *netrc);
|
||||||
|
|
||||||
|
// set timeout
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_CONNECTTIMEOUT, (long)eConnectTimeoutS);
|
||||||
|
|
||||||
|
// set user-agent
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_USERAGENT, *cString::sprintf("vdr-%s/%s", PLUGIN_NAME_I18N, VERSION));
|
||||||
|
|
||||||
|
// set url
|
||||||
|
//char *p = curl_easy_unescape(handleM, *streamUrlM, 0, NULL);
|
||||||
|
//streamUrlM = p;
|
||||||
|
//curl_free(p);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_URL, *streamUrlM);
|
||||||
|
|
||||||
|
// protocol specific initializations
|
||||||
|
switch (modeM) {
|
||||||
|
case eModeRtsp:
|
||||||
|
{
|
||||||
|
cString uri, control, transport, range;
|
||||||
|
|
||||||
|
// request server options
|
||||||
|
uri = cString::sprintf("%s", *streamUrlM);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS);
|
||||||
|
iptv_curl_easy_perform(handleM);
|
||||||
|
|
||||||
|
// request session description - SDP is delivered in message body and not in the header!
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEFUNCTION, cIptvProtocolCurl::DescribeCallback);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, this);
|
||||||
|
uri = cString::sprintf("%s", *streamUrlM);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE);
|
||||||
|
iptv_curl_easy_perform(handleM);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEFUNCTION, NULL);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, NULL);
|
||||||
|
|
||||||
|
// setup media stream
|
||||||
|
uri = cString::sprintf("%s/%s", *streamUrlM, *rtspControlM);
|
||||||
|
transport = "RTP/AVP/TCP;unicast;interleaved=0-1";
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_TRANSPORT, *transport);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP);
|
||||||
|
iptv_curl_easy_perform(handleM);
|
||||||
|
|
||||||
|
// start playing
|
||||||
|
uri = cString::sprintf("%s/", *streamUrlM);
|
||||||
|
range = "0.000-";
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_RANGE, *range);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY);
|
||||||
|
iptv_curl_easy_perform(handleM);
|
||||||
|
|
||||||
|
// start receiving
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_INTERLEAVEFUNCTION, cIptvProtocolCurl::WriteRtspCallback);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_INTERLEAVEDATA, this);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_RECEIVE);
|
||||||
|
iptv_curl_easy_perform(handleM);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case eModeHttp:
|
||||||
|
case eModeHttps:
|
||||||
|
{
|
||||||
|
// limit download speed (bytes/s)
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_MAX_RECV_SPEED_LARGE, eMaxDownloadSpeedMBits * 131072L);
|
||||||
|
|
||||||
|
// follow location
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_FOLLOWLOCATION, 1L);
|
||||||
|
|
||||||
|
// fail if HTTP return code is >= 400
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_FAILONERROR, 1L);
|
||||||
|
|
||||||
|
// set additional headers to prevent caching
|
||||||
|
headerListM = curl_slist_append(headerListM, "Cache-Control: no-store, no-cache, must-revalidate");
|
||||||
|
headerListM = curl_slist_append(headerListM, "Cache-Control: post-check=0, pre-check=0");
|
||||||
|
headerListM = curl_slist_append(headerListM, "Pragma: no-cache");
|
||||||
|
headerListM = curl_slist_append(headerListM, "Expires: Mon, 26 Jul 1997 05:00:00 GMT");
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_HTTPHEADER, headerListM);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case eModeFile:
|
||||||
|
case eModeUnknown:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add handle into multi set
|
||||||
|
curl_multi_add_handle(multiM, handleM);
|
||||||
|
|
||||||
|
connectedM = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cIptvProtocolCurl::Disconnect()
|
||||||
|
{
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
debug("cIptvProtocolCurl::Disconnect()\n");
|
||||||
|
if (handleM) {
|
||||||
|
// mode specific tricks
|
||||||
|
switch (modeM) {
|
||||||
|
case eModeRtsp:
|
||||||
|
{
|
||||||
|
CURLcode res = CURLE_OK;
|
||||||
|
// teardown rtsp session
|
||||||
|
cString uri = cString::sprintf("%s/", *streamUrlM);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN);
|
||||||
|
iptv_curl_easy_perform(handleM);
|
||||||
|
rtspControlM = "";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case eModeHttp:
|
||||||
|
case eModeHttps:
|
||||||
|
case eModeFile:
|
||||||
|
case eModeUnknown:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup curl stuff
|
||||||
|
if (headerListM) {
|
||||||
|
curl_slist_free_all(headerListM);
|
||||||
|
headerListM = NULL;
|
||||||
|
}
|
||||||
|
curl_multi_remove_handle(multiM, handleM);
|
||||||
|
curl_multi_cleanup(multiM);
|
||||||
|
multiM = NULL;
|
||||||
|
curl_easy_cleanup(handleM);
|
||||||
|
handleM = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearData();
|
||||||
|
connectedM = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cIptvProtocolCurl::Open(void)
|
||||||
|
{
|
||||||
|
debug("cIptvProtocolCurl::Open()\n");
|
||||||
|
return Connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cIptvProtocolCurl::Close(void)
|
||||||
|
{
|
||||||
|
debug("cIptvProtocolCurl::Close()\n");
|
||||||
|
Disconnect();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cIptvProtocolCurl::Read(unsigned char* BufferAddr, unsigned int BufferLen)
|
||||||
|
{
|
||||||
|
//debug("cIptvProtocolCurl::Read()\n");
|
||||||
|
int len = 0;
|
||||||
|
if (ringBufferM) {
|
||||||
|
// fill up the buffer
|
||||||
|
if (handleM && multiM) {
|
||||||
|
switch (modeM) {
|
||||||
|
case eModeRtsp:
|
||||||
|
{
|
||||||
|
//CURLcode res = CURLE_OK;
|
||||||
|
//iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_RECEIVE);
|
||||||
|
//iptv_curl_easy_perform(handleM);
|
||||||
|
// @todo - how to detect eof?
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case eModeFile:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case eModeHttp:
|
||||||
|
case eModeHttps:
|
||||||
|
{
|
||||||
|
CURLMcode res;
|
||||||
|
int running_handles;
|
||||||
|
|
||||||
|
do {
|
||||||
|
res = curl_multi_perform(multiM, &running_handles);
|
||||||
|
} while (res == CURLM_CALL_MULTI_PERFORM);
|
||||||
|
|
||||||
|
// shall be continue filling up the buffer?
|
||||||
|
mutexM.Lock();
|
||||||
|
if (pausedM && (ringBufferM->Free() > ringBufferM->Available())) {
|
||||||
|
debug("cIptvProtocolCurl::Read(continue): free=%d available=%d\n", ringBufferM->Free(), ringBufferM->Available());
|
||||||
|
pausedM = false;
|
||||||
|
curl_easy_pause(handleM, CURLPAUSE_CONT);
|
||||||
|
}
|
||||||
|
mutexM.Unlock();
|
||||||
|
|
||||||
|
// check end of file
|
||||||
|
if (running_handles == 0) {
|
||||||
|
int msgcount;
|
||||||
|
CURLMsg *msg = curl_multi_info_read(multiM, &msgcount);
|
||||||
|
if (msg && (msg->msg == CURLMSG_DONE)) {
|
||||||
|
debug("cIptvProtocolCurl::Read(done): %s (%d)\n", curl_easy_strerror(msg->data.result), msg->data.result);
|
||||||
|
Disconnect();
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... and try to empty it
|
||||||
|
unsigned char *p = GetData(&BufferLen);
|
||||||
|
if (p && (BufferLen > 0)) {
|
||||||
|
memcpy(BufferAddr, p, BufferLen);
|
||||||
|
DelData(BufferLen);
|
||||||
|
len = BufferLen;
|
||||||
|
//debug("cIptvProtocolCurl::Read(): get %d bytes\n", len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cIptvProtocolCurl::Set(const char* Location, const int Parameter, const int Index)
|
||||||
|
{
|
||||||
|
debug("cIptvProtocolCurl::Set(): Location=%s Parameter=%d Index=%d\n", Location, Parameter, Index);
|
||||||
|
if (!isempty(Location)) {
|
||||||
|
// Disconnect
|
||||||
|
Disconnect();
|
||||||
|
// Update stream URL: colons (%3A) and pipes (%7C) shall be decoded
|
||||||
|
char *s = strdup(Location);
|
||||||
|
strreplace(s, "%3A", ":");
|
||||||
|
strreplace(s, "%7C", "|");
|
||||||
|
streamUrlM = s;
|
||||||
|
free(s);
|
||||||
|
if (startswith(*streamUrlM, "rtsp") || startswith(*streamUrlM, "RTSP"))
|
||||||
|
modeM = eModeRtsp;
|
||||||
|
else if (startswith(*streamUrlM, "https") || startswith(*streamUrlM, "HTTPS"))
|
||||||
|
modeM = eModeHttp;
|
||||||
|
else if (startswith(*streamUrlM, "http") || startswith(*streamUrlM, "HTTP"))
|
||||||
|
modeM = eModeHttps;
|
||||||
|
else if (startswith(*streamUrlM, "file") || startswith(*streamUrlM, "FILE"))
|
||||||
|
modeM = eModeFile;
|
||||||
|
else
|
||||||
|
modeM = eModeUnknown;
|
||||||
|
// Update stream parameter
|
||||||
|
streamParamM = Parameter;
|
||||||
|
//debug("%s [%d]\n", *streamUrlM, streamParamM);
|
||||||
|
// Reconnect
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cIptvProtocolCurl::GetInformation(void)
|
||||||
|
{
|
||||||
|
//debug("cIptvProtocolCurl::GetInformation()");
|
||||||
|
return cString::sprintf("%s [%d]", *streamUrlM, streamParamM);
|
||||||
|
}
|
72
protocolcurl.h
Normal file
72
protocolcurl.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* protocolcurl.h: IPTV plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __IPTV_PROTOCOLCURL_H
|
||||||
|
#define __IPTV_PROTOCOLCURL_H
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <curl/easy.h>
|
||||||
|
|
||||||
|
#include <vdr/ringbuffer.h>
|
||||||
|
#include <vdr/thread.h>
|
||||||
|
#include <vdr/tools.h>
|
||||||
|
|
||||||
|
#include "protocolif.h"
|
||||||
|
|
||||||
|
class cIptvProtocolCurl : public cIptvProtocolIf {
|
||||||
|
private:
|
||||||
|
enum eModeType {
|
||||||
|
eModeUnknown = 0,
|
||||||
|
eModeHttp,
|
||||||
|
eModeHttps,
|
||||||
|
eModeRtsp,
|
||||||
|
eModeFile,
|
||||||
|
eModeCount
|
||||||
|
};
|
||||||
|
enum {
|
||||||
|
eConnectTimeoutS = 5, // in seconds
|
||||||
|
eSelectTimeoutMs = 10, // in milliseconds
|
||||||
|
eMaxDownloadSpeedMBits = 20 // in megabits per second
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t WriteCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP);
|
||||||
|
static size_t WriteRtspCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP);
|
||||||
|
static size_t DescribeCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP);
|
||||||
|
static size_t HeaderCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP);
|
||||||
|
|
||||||
|
cString streamUrlM;
|
||||||
|
int streamParamM;
|
||||||
|
cMutex mutexM;
|
||||||
|
CURL *handleM;
|
||||||
|
CURLM *multiM;
|
||||||
|
struct curl_slist *headerListM;
|
||||||
|
cRingBufferLinear *ringBufferM;
|
||||||
|
cString rtspControlM;
|
||||||
|
eModeType modeM;
|
||||||
|
bool connectedM;
|
||||||
|
bool pausedM;
|
||||||
|
|
||||||
|
bool Connect(void);
|
||||||
|
bool Disconnect(void);
|
||||||
|
bool PutData(unsigned char *dataP, int lenP);
|
||||||
|
void DelData(int lenP);
|
||||||
|
void ClearData(void);
|
||||||
|
unsigned char *GetData(unsigned int *lenP);
|
||||||
|
|
||||||
|
public:
|
||||||
|
cIptvProtocolCurl();
|
||||||
|
virtual ~cIptvProtocolCurl();
|
||||||
|
int Read(unsigned char* BufferAddr, unsigned int BufferLen);
|
||||||
|
bool Set(const char* Location, const int Parameter, const int Index);
|
||||||
|
bool Open(void);
|
||||||
|
bool Close(void);
|
||||||
|
cString GetInformation(void);
|
||||||
|
void SetRtspControl(const char *controlP);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __IPTV_PROTOCOLCURL_H
|
||||||
|
|
1
setup.c
1
setup.c
@ -36,6 +36,7 @@ public:
|
|||||||
cIptvMenuInfo::cIptvMenuInfo()
|
cIptvMenuInfo::cIptvMenuInfo()
|
||||||
:cOsdMenu(tr("IPTV Information")), text(""), timeout(), page(IPTV_DEVICE_INFO_GENERAL)
|
:cOsdMenu(tr("IPTV Information")), text(""), timeout(), page(IPTV_DEVICE_INFO_GENERAL)
|
||||||
{
|
{
|
||||||
|
SetMenuCategory(mcText);
|
||||||
timeout.Set(INFO_TIMEOUT_MS);
|
timeout.Set(INFO_TIMEOUT_MS);
|
||||||
UpdateInfo();
|
UpdateInfo();
|
||||||
SetHelp(tr("General"), tr("Pids"), tr("Filters"), tr("Bits/bytes"));
|
SetHelp(tr("General"), tr("Pids"), tr("Filters"), tr("Bits/bytes"));
|
||||||
|
2
setup.h
2
setup.h
@ -19,7 +19,7 @@ private:
|
|||||||
int sidscan;
|
int sidscan;
|
||||||
int pidscan;
|
int pidscan;
|
||||||
int protocol;
|
int protocol;
|
||||||
char address[MaxFileName];
|
char address[NAME_MAX];
|
||||||
int parameter;
|
int parameter;
|
||||||
public:
|
public:
|
||||||
cIptvTransponderParameters(const char *Parameters = NULL);
|
cIptvTransponderParameters(const char *Parameters = NULL);
|
||||||
|
3
socket.c
3
socket.c
@ -265,7 +265,8 @@ int cIptvUdpSocket::Read(unsigned char* BufferAddr, unsigned int BufferLen)
|
|||||||
if (BufferAddr[0] == TS_SYNC_BYTE)
|
if (BufferAddr[0] == TS_SYNC_BYTE)
|
||||||
return len;
|
return len;
|
||||||
else if (len > 3) {
|
else if (len > 3) {
|
||||||
// http://www.networksorcery.com/enp/rfc/rfc2250.txt
|
// http://tools.ietf.org/html/rfc3550
|
||||||
|
// http://tools.ietf.org/html/rfc2250
|
||||||
// version
|
// version
|
||||||
unsigned int v = (BufferAddr[0] >> 6) & 0x03;
|
unsigned int v = (BufferAddr[0] >> 6) & 0x03;
|
||||||
// extension bit
|
// extension bit
|
||||||
|
2
socket.h
2
socket.h
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* protocoludp.h: IPTV plugin for the Video Disk Recorder
|
* socket.h: IPTV plugin for the Video Disk Recorder
|
||||||
*
|
*
|
||||||
* See the README file for copyright information and how to reach the author.
|
* See the README file for copyright information and how to reach the author.
|
||||||
*
|
*
|
||||||
|
8
source.c
8
source.c
@ -32,6 +32,9 @@ cString cIptvTransponderParameters::ToString(char Type) const
|
|||||||
case eProtocolEXT:
|
case eProtocolEXT:
|
||||||
protocolstr = "EXT";
|
protocolstr = "EXT";
|
||||||
break;
|
break;
|
||||||
|
case eProtocolCURL:
|
||||||
|
protocolstr = "CURL";
|
||||||
|
break;
|
||||||
case eProtocolHTTP:
|
case eProtocolHTTP:
|
||||||
protocolstr = "HTTP";
|
protocolstr = "HTTP";
|
||||||
break;
|
break;
|
||||||
@ -81,6 +84,10 @@ bool cIptvTransponderParameters::Parse(const char *s)
|
|||||||
protocol = eProtocolUDP;
|
protocol = eProtocolUDP;
|
||||||
found_f = true;
|
found_f = true;
|
||||||
}
|
}
|
||||||
|
else if (strstr(data, "CURL")) {
|
||||||
|
protocol = eProtocolCURL;
|
||||||
|
found_f = true;
|
||||||
|
}
|
||||||
else if (strstr(data, "HTTP")) {
|
else if (strstr(data, "HTTP")) {
|
||||||
protocol = eProtocolHTTP;
|
protocol = eProtocolHTTP;
|
||||||
found_f = true;
|
found_f = true;
|
||||||
@ -134,6 +141,7 @@ cIptvSourceParam::cIptvSourceParam(char Source, const char *Description)
|
|||||||
debug("cIptvSourceParam::cIptvSourceParam(): Source=%c Description=%s\n", Source, Description);
|
debug("cIptvSourceParam::cIptvSourceParam(): Source=%c Description=%s\n", Source, Description);
|
||||||
|
|
||||||
protocols[cIptvTransponderParameters::eProtocolUDP] = tr("UDP");
|
protocols[cIptvTransponderParameters::eProtocolUDP] = tr("UDP");
|
||||||
|
protocols[cIptvTransponderParameters::eProtocolCURL] = tr("CURL");
|
||||||
protocols[cIptvTransponderParameters::eProtocolHTTP] = tr("HTTP");
|
protocols[cIptvTransponderParameters::eProtocolHTTP] = tr("HTTP");
|
||||||
protocols[cIptvTransponderParameters::eProtocolFILE] = tr("FILE");
|
protocols[cIptvTransponderParameters::eProtocolFILE] = tr("FILE");
|
||||||
protocols[cIptvTransponderParameters::eProtocolEXT] = tr("EXT");
|
protocols[cIptvTransponderParameters::eProtocolEXT] = tr("EXT");
|
||||||
|
3
source.h
3
source.h
@ -20,12 +20,13 @@ private:
|
|||||||
int sidscan;
|
int sidscan;
|
||||||
int pidscan;
|
int pidscan;
|
||||||
int protocol;
|
int protocol;
|
||||||
char address[MaxFileName];
|
char address[NAME_MAX];
|
||||||
int parameter;
|
int parameter;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum {
|
enum {
|
||||||
eProtocolUDP,
|
eProtocolUDP,
|
||||||
|
eProtocolCURL,
|
||||||
eProtocolHTTP,
|
eProtocolHTTP,
|
||||||
eProtocolFILE,
|
eProtocolFILE,
|
||||||
eProtocolEXT,
|
eProtocolEXT,
|
||||||
|
22
statistics.c
22
statistics.c
@ -36,10 +36,10 @@ cString cIptvSectionStatistics::GetSectionStatistic()
|
|||||||
if (!IptvConfig.GetUseBytes())
|
if (!IptvConfig.GetUseBytes())
|
||||||
bitrate *= 8;
|
bitrate *= 8;
|
||||||
// no trailing linefeed here!
|
// no trailing linefeed here!
|
||||||
cString info = cString::sprintf("%4ld (%4ld k%s/s)", numberOfCalls, bitrate,
|
cString s = cString::sprintf("%4ld (%4ld k%s/s)", numberOfCalls, bitrate,
|
||||||
IptvConfig.GetUseBytes() ? "B" : "bit");
|
IptvConfig.GetUseBytes() ? "B" : "bit");
|
||||||
filteredData = numberOfCalls = 0;
|
filteredData = numberOfCalls = 0;
|
||||||
return info;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cIptvSectionStatistics::AddSectionStatistic(long Bytes, long Calls)
|
void cIptvSectionStatistics::AddSectionStatistic(long Bytes, long Calls)
|
||||||
@ -72,26 +72,26 @@ cString cIptvPidStatistics::GetPidStatistic()
|
|||||||
cMutexLock MutexLock(&mutex);
|
cMutexLock MutexLock(&mutex);
|
||||||
uint64_t elapsed = timer.Elapsed(); /* in milliseconds */
|
uint64_t elapsed = timer.Elapsed(); /* in milliseconds */
|
||||||
timer.Set();
|
timer.Set();
|
||||||
cString info("Active pids:\n");
|
cString s("Active pids:\n");
|
||||||
for (unsigned int i = 0; i < IPTV_STATS_ACTIVE_PIDS_COUNT; ++i) {
|
for (unsigned int i = 0; i < IPTV_STATS_ACTIVE_PIDS_COUNT; ++i) {
|
||||||
if (mostActivePids[i].pid) {
|
if (mostActivePids[i].pid) {
|
||||||
long bitrate = elapsed ? (long)(1000.0L * mostActivePids[i].DataAmount / KILOBYTE(1) / elapsed) : 0L;
|
long bitrate = elapsed ? (long)(1000.0L * mostActivePids[i].DataAmount / KILOBYTE(1) / elapsed) : 0L;
|
||||||
if (!IptvConfig.GetUseBytes())
|
if (!IptvConfig.GetUseBytes())
|
||||||
bitrate *= 8;
|
bitrate *= 8;
|
||||||
info = cString::sprintf("%sPid %d: %4d (%4ld k%s/s)\n", *info, i,
|
s = cString::sprintf("%sPid %d: %4d (%4ld k%s/s)\n", *s, i,
|
||||||
mostActivePids[i].pid, bitrate,
|
mostActivePids[i].pid, bitrate,
|
||||||
IptvConfig.GetUseBytes() ? "B" : "bit");
|
IptvConfig.GetUseBytes() ? "B" : "bit");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memset(mostActivePids, '\0', sizeof(mostActivePids));
|
memset(mostActivePids, '\0', sizeof(mostActivePids));
|
||||||
return info;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cIptvPidStatistics::SortPids(const void* data1, const void* data2)
|
int cIptvPidStatistics::SortPids(const void* data1, const void* data2)
|
||||||
{
|
{
|
||||||
//debug("cIptvPidStatistics::SortPids()\n");
|
//debug("cIptvPidStatistics::SortPids()\n");
|
||||||
pidStruct *comp1 = (pidStruct*)data1;
|
const pidStruct *comp1 = reinterpret_cast<const pidStruct*>(data1);
|
||||||
pidStruct *comp2 = (pidStruct*)data2;
|
const pidStruct *comp2 = reinterpret_cast<const pidStruct*>(data2);
|
||||||
if (comp1->DataAmount > comp2->DataAmount)
|
if (comp1->DataAmount > comp2->DataAmount)
|
||||||
return -1;
|
return -1;
|
||||||
if (comp1->DataAmount < comp2->DataAmount)
|
if (comp1->DataAmount < comp2->DataAmount)
|
||||||
@ -148,9 +148,9 @@ cString cIptvStreamerStatistics::GetStreamerStatistic()
|
|||||||
long bitrate = elapsed ? (long)(1000.0L * dataBytes / KILOBYTE(1) / elapsed) : 0L;
|
long bitrate = elapsed ? (long)(1000.0L * dataBytes / KILOBYTE(1) / elapsed) : 0L;
|
||||||
if (!IptvConfig.GetUseBytes())
|
if (!IptvConfig.GetUseBytes())
|
||||||
bitrate *= 8;
|
bitrate *= 8;
|
||||||
cString info = cString::sprintf("%ld k%s/s", bitrate, IptvConfig.GetUseBytes() ? "B" : "bit");
|
cString s = cString::sprintf("%ld k%s/s", bitrate, IptvConfig.GetUseBytes() ? "B" : "bit");
|
||||||
dataBytes = 0;
|
dataBytes = 0;
|
||||||
return info;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cIptvStreamerStatistics::AddStreamerStatistic(long Bytes)
|
void cIptvStreamerStatistics::AddStreamerStatistic(long Bytes)
|
||||||
@ -193,12 +193,12 @@ cString cIptvBufferStatistics::GetBufferStatistic()
|
|||||||
totalKilos *= 8;
|
totalKilos *= 8;
|
||||||
usedKilos *= 8;
|
usedKilos *= 8;
|
||||||
}
|
}
|
||||||
cString info = cString::sprintf("Buffer bitrate: %ld k%s/s\nBuffer usage: %ld/%ld k%s (%2.1f%%)\n", bitrate,
|
cString s = cString::sprintf("Buffer bitrate: %ld k%s/s\nBuffer usage: %ld/%ld k%s (%2.1f%%)\n", bitrate,
|
||||||
IptvConfig.GetUseBytes() ? "B" : "bit", usedKilos, totalKilos,
|
IptvConfig.GetUseBytes() ? "B" : "bit", usedKilos, totalKilos,
|
||||||
IptvConfig.GetUseBytes() ? "B" : "bit", percentage);
|
IptvConfig.GetUseBytes() ? "B" : "bit", percentage);
|
||||||
dataBytes = 0;
|
dataBytes = 0;
|
||||||
usedSpace = 0;
|
usedSpace = 0;
|
||||||
return info;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cIptvBufferStatistics::AddBufferStatistic(long Bytes, long Used)
|
void cIptvBufferStatistics::AddBufferStatistic(long Bytes, long Used)
|
||||||
|
@ -46,7 +46,7 @@ void cIptvStreamer::Action(void)
|
|||||||
while (packetBuffer && Running()) {
|
while (packetBuffer && Running()) {
|
||||||
int length = -1;
|
int length = -1;
|
||||||
if (protocol)
|
if (protocol)
|
||||||
length = protocol->Read(packetBuffer, packetBufferLen);
|
length = protocol->Read(packetBuffer, min((unsigned int)ringBuffer->Free(), packetBufferLen));
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
AddStreamerStatistic(length);
|
AddStreamerStatistic(length);
|
||||||
if (ringBuffer) {
|
if (ringBuffer) {
|
||||||
|
Loading…
Reference in New Issue
Block a user