1 | /********** |
2 | This library is free software; you can redistribute it and/or modify it under |
3 | the terms of the GNU Lesser General Public License as published by the |
4 | Free Software Foundation; either version 3 of the License, or (at your |
5 | option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
6 | |
7 | This library is distributed in the hope that it will be useful, but WITHOUT |
8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
9 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
10 | more details. |
11 | |
12 | You should have received a copy of the GNU Lesser General Public License |
13 | along with this library; if not, write to the Free Software Foundation, Inc., |
14 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
15 | **********/ |
16 | // "liveMedia" |
17 | // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. |
18 | // A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s |
19 | // on demand, from a MPEG-1 or 2 demuxer. |
20 | // Implementation |
21 | |
22 | #include "MPEG1or2DemuxedServerMediaSubsession.hh" |
23 | #include "MPEG1or2AudioStreamFramer.hh" |
24 | #include "MPEG1or2AudioRTPSink.hh" |
25 | #include "MPEG1or2VideoStreamFramer.hh" |
26 | #include "MPEG1or2VideoRTPSink.hh" |
27 | #include "AC3AudioStreamFramer.hh" |
28 | #include "AC3AudioRTPSink.hh" |
29 | #include "ByteStreamFileSource.hh" |
30 | |
31 | MPEG1or2DemuxedServerMediaSubsession* MPEG1or2DemuxedServerMediaSubsession |
32 | ::createNew(MPEG1or2FileServerDemux& demux, u_int8_t streamIdTag, |
33 | Boolean reuseFirstSource, Boolean iFramesOnly, double vshPeriod) { |
34 | return new MPEG1or2DemuxedServerMediaSubsession(demux, streamIdTag, |
35 | reuseFirstSource, |
36 | iFramesOnly, vshPeriod); |
37 | } |
38 | |
39 | MPEG1or2DemuxedServerMediaSubsession |
40 | ::MPEG1or2DemuxedServerMediaSubsession(MPEG1or2FileServerDemux& demux, |
41 | u_int8_t streamIdTag, Boolean reuseFirstSource, |
42 | Boolean iFramesOnly, double vshPeriod) |
43 | : OnDemandServerMediaSubsession(demux.envir(), reuseFirstSource), |
44 | fOurDemux(demux), fStreamIdTag(streamIdTag), |
45 | fIFramesOnly(iFramesOnly), fVSHPeriod(vshPeriod) { |
46 | } |
47 | |
48 | MPEG1or2DemuxedServerMediaSubsession::~MPEG1or2DemuxedServerMediaSubsession() { |
49 | } |
50 | |
51 | FramedSource* MPEG1or2DemuxedServerMediaSubsession |
52 | ::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) { |
53 | FramedSource* es = NULL; |
54 | do { |
55 | es = fOurDemux.newElementaryStream(clientSessionId, fStreamIdTag); |
56 | if (es == NULL) break; |
57 | |
58 | if ((fStreamIdTag&0xF0) == 0xC0 /*MPEG audio*/) { |
59 | estBitrate = 128; // kbps, estimate |
60 | return MPEG1or2AudioStreamFramer::createNew(envir(), es); |
61 | } else if ((fStreamIdTag&0xF0) == 0xE0 /*video*/) { |
62 | estBitrate = 500; // kbps, estimate |
63 | return MPEG1or2VideoStreamFramer::createNew(envir(), es, |
64 | fIFramesOnly, fVSHPeriod); |
65 | } else if (fStreamIdTag == 0xBD /*AC-3 audio*/) { |
66 | estBitrate = 192; // kbps, estimate |
67 | return AC3AudioStreamFramer::createNew(envir(), es, 0x80); |
68 | } else { // unknown stream type |
69 | break; |
70 | } |
71 | } while (0); |
72 | |
73 | // An error occurred: |
74 | Medium::close(es); |
75 | return NULL; |
76 | } |
77 | |
78 | RTPSink* MPEG1or2DemuxedServerMediaSubsession |
79 | ::createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, |
80 | FramedSource* inputSource) { |
81 | if ((fStreamIdTag&0xF0) == 0xC0 /*MPEG audio*/) { |
82 | return MPEG1or2AudioRTPSink::createNew(envir(), rtpGroupsock); |
83 | } else if ((fStreamIdTag&0xF0) == 0xE0 /*video*/) { |
84 | return MPEG1or2VideoRTPSink::createNew(envir(), rtpGroupsock); |
85 | } else if (fStreamIdTag == 0xBD /*AC-3 audio*/) { |
86 | // Get the sampling frequency from the audio source; use it for the RTP frequency: |
87 | AC3AudioStreamFramer* audioSource |
88 | = (AC3AudioStreamFramer*)inputSource; |
89 | return AC3AudioRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic, |
90 | audioSource->samplingRate()); |
91 | } else { |
92 | return NULL; |
93 | } |
94 | } |
95 | |
96 | void MPEG1or2DemuxedServerMediaSubsession |
97 | ::seekStreamSource(FramedSource* inputSource, double& seekNPT, double /*streamDuration*/, u_int64_t& /*numBytes*/) { |
98 | float const dur = duration(); |
99 | unsigned const size = fOurDemux.fileSize(); |
100 | unsigned absBytePosition = dur == 0.0 ? 0 : (unsigned)((seekNPT/dur)*size); |
101 | |
102 | // "inputSource" is a 'framer' |
103 | // Flush its data, to account for the seek that we're about to do: |
104 | if ((fStreamIdTag&0xF0) == 0xC0 /*MPEG audio*/) { |
105 | MPEG1or2AudioStreamFramer* framer = (MPEG1or2AudioStreamFramer*)inputSource; |
106 | framer->flushInput(); |
107 | } else if ((fStreamIdTag&0xF0) == 0xE0 /*video*/) { |
108 | MPEG1or2VideoStreamFramer* framer = (MPEG1or2VideoStreamFramer*)inputSource; |
109 | framer->flushInput(); |
110 | } |
111 | |
112 | // "inputSource" is a filter; its input source is the original elem stream source: |
113 | MPEG1or2DemuxedElementaryStream* elemStreamSource |
114 | = (MPEG1or2DemuxedElementaryStream*)(((FramedFilter*)inputSource)->inputSource()); |
115 | |
116 | // Next, get the original source demux: |
117 | MPEG1or2Demux& sourceDemux = elemStreamSource->sourceDemux(); |
118 | |
119 | // and flush its input buffers: |
120 | sourceDemux.flushInput(); |
121 | |
122 | // Then, get the original input file stream from the source demux: |
123 | ByteStreamFileSource* inputFileSource |
124 | = (ByteStreamFileSource*)(sourceDemux.inputSource()); |
125 | // Note: We can make that cast, because we know that the demux was originally |
126 | // created from a "ByteStreamFileSource". |
127 | |
128 | // Do the appropriate seek within the input file stream: |
129 | inputFileSource->seekToByteAbsolute(absBytePosition); |
130 | } |
131 | |
132 | float MPEG1or2DemuxedServerMediaSubsession::duration() const { |
133 | return fOurDemux.fileDuration(); |
134 | } |
135 | |