1/**********
2This library is free software; you can redistribute it and/or modify it under
3the terms of the GNU Lesser General Public License as published by the
4Free Software Foundation; either version 3 of the License, or (at your
5option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7This library is distributed in the hope that it will be useful, but WITHOUT
8ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10more details.
11
12You should have received a copy of the GNU Lesser General Public License
13along with this library; if not, write to the Free Software Foundation, Inc.,
1451 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 filter that converts a MPEG Transport Stream file - with corresponding index file
19// - to a corresponding Video Elementary Stream. It also uses a "scale" parameter
20// to implement 'trick mode' (fast forward or reverse play, using I-frames) on
21// the video stream.
22// Implementation
23
24#include "MPEG2TransportStreamTrickModeFilter.hh"
25#include <ByteStreamFileSource.hh>
26
27// Define the following to be True if we want the output file to have the same frame rate as the original file.
28// (Because the output file contains I-frames only, this means that each I-frame will appear in the output file
29// several times, and therefore the output file's bitrate will be significantly higher than that of the original.)
30// Define the following to be False if we want the output file to include each I-frame no more than once.
31// (This means that - except for high 'scale' values - both the output frame rate and the output bit rate
32// will be less than that of the original.)
33#define KEEP_ORIGINAL_FRAME_RATE False
34
35MPEG2TransportStreamTrickModeFilter* MPEG2TransportStreamTrickModeFilter
36::createNew(UsageEnvironment& env, FramedSource* inputSource,
37 MPEG2TransportStreamIndexFile* indexFile, int scale) {
38 return new MPEG2TransportStreamTrickModeFilter(env, inputSource, indexFile, scale);
39}
40
41MPEG2TransportStreamTrickModeFilter
42::MPEG2TransportStreamTrickModeFilter(UsageEnvironment& env, FramedSource* inputSource,
43 MPEG2TransportStreamIndexFile* indexFile, int scale)
44 : FramedFilter(env, inputSource),
45 fHaveStarted(False), fIndexFile(indexFile), fScale(scale), fDirection(1),
46 fState(SKIPPING_FRAME), fFrameCount(0),
47 fNextIndexRecordNum(0), fNextTSPacketNum(0),
48 fCurrentTSPacketNum((unsigned long)(-1)), fUseSavedFrameNextTime(False) {
49 if (fScale < 0) { // reverse play
50 fScale = -fScale;
51 fDirection = -1;
52 }
53}
54
55MPEG2TransportStreamTrickModeFilter::~MPEG2TransportStreamTrickModeFilter() {
56}
57
58Boolean MPEG2TransportStreamTrickModeFilter::seekTo(unsigned long tsPacketNumber,
59 unsigned long indexRecordNumber) {
60 seekToTransportPacket(tsPacketNumber);
61 fNextIndexRecordNum = indexRecordNumber;
62 return True;
63}
64
65#define isIFrameStart(type) ((type) == 0x81/*actually, a VSH*/ || (type) == 0x85/*actually, a SPS, for H.264*/ || (type) == 0x8B/*actually, a VPS, for H.265*/)
66 // This relies upon I-frames always being preceded by a VSH+GOP (for MPEG-2 data),
67 // by a SPS (for H.264 data), or by a VPS (for H.265 data)
68#define isNonIFrameStart(type) ((type) == 0x83 || (type) == 0x88/*for H.264*/ || (type) == 0x8E/*for H.265*/)
69
70void MPEG2TransportStreamTrickModeFilter::doGetNextFrame() {
71 // fprintf(stderr, "#####DGNF1\n");
72 // If our client's buffer size is too small, then deliver
73 // a 0-byte 'frame', to tell it to process all of the data that it has
74 // already read, before asking for more data from us:
75 if (fMaxSize < TRANSPORT_PACKET_SIZE) {
76 fFrameSize = 0;
77 afterGetting(this);
78 return;
79 }
80
81 while (1) {
82 // Get the next record from our index file.
83 // This tells us the type of frame this data is, which Transport Stream packet
84 // (from the input source) the data comes from, and where in the Transport Stream
85 // packet it comes from:
86 u_int8_t recordType;
87 float recordPCR;
88 Boolean endOfIndexFile = False;
89 if (!fIndexFile->readIndexRecordValues(fNextIndexRecordNum,
90 fDesiredTSPacketNum, fDesiredDataOffset,
91 fDesiredDataSize, recordPCR,
92 recordType)) {
93 // We ran off the end of the index file. If we're not delivering a
94 // pre-saved frame, then handle this the same way as if the
95 // input Transport Stream source ended.
96 if (fState != DELIVERING_SAVED_FRAME) {
97 onSourceClosure1();
98 return;
99 }
100 endOfIndexFile = True;
101 } else if (!fHaveStarted) {
102 fFirstPCR = recordPCR;
103 fHaveStarted = True;
104 }
105 // fprintf(stderr, "#####read index record %ld: ts %ld: %c, PCR %f\n", fNextIndexRecordNum, fDesiredTSPacketNum, isIFrameStart(recordType) ? 'I' : isNonIFrameStart(recordType) ? 'j' : 'x', recordPCR);
106 fNextIndexRecordNum
107 += (fState == DELIVERING_SAVED_FRAME) ? 1 : fDirection;
108
109 // Handle this index record, depending on the record type and our current state:
110 switch (fState) {
111 case SKIPPING_FRAME:
112 case SAVING_AND_DELIVERING_FRAME: {
113 // if (fState == SKIPPING_FRAME) fprintf(stderr, "\tSKIPPING_FRAME\n"); else fprintf(stderr, "\tSAVING_AND_DELIVERING_FRAME\n");//#####
114 if (isIFrameStart(recordType)) {
115 // Save a record of this frame:
116 fSavedFrameIndexRecordStart = fNextIndexRecordNum - fDirection;
117 fUseSavedFrameNextTime = True;
118 // fprintf(stderr, "\trecording\n");//#####
119 if ((fFrameCount++)%fScale == 0 && fUseSavedFrameNextTime) {
120 // A frame is due now.
121 fFrameCount = 1; // reset to avoid overflow
122 if (fDirection > 0) {
123 // Begin delivering this frame, as we're scanning it:
124 fState = SAVING_AND_DELIVERING_FRAME;
125 // fprintf(stderr, "\tdelivering\n");//#####
126 fDesiredDataPCR = recordPCR; // use this frame's PCR
127 attemptDeliveryToClient();
128 return;
129 } else {
130 // Deliver this frame, then resume normal scanning:
131 // (This relies on the index records having begun with an I-frame.)
132 fState = DELIVERING_SAVED_FRAME;
133 fSavedSequentialIndexRecordNum = fNextIndexRecordNum;
134 fDesiredDataPCR = recordPCR;
135 // use this frame's (not the saved frame's) PCR
136 fNextIndexRecordNum = fSavedFrameIndexRecordStart;
137 // fprintf(stderr, "\tbeginning delivery of saved frame\n");//#####
138 }
139 } else {
140 // No frame is needed now:
141 fState = SKIPPING_FRAME;
142 }
143 } else if (isNonIFrameStart(recordType)) {
144 if ((fFrameCount++)%fScale == 0 && fUseSavedFrameNextTime) {
145 // A frame is due now, so begin delivering the one that we had saved:
146 // (This relies on the index records having begun with an I-frame.)
147 fFrameCount = 1; // reset to avoid overflow
148 fState = DELIVERING_SAVED_FRAME;
149 fSavedSequentialIndexRecordNum = fNextIndexRecordNum;
150 fDesiredDataPCR = recordPCR;
151 // use this frame's (not the saved frame's) PCR
152 fNextIndexRecordNum = fSavedFrameIndexRecordStart;
153 // fprintf(stderr, "\tbeginning delivery of saved frame\n");//#####
154 } else {
155 // No frame is needed now:
156 fState = SKIPPING_FRAME;
157 }
158 } else {
159 // Not the start of a frame, but deliver it, if it's needed:
160 if (fState == SAVING_AND_DELIVERING_FRAME) {
161 // fprintf(stderr, "\tdelivering\n");//#####
162 fDesiredDataPCR = recordPCR; // use this frame's PCR
163 attemptDeliveryToClient();
164 return;
165 }
166 }
167 break;
168 }
169 case DELIVERING_SAVED_FRAME: {
170 // fprintf(stderr, "\tDELIVERING_SAVED_FRAME\n");//#####
171 if (endOfIndexFile
172 || (isIFrameStart(recordType)
173 && fNextIndexRecordNum-1 != fSavedFrameIndexRecordStart)
174 || isNonIFrameStart(recordType)) {
175 // fprintf(stderr, "\tended delivery of saved frame\n");//#####
176 // We've reached the end of the saved frame, so revert to the
177 // original sequence of index records:
178 fNextIndexRecordNum = fSavedSequentialIndexRecordNum;
179 fUseSavedFrameNextTime = KEEP_ORIGINAL_FRAME_RATE;
180 fState = SKIPPING_FRAME;
181 } else {
182 // Continue delivering:
183 // fprintf(stderr, "\tdelivering\n");//#####
184 attemptDeliveryToClient();
185 return;
186 }
187 break;
188 }
189 }
190 }
191}
192
193void MPEG2TransportStreamTrickModeFilter::doStopGettingFrames() {
194 FramedFilter::doStopGettingFrames();
195 fIndexFile->stopReading();
196}
197
198void MPEG2TransportStreamTrickModeFilter::attemptDeliveryToClient() {
199 if (fCurrentTSPacketNum == fDesiredTSPacketNum) {
200 // fprintf(stderr, "\t\tdelivering ts %d:%d, %d bytes, PCR %f\n", fCurrentTSPacketNum, fDesiredDataOffset, fDesiredDataSize, fDesiredDataPCR);//#####
201 // We already have the Transport Packet that we want. Deliver its data:
202 memmove(fTo, &fInputBuffer[fDesiredDataOffset], fDesiredDataSize);
203 fFrameSize = fDesiredDataSize;
204 float deliveryPCR = fDirection*(fDesiredDataPCR - fFirstPCR)/fScale;
205 if (deliveryPCR < 0.0) deliveryPCR = 0.0;
206 fPresentationTime.tv_sec = (unsigned long)deliveryPCR;
207 fPresentationTime.tv_usec
208 = (unsigned long)((deliveryPCR - fPresentationTime.tv_sec)*1000000.0f);
209 // fprintf(stderr, "#####DGNF9\n");
210
211 afterGetting(this);
212 } else {
213 // Arrange to read the Transport Packet that we want:
214 readTransportPacket(fDesiredTSPacketNum);
215 }
216}
217
218void MPEG2TransportStreamTrickModeFilter::seekToTransportPacket(unsigned long tsPacketNum) {
219 if (tsPacketNum == fNextTSPacketNum) return; // we're already there
220
221 ByteStreamFileSource* tsFile = (ByteStreamFileSource*)fInputSource;
222 u_int64_t tsPacketNum64 = (u_int64_t)tsPacketNum;
223 tsFile->seekToByteAbsolute(tsPacketNum64*TRANSPORT_PACKET_SIZE);
224
225 fNextTSPacketNum = tsPacketNum;
226}
227
228void MPEG2TransportStreamTrickModeFilter::readTransportPacket(unsigned long tsPacketNum) {
229 seekToTransportPacket(tsPacketNum);
230 fInputSource->getNextFrame(fInputBuffer, TRANSPORT_PACKET_SIZE,
231 afterGettingFrame, this,
232 onSourceClosure, this);
233}
234
235void MPEG2TransportStreamTrickModeFilter
236::afterGettingFrame(void* clientData, unsigned frameSize,
237 unsigned /*numTruncatedBytes*/,
238 struct timeval presentationTime,
239 unsigned /*durationInMicroseconds*/) {
240 MPEG2TransportStreamTrickModeFilter* filter = (MPEG2TransportStreamTrickModeFilter*)clientData;
241 filter->afterGettingFrame1(frameSize);
242}
243
244void MPEG2TransportStreamTrickModeFilter::afterGettingFrame1(unsigned frameSize) {
245 if (frameSize != TRANSPORT_PACKET_SIZE) {
246 // Treat this as if the input source ended:
247 onSourceClosure1();
248 return;
249 }
250
251 fCurrentTSPacketNum = fNextTSPacketNum; // i.e., the one that we just read
252 ++fNextTSPacketNum;
253
254 // Attempt deliver again:
255 attemptDeliveryToClient();
256}
257
258void MPEG2TransportStreamTrickModeFilter::onSourceClosure(void* clientData) {
259 MPEG2TransportStreamTrickModeFilter* filter = (MPEG2TransportStreamTrickModeFilter*)clientData;
260 filter->onSourceClosure1();
261}
262
263void MPEG2TransportStreamTrickModeFilter::onSourceClosure1() {
264 fIndexFile->stopReading();
265 handleClosure();
266}
267