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 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 | |
35 | MPEG2TransportStreamTrickModeFilter* MPEG2TransportStreamTrickModeFilter |
36 | ::createNew(UsageEnvironment& env, FramedSource* inputSource, |
37 | MPEG2TransportStreamIndexFile* indexFile, int scale) { |
38 | return new MPEG2TransportStreamTrickModeFilter(env, inputSource, indexFile, scale); |
39 | } |
40 | |
41 | MPEG2TransportStreamTrickModeFilter |
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 | |
55 | MPEG2TransportStreamTrickModeFilter::~MPEG2TransportStreamTrickModeFilter() { |
56 | } |
57 | |
58 | Boolean 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 | |
70 | void 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 | |
193 | void MPEG2TransportStreamTrickModeFilter::doStopGettingFrames() { |
194 | FramedFilter::doStopGettingFrames(); |
195 | fIndexFile->stopReading(); |
196 | } |
197 | |
198 | void 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 | |
218 | void 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 | |
228 | void MPEG2TransportStreamTrickModeFilter::readTransportPacket(unsigned long tsPacketNum) { |
229 | seekToTransportPacket(tsPacketNum); |
230 | fInputSource->getNextFrame(fInputBuffer, TRANSPORT_PACKET_SIZE, |
231 | afterGettingFrame, this, |
232 | onSourceClosure, this); |
233 | } |
234 | |
235 | void 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 | |
244 | void 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 | |
258 | void MPEG2TransportStreamTrickModeFilter::onSourceClosure(void* clientData) { |
259 | MPEG2TransportStreamTrickModeFilter* filter = (MPEG2TransportStreamTrickModeFilter*)clientData; |
260 | filter->onSourceClosure1(); |
261 | } |
262 | |
263 | void MPEG2TransportStreamTrickModeFilter::onSourceClosure1() { |
264 | fIndexFile->stopReading(); |
265 | handleClosure(); |
266 | } |
267 | |