MythTV master
pespacket.cpp
Go to the documentation of this file.
1// -*- Mode: c++ -*-
2// Copyright (c) 2003-2004, Daniel Thor Kristjansson
4#ifndef __cpp_size_t_suffix
6#endif
7#include "pespacket.h"
8#include "mpegtables.h"
9
10extern "C" {
11#include "libmythbase/mythconfig.h"
12#include "libavcodec/avcodec.h"
13#include "libavformat/avformat.h"
14#include "libavutil/crc.h"
15#include "libavutil/bswap.h"
16}
17
18#include <vector>
19#include <map>
20
21// return true if complete or broken
22bool PESPacket::AddTSPacket(const TSPacket* packet, int cardid, bool &broken)
23{
24 broken = true;
25 if (!tsheader()->PayloadStart())
26 {
27 LOG(VB_RECORD, LOG_ERR,
28 "Error: We started a PES packet, without a payloadStart!");
29 return true;
30 }
31 if (!IsClone())
32 {
33 LOG(VB_RECORD, LOG_ERR,
34 "Error: Must clone initially to use addPackets()");
35 return false;
36 }
37
38 const int cc = packet->ContinuityCounter();
39 const int ccExp = (m_ccLast + 1) & 0xf;
40 uint payloadSize = TSPacket::kPayloadSize;
41 uint payloadStart = TSPacket::kHeaderSize;
42
43 // If the next TS has an offset, we need to strip it out.
44 // The offset will be used when a new PESPacket is created.
45 if (packet->PayloadStart())
46 {
47 payloadSize--;
48 payloadStart++;
49 }
50 else
51 {
52 // Skip past the adaptation field if present
53 if (packet->HasAdaptationField())
54 {
55 uint delta = packet->AdaptationFieldSize() + 1;
56
57 // Adaptation field size is max 182 (control is '11') or 183 (control is '10').
58 // Do not skip beyond end of packet when the adaptation field size is wrong.
59 if (delta > TSPacket::kPayloadSize)
60 {
62 LOG(VB_GENERAL, LOG_ERR, QString("PESPacket[%1] Invalid adaptation field size:%2 control:%3")
63 .arg(cardid).arg(packet->AdaptationFieldSize()).arg(packet->AdaptationFieldControl()));
64 }
65 payloadSize -= delta;
66 payloadStart += delta;
67 }
68 }
69
70 if (ccExp == cc)
71 {
72 if (m_pesDataSize + payloadSize >= m_allocSize)
73 {
74 uint sz = (((m_allocSize * 2) + 4095) / 4096) * 4096;
75 unsigned char *nbuf = pes_alloc(sz);
76 memcpy(nbuf, m_fullBuffer, m_pesDataSize);
78 m_fullBuffer = nbuf;
80 m_allocSize = sz;
81 }
82
84 packet->data() + payloadStart,
85 payloadSize);
86
87 m_ccLast = cc;
88 m_pesDataSize += payloadSize;
89 }
90 else if (int(m_ccLast) == cc)
91 {
92 // Do nothing with repeats
93 if (VERBOSE_LEVEL_CHECK(VB_RECORD, LOG_DEBUG))
94 {
95 LOG(VB_RECORD, LOG_ERR,
96 QString("AddTSPacket[%1]: Repeat packet!! ").arg(cardid) +
97 QString("PID: 0x%1, continuity counter: %2 ").arg(packet->PID(),0,16).arg(cc) +
98 QString("(expected %1)").arg(ccExp));
99 }
100 return true;
101 }
102 else
103 {
104 // Even if the packet is out of sync it is still the last packet received
105 m_ccLast = cc;
106
107 LOG(VB_RECORD, LOG_ERR,
108 QString("AddTSPacket[%1]: Out of sync!!! Need to wait for next payloadStart ").arg(cardid) +
109 QString("PID: 0x%1, continuity counter: %2 ").arg(packet->PID(),0,16).arg(cc) +
110 QString("(expected %1)").arg(ccExp));
111 return true;
112 }
113
114 // packet is correct or incomplete
115 broken = false;
116 // check if it's safe to call Length
117 if ((m_psiOffset + 1 + 3) <= m_pesDataSize)
118 {
119 // +3 = first 3 bytes of pespacket header, not included in Length()
120 uint tlen = Length() + (m_pesData - m_fullBuffer) +3;
121
122 if (m_pesDataSize >= tlen)
123 {
124 m_badPacket = !VerifyCRC(cardid, packet->PID());
125 return true;
126 }
127 }
128
129 return false;
130}
131
135void PESPacket::GetAsTSPackets(std::vector<TSPacket> &output, uint cc) const
136{
137 uint last_byte_of_pesdata = Length() + 4 - 1;
138 uint size = last_byte_of_pesdata + m_pesData - m_fullBuffer;
139
140 if (m_pesData == m_fullBuffer)
141 {
142 LOG(VB_GENERAL, LOG_ERR, "WriteAsTSPackets m_pesData == m_fullBuffer");
143 output.resize(0);
144 return;
145 }
146
147 output.resize(1);
149 output[0].data()[3] = (output[0].data()[3] & 0xf0) | cc;
150 if (size <= TSPacket::kSize)
151 return;
152
153 TSHeader header;
154 header.data()[1] = 0x00;
155 header.data()[2] = 0x00;
156 header.data()[3] = 0x10; // adaptation field control == payload only
157 header.SetPID(tsheader()->PID());
158
159 const unsigned char *data = m_fullBuffer + TSPacket::kSize;
160 size -= TSPacket::kSize;
161 while (size > 0)
162 {
163 cc = (cc + 1) & 0xF;
164 header.SetContinuityCounter(cc);
165 output.resize(output.size()+1);
166 output[output.size()-1].InitHeader(header.data());
167 uint write_size = std::min(size, TSPacket::kPayloadSize);
168 output[output.size()-1].InitPayload(data, write_size);
169 data += write_size;
170 size -= write_size;
171 }
172}
173
175{
176 if (Length() < 1)
177 return kTheMagicNoCRCCRC;
178 return av_bswap32(av_crc(av_crc_get_table(AV_CRC_32_IEEE), UINT32_MAX,
179 m_pesData, Length() - 1));
180}
181
182bool PESPacket::VerifyCRC(void) const
183{
184 bool ret = !HasCRC() || (CalcCRC() == CRC());
185 if (!ret)
186 {
187 LOG(VB_SIPARSER, LOG_INFO,
188 QString("PESPacket: Failed CRC check 0x%1 != 0x%2 "
189 "for StreamID = 0x%3")
190 .arg(CRC(),8,16,QLatin1Char('0')).arg(CalcCRC(),8,16,QLatin1Char('0')).arg(StreamID(),0,16));
191 }
192 return ret;
193}
194
195bool PESPacket::VerifyCRC(int cardid, int pid) const
196{
197 bool ret = !HasCRC() || (CalcCRC() == CRC());
198 if (!ret)
199 {
200 LOG(VB_RECORD, LOG_INFO,
201 QString("PESPacket[%1] pid(0x%2): ").arg(cardid).arg(pid,0,16) +
202 QString("Failed CRC check 0x%1 != 0x%2 for ID = 0x%3")
203 .arg(CRC(),8,16,QLatin1Char('0')).arg(CalcCRC(),8,16,QLatin1Char('0')).arg(StreamID(),0,16));
204 }
205 return ret;
206}
207
208// These are pixel aspect ratios
210{
211 0.0000F, 1.0000F, 0.6735F, 0.7031F,
212 0.7615F, 0.8055F, 0.8437F, 0.8935F,
213 0.9157F, 0.9815F, 1.0255F, 1.0695F,
214 1.0950F, 1.1575F, 1.2015F, 0.0000F,
215};
216
220{
221 0.0000F, 1.0000F, -3.0F/4.0F, -9.0F/16.0F,
222 -1.0F/2.21F, 0.0000F, 0.0000F, 0.0000F,
223 0.0000F, 0.0000F, 0.0000F, 0.0000F,
224 0.0000F, 0.0000F, 0.0000F, 0.0000F,
225};
226
228{
229 0.0F, 24000/1001.0F, 24.0F, 25.0F,
230 30000/1001.0F, 30.0F, 50.0F, 60000/1001.0F,
231 60.0F, 1.0F, 1.0F, 1.0F,
232 1.0F, 1.0F, 1.0F, 1.0F,
233};
234
236float SequenceHeader::aspect(bool mpeg1) const
237{
238 if (!height())
239 return 1.0F; // avoid segfaults on broken seq data
240
241 uint index = aspectNum();
242 float aspect = (mpeg1) ? kMpeg1Aspect[index] : kMpeg2Aspect[index];
243
244 float retval = 0.0F;
245 retval = (aspect > 0.0F) ? width() / (aspect * height()) : retval;
246 retval = (aspect < 0.0F) ? -1.0F / aspect : retval;
247 retval = (retval <= 0.0F) ? width() * 1.0F / height() : retval;
248 return retval;
249}
250
251
252
254// Memory allocator to avoid malloc global lock and waste less memory. //
256
257#if !CONFIG_VALGRIND
258static constexpr size_t BLOCKS188 { 512 };
259#ifdef __cpp_size_t_suffix
260using block188 = std::array<uint8_t, 188UZ>;
261#else
262using block188 = std::array<uint8_t, 188_UZ>;
263#endif
265 public:
266 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,modernize-use-equals-default)
267 groupof188s() {}; // // constructor doesn't initialize anything
268
269 std::array<block188, BLOCKS188> blocks;
270};
271
272static constexpr size_t BLOCKS4096 { 128 };
273#ifdef __cpp_size_t_suffix
274using block4096 = std::array<uint8_t, 4096UZ>;
275#else
276using block4096 = std::array<uint8_t, 4096_UZ>;
277#endif
279 public:
280 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,modernize-use-equals-default)
281 groupof4096s() {}; // constructor doesn't initialize anything
282
283 std::array<block4096, BLOCKS4096> blocks;
284};
285
286
287static std::vector<groupof188s*> mem188;
288static std::vector<unsigned char*> free188;
289static std::map<unsigned char*, bool> alloc188;
290
291static std::vector<groupof4096s*> mem4096;
292static std::vector<unsigned char*> free4096;
293static std::map<unsigned char*, bool> alloc4096;
294
295
296static unsigned char* get_188_block()
297{
298 if (free188.empty())
299 {
300 mem188.push_back(new groupof188s);
301 free188.reserve(BLOCKS188);
302 for (block188 &b : mem188.back()->blocks)
303 free188.push_back(b.data());
304 }
305
306 unsigned char *ptr = free188.back();
307 free188.pop_back();
308 alloc188[ptr] = true;
309 return ptr;
310}
311
312static bool is_188_block(unsigned char* ptr)
313{
314 return alloc188.contains(ptr);
315}
316
317static void return_188_block(unsigned char* ptr)
318{
319 alloc188.erase(ptr);
320 free188.push_back(ptr);
321 // free the allocator only if more than 1 block was used
322 if (alloc188.empty() && mem188.size() > 1)
323 {
324 for (auto *b : mem188)
325 delete b;
326 mem188.clear();
327 free188.clear();
328#if 0
329 LOG(VB_GENERAL, LOG_DEBUG, "freeing all 188 blocks");
330#endif
331 }
332}
333
334static unsigned char* get_4096_block()
335{
336 if (free4096.empty())
337 {
338 mem4096.push_back(new groupof4096s);
339 free4096.reserve(BLOCKS4096);
340 for (block4096 &b : mem4096.back()->blocks)
341 free4096.push_back(b.data());
342 }
343
344 unsigned char *ptr = free4096.back();
345 free4096.pop_back();
346 alloc4096[ptr] = true;
347 return ptr;
348}
349
350static bool is_4096_block(unsigned char* ptr)
351{
352 return alloc4096.contains(ptr);
353}
354
355static void return_4096_block(unsigned char* ptr)
356{
357 alloc4096.erase(ptr);
358 free4096.push_back(ptr);
359
360#if 0 // enable this to debug memory leaks
361 LOG(VB_GENERAL, LOG_DEBUG, QString("%1 4096 blocks remain")
362 .arg(alloc4096.size()));
363 map<unsigned char*, bool>::iterator it;
364 for (it = alloc4096.begin(); it != alloc4096.end(); ++it)
365 {
366 TSPacket *ts = (TSPacket*) it->first;
367 LGO(VB_GENERAL, LOG_DEBUG, QString("PES Packet: pid(0x%1)")
368 .arg(ts->PID(),0,16));
369 if (ts->PID() == 0x1ffb)
370 {
371 LOG(VB_GENERAL, LOG_DEBUG, QString(" tid(0x%1) ext(0x%2)")
372 .arg(PSIPTable(*ts).TableID(),0,16)
373 .arg(PSIPTable(*ts).TableIDExtension(),0,16));
374 }
375 }
376#endif
377
378 // free the allocator only if more than 1 block was used
379 if (alloc4096.empty() && mem4096.size() > 1)
380 {
381 for (auto *b : mem4096)
382 delete b;
383 mem4096.clear();
384 free4096.clear();
385#if 0
386 LOG(VB_GENERAL, LOG_DEBUG, "freeing all 4096 blocks");
387#endif
388 }
389}
390#endif
391
392static QMutex pes_alloc_mutex;
393
394unsigned char *pes_alloc(uint size)
395{
396 QMutexLocker locker(&pes_alloc_mutex);
397#if !CONFIG_VALGRIND
398 if (size <= 188)
399 return get_188_block();
400 if (size <= 4096)
401 return get_4096_block();
402#endif // CONFIG_VALGRIND
403 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
404 return (unsigned char*) malloc(size);
405}
406
407void pes_free(unsigned char *ptr)
408{
409 QMutexLocker locker(&pes_alloc_mutex);
410#if !CONFIG_VALGRIND
411 if (is_188_block(ptr))
412 return_188_block(ptr);
413 else if (is_4096_block(ptr))
415 else
416#endif // CONFIG_VALGRIND
417 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
418 free(ptr);
419}
bool VerifyCRC(void) const
Definition: pespacket.cpp:182
bool AddTSPacket(const TSPacket *tspacket, int cardid, bool &broken)
Definition: pespacket.cpp:22
const unsigned char * data() const
Definition: pespacket.h:167
static const uint kTheMagicNoCRCCRC
Definition: pespacket.h:230
unsigned char * m_pesData
Pointer to PES data in full buffer.
Definition: pespacket.h:219
uint CalcCRC(void) const
Definition: pespacket.cpp:174
uint m_psiOffset
AFCOffset + StartOfFieldPointer.
Definition: pespacket.h:222
uint m_pesDataSize
Number of data bytes (TS header + PES data)
Definition: pespacket.h:224
void GetAsTSPackets(std::vector< TSPacket > &output, uint cc) const
Returns payload only PESPacket as series of TSPackets.
Definition: pespacket.cpp:135
const TSHeader * tsheader() const
Definition: pespacket.h:90
uint m_allocSize
Total number of bytes we allocated.
Definition: pespacket.h:225
bool m_badPacket
true if a CRC is not good yet
Definition: pespacket.h:226
virtual bool HasCRC() const
1 bit Cyclic Redundancy Check present
Definition: pespacket.h:130
uint StreamID() const
Definition: pespacket.h:98
unsigned char * m_fullBuffer
Pointer to allocated data.
Definition: pespacket.h:220
uint m_ccLast
Continuity counter of last inserted TS Packet.
Definition: pespacket.h:223
bool IsClone() const
Definition: pespacket.h:83
uint CRC(void) const
Definition: pespacket.h:189
uint Length() const
Definition: pespacket.h:99
A PSIP table is a variant of a PES packet containing an MPEG, ATSC or DVB table.
Definition: mpegtables.h:410
uint TableID(void) const
Definition: mpegtables.h:496
uint TableIDExtension(void) const
Definition: mpegtables.h:515
uint width(void) const
Definition: pespacket.h:239
static const AspectArray kMpeg2Aspect
The negative values are screen aspect ratios, while the positive ones are pixel aspect ratios.
Definition: pespacket.h:252
static const AspectArray kMpeg1Aspect
Definition: pespacket.h:251
float aspect(bool mpeg1) const
Returns the screen aspect ratio.
Definition: pespacket.cpp:236
uint height(void) const
Definition: pespacket.h:240
uint aspectNum(void) const
Definition: pespacket.h:241
static const AspectArray kMpeg2Fps
Definition: pespacket.h:253
Used to access header of a TSPacket.
Definition: tspacket.h:47
bool HasAdaptationField(void) const
Definition: tspacket.h:113
unsigned int ContinuityCounter(void) const
Definition: tspacket.h:109
unsigned int PID(void) const
Definition: tspacket.h:93
static constexpr unsigned int kHeaderSize
Definition: tspacket.h:177
bool PayloadStart(void) const
Definition: tspacket.h:89
unsigned int AdaptationFieldControl(void) const
Definition: tspacket.h:103
void SetContinuityCounter(unsigned int cc)
Definition: tspacket.h:170
void SetPID(unsigned int pid)
Definition: tspacket.h:160
const unsigned char * data(void) const
Definition: tspacket.h:174
size_t AdaptationFieldSize(void) const
Definition: tspacket.h:114
Used to access the data of a Transport Stream packet.
Definition: tspacket.h:208
static constexpr unsigned int kPayloadSize
Definition: tspacket.h:262
static constexpr unsigned int kSize
Definition: tspacket.h:261
std::array< block188, BLOCKS188 > blocks
Definition: pespacket.cpp:269
std::array< block4096, BLOCKS4096 > blocks
Definition: pespacket.cpp:283
unsigned int uint
Definition: compat.h:60
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
std::array< uint8_t, 4096_UZ > block4096
Definition: pespacket.cpp:276
static bool is_188_block(unsigned char *ptr)
Definition: pespacket.cpp:312
void pes_free(unsigned char *ptr)
Definition: pespacket.cpp:407
static std::vector< unsigned char * > free188
Definition: pespacket.cpp:288
static std::vector< groupof4096s * > mem4096
Definition: pespacket.cpp:291
static unsigned char * get_188_block()
Definition: pespacket.cpp:296
static void return_4096_block(unsigned char *ptr)
Definition: pespacket.cpp:355
static bool is_4096_block(unsigned char *ptr)
Definition: pespacket.cpp:350
static std::vector< groupof188s * > mem188
Definition: pespacket.cpp:287
static constexpr size_t BLOCKS188
Definition: pespacket.cpp:258
static std::map< unsigned char *, bool > alloc188
Definition: pespacket.cpp:289
static std::map< unsigned char *, bool > alloc4096
Definition: pespacket.cpp:293
static std::vector< unsigned char * > free4096
Definition: pespacket.cpp:292
static constexpr size_t BLOCKS4096
Definition: pespacket.cpp:272
static unsigned char * get_4096_block()
Definition: pespacket.cpp:334
unsigned char * pes_alloc(uint size)
Definition: pespacket.cpp:394
static QMutex pes_alloc_mutex
Definition: pespacket.cpp:392
std::array< uint8_t, 188_UZ > block188
Definition: pespacket.cpp:262
static void return_188_block(unsigned char *ptr)
Definition: pespacket.cpp:317
std::array< float, 16 > AspectArray
Definition: pespacket.h:13
#define output