Skip to content

Commit 893e9c4

Browse files
committed
initial version with M3U handler
1 parent f7369c9 commit 893e9c4

40 files changed

+10402
-34
lines changed

Makefile

+11-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ PLUGIN = iptv
1414

1515
### The version number of this plugin (taken from the main source file):
1616

17-
VERSION = $(shell grep 'const char VERSION\[\] *=' $(PLUGIN).c | awk '{ print $$5 }' | sed -e 's/[";]//g')
17+
VERSION = $(shell grep 'const char VERSION\[\] *=' $(PLUGIN).cpp | awk '{ print $$5 }' | sed -e 's/[";]//g')
1818
GITTAG = $(shell git describe --always 2>/dev/null)
1919

2020
### The directory environment:
@@ -78,6 +78,7 @@ all-redirect: all
7878
OBJS = $(PLUGIN).o common.o config.o device.o pidscanner.o \
7979
protocolcurl.o protocolext.o protocolfile.o protocolhttp.o \
8080
protocoludp.o sectionfilter.o setup.o sidscanner.o socket.o \
81+
protocolm3u.o m3u8handler.o process.o process_unix.o ffmpeghandler.o \
8182
source.o statistics.o streamer.o
8283

8384
### The main target:
@@ -90,6 +91,10 @@ all: $(SOFILE) i18n
9091
@echo CC $@
9192
$(Q)$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
9293

94+
%.o: %.cpp Makefile
95+
@echo CC $@
96+
$(Q)$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
97+
9398
### Dependencies:
9499

95100
MAKEDEP = $(CXX) -MM -MG
@@ -111,7 +116,11 @@ I18Npot = $(PODIR)/$(PLUGIN).pot
111116
@echo MO $@
112117
$(Q)msgfmt -c -o $@ $<
113118

114-
$(I18Npot): $(wildcard *.c)
119+
#$(I18Npot): $(wildcard *.c)
120+
# @echo GT $@
121+
# $(Q)xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<see README>' -o $@ `ls $^`
122+
123+
$(I18Npot): $(wildcard *.cpp)
115124
@echo GT $@
116125
$(Q)xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<see README>' -o $@ `ls $^`
117126

device.c

+6-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ cIptvDevice::cIptvDevice(unsigned int indexP)
3737
pHttpProtocolM = new cIptvProtocolHttp();
3838
pFileProtocolM = new cIptvProtocolFile();
3939
pExtProtocolM = new cIptvProtocolExt();
40+
pM3UProtocolM = new cIptvProtocolM3U();
4041
pPidScannerM = new cPidScanner();
4142
// Start section handler for iptv device
4243
pIptvSectionM = new cIptvSectionFilterHandler(deviceIndexM, bufsize + 1);
@@ -68,6 +69,7 @@ cIptvDevice::~cIptvDevice()
6869
DELETE_POINTER(pHttpProtocolM);
6970
DELETE_POINTER(pCurlProtocolM);
7071
DELETE_POINTER(pUdpProtocolM);
72+
DELETE_POINTER(pM3UProtocolM);
7173
DELETE_POINTER(tsBufferM);
7274
// Close dvr fifo
7375
if (dvrFdM >= 0) {
@@ -286,14 +288,17 @@ bool cIptvDevice::SetChannelDevice(const cChannel *channelP, bool liveViewP)
286288
case cIptvTransponderParameters::eProtocolEXT:
287289
protocol = pExtProtocolM;
288290
break;
291+
case cIptvTransponderParameters::eProtocolM3U:
292+
protocol = pM3UProtocolM;
293+
break;
289294
default:
290295
error("Unrecognized IPTV protocol: %s", channelP->Parameters());
291296
return false;
292297
break;
293298
}
294299
sidScanEnabledM = itp.SidScan() ? true : false;
295300
pidScanEnabledM = itp.PidScan() ? true : false;
296-
if (pIptvStreamerM && pIptvStreamerM->SetSource(itp.Address(), itp.Parameter(), deviceIndexM, protocol)) {
301+
if (pIptvStreamerM && pIptvStreamerM->SetSource(itp.Address(), itp.Parameter(), deviceIndexM, protocol, channelP->Number())) {
297302
channelM = *channelP;
298303
if (sidScanEnabledM && pSidScannerM && IptvConfig.GetSectionFiltering())
299304
pSidScannerM->SetChannel(channelM.GetChannelID());

device.h

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "protocolhttp.h"
1717
#include "protocolfile.h"
1818
#include "protocolext.h"
19+
#include "protocolm3u.h"
1920
#include "streamer.h"
2021
#include "sectionfilter.h"
2122
#include "pidscanner.h"
@@ -46,6 +47,7 @@ class cIptvDevice : public cDevice, public cIptvPidStatistics, public cIptvBuffe
4647
cIptvProtocolHttp *pHttpProtocolM;
4748
cIptvProtocolFile *pFileProtocolM;
4849
cIptvProtocolExt *pExtProtocolM;
50+
cIptvProtocolM3U *pM3UProtocolM;
4951
cIptvStreamer *pIptvStreamerM;
5052
cIptvSectionFilterHandler *pIptvSectionM;
5153
cPidScanner *pPidScannerM;

ffmpeghandler.c

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#include <string>
2+
#include <chrono>
3+
#include <iterator>
4+
#include "ffmpeghandler.h"
5+
#include "log.h"
6+
7+
FFmpegHandler::FFmpegHandler() {
8+
streamHandler = nullptr;
9+
}
10+
11+
FFmpegHandler::~FFmpegHandler() {
12+
stopVideo();
13+
}
14+
15+
std::vector<std::string> FFmpegHandler::prepareStreamCmd(const m3u_stream& stream) {
16+
// create parameter list
17+
std::vector<std::string> callStr {
18+
"ffmpeg", "-hide_banner", "-re", "-y"
19+
};
20+
21+
// add main input
22+
callStr.emplace_back("-i");
23+
callStr.emplace_back(stream.url);
24+
25+
// add optional audio input
26+
std::vector<std::string> audioMetadata;
27+
for (auto a : stream.audio) {
28+
callStr.emplace_back("-i");
29+
callStr.emplace_back(a.uri);
30+
}
31+
32+
// transmux
33+
callStr.emplace_back("-codec");
34+
callStr.emplace_back("copy");
35+
36+
// main input
37+
if (!stream.audio.empty()) {
38+
callStr.emplace_back("-map");
39+
callStr.emplace_back("0:v");
40+
41+
int idx = 1;
42+
for (auto a: stream.audio) {
43+
callStr.emplace_back("-map");
44+
callStr.emplace_back(std::to_string(idx) + ":a");
45+
idx++;
46+
}
47+
48+
// TODO: Metadata einfügen, sofern vorhanden
49+
callStr.insert(std::end(callStr), std::begin(audioMetadata), std::end(audioMetadata));
50+
}
51+
52+
callStr.emplace_back("-streamid");
53+
callStr.emplace_back("0:" + std::to_string(stream.vpid));
54+
55+
int aidx = 1;
56+
for (auto a: stream.apids) {
57+
callStr.emplace_back("-streamid");
58+
callStr.emplace_back(std::to_string(aidx) + ":" + std::to_string(a));
59+
aidx++;
60+
}
61+
62+
callStr.emplace_back("-f");
63+
callStr.emplace_back("mpegts");
64+
65+
callStr.emplace_back("-mpegts_transport_stream_id");
66+
callStr.emplace_back(std::to_string(stream.tpid));
67+
68+
callStr.emplace_back("-mpegts_pmt_start_pid");
69+
callStr.emplace_back("4096");
70+
71+
callStr.emplace_back("-mpegts_service_id");
72+
callStr.emplace_back(std::to_string(stream.spid));
73+
74+
callStr.emplace_back("-mpegts_original_network_id");
75+
callStr.emplace_back("65281");
76+
77+
callStr.emplace_back("-mpegts_flags");
78+
callStr.emplace_back("system_b");
79+
80+
callStr.emplace_back("-mpegts_flags");
81+
callStr.emplace_back("nit");
82+
83+
callStr.emplace_back("-metadata");
84+
callStr.emplace_back("service_name=" + stream.channelName);
85+
86+
callStr.emplace_back("pipe:1");
87+
88+
std::ostringstream paramOut;
89+
if (!callStr.empty()) {
90+
std::copy(callStr.begin(), callStr.end() - 1, std::ostream_iterator<std::string>(paramOut, " "));
91+
paramOut << callStr.back();
92+
}
93+
94+
debug2("FFmpeg call: %s\n", paramOut.str().c_str());
95+
96+
return callStr;
97+
}
98+
99+
bool FFmpegHandler::streamVideo(const m3u_stream& stream) {
100+
// create parameter list
101+
std::vector<std::string> callStr = prepareStreamCmd(stream);
102+
103+
streamHandler = new TinyProcessLib::Process(callStr, "",
104+
[this](const char *bytes, size_t n) {
105+
debug4("Queue size %ld\n", tsPackets.size());
106+
107+
std::lock_guard<std::mutex> guard(queueMutex);
108+
tsPackets.emplace(bytes, n);
109+
},
110+
111+
[this](const char *bytes, size_t n) {
112+
// TODO: ffmpeg prints many information on stderr
113+
// How to handle this? ignore? filter?
114+
115+
// std::string msg = std::string(bytes, n);
116+
// printf("Error: %s\n", msg.c_str());
117+
},
118+
119+
true
120+
);
121+
122+
return true;
123+
}
124+
125+
void FFmpegHandler::stopVideo() {
126+
if (streamHandler != nullptr) {
127+
streamHandler->kill(true);
128+
streamHandler->get_exit_status();
129+
delete streamHandler;
130+
streamHandler = nullptr;
131+
}
132+
133+
std::queue<std::string> empty;
134+
std::swap(tsPackets, empty);
135+
}
136+
137+
int FFmpegHandler::popPackets(unsigned char* bufferAddrP, unsigned int bufferLenP) {
138+
std::lock_guard<std::mutex> guard(queueMutex);
139+
if (!tsPackets.empty()) {
140+
std::string front = tsPackets.front();
141+
142+
if (bufferLenP < front.size()) {
143+
error("WARNING: BufferLen %u < Size %ld\n", bufferLenP, front.size());
144+
return -1;
145+
}
146+
147+
debug4("Read from queue, size %ld\n", front.size());
148+
149+
memcpy(bufferAddrP, front.data(), front.size());
150+
151+
tsPackets.pop();
152+
return front.size();
153+
}
154+
155+
return -1;
156+
}

ffmpeghandler.h

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <thread>
5+
#include <queue>
6+
#include "process.hpp"
7+
#include "m3u8handler.h"
8+
9+
class FFmpegHandler {
10+
private:
11+
TinyProcessLib::Process *streamHandler;
12+
13+
public:
14+
FFmpegHandler();
15+
~FFmpegHandler();
16+
17+
bool streamVideo(const m3u_stream& stream);
18+
void stopVideo();
19+
20+
int popPackets(unsigned char* bufferAddrP, unsigned int bufferLenP);
21+
22+
private:
23+
std::queue<std::string> tsPackets;
24+
std::mutex queueMutex;
25+
26+
private:
27+
std::vector<std::string> prepareStreamCmd(const m3u_stream& stream);
28+
};

0 commit comments

Comments
 (0)