MythTV master
mpeg2fix.cpp
Go to the documentation of this file.
1//To Do
2//support missing audio frames
3//support analyze-only mode
4
5// C++ headers
6#include <cstdint>
7#include <cstdio>
8#include <cstdlib>
9#include <fcntl.h>
10#include <getopt.h>
11#include <sys/stat.h>
12#include <unistd.h>
13#include <utility>
14
15// Qt headers
16#include <QtGlobal>
17#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
18#include <QtSystemDetection>
19#endif
20#include <QFileInfo>
21#include <QList>
22#include <QMap>
23#include <QQueue>
24
25// MythTV headers
26#include "libmythbase/mythconfig.h"
27
28#include "libmythbase/mthread.h"
32
33extern "C" {
34#include "libavutil/cpu.h"
35#include "libmythmpeg2/attributes.h" // for ATTR_ALIGN() in mpeg2_internal.h
36#include "libmythmpeg2/mpeg2.h" // for mpeg2_decoder_t, mpeg2_fbuf_t, et c
37#include "libmythmpeg2/mpeg2_internal.h"
38}
39
40// MythTranscode
41#include "mpeg2fix.h"
42
43#ifdef Q_OS_WINDOWS
44#include <winsock2.h>
45#else
46#include <netinet/in.h>
47#endif
48
49#ifndef O_LARGEFILE
50#define O_LARGEFILE 0
51#endif
52
53static void my_av_print([[maybe_unused]] void *ptr,
54 int level, const char* fmt, va_list vl)
55{
56 static QString s_fullLine("");
57
58 if (level > AV_LOG_INFO)
59 return;
60
61 s_fullLine += QString::vasprintf(fmt, vl);
62 if (s_fullLine.endsWith("\n"))
63 {
64 s_fullLine.chop(1);
65 LOG(VB_GENERAL, LOG_INFO, s_fullLine);
66 s_fullLine = QString("");
67 }
68}
69
70static QString PtsTime(int64_t pts)
71{
72 bool is_neg = false;
73 if (pts < 0)
74 {
75 pts = -pts;
76 is_neg = true;
77 }
78 return QString("%1%2:%3:%4.%5")
79 .arg(is_neg ? "-" : "")
80 .arg((uint)(pts / 90000.) / 3600, 2, 10, QChar('0'))
81 .arg(((uint)(pts / 90000.) % 3600) / 60, 2, 10, QChar('0'))
82 .arg(((uint)(pts / 90000.) % 3600) % 60, 2, 10, QChar('0'))
83 .arg(((((uint)(pts / 90.) % 3600000) % 60000) % 1000), 3, 10, QChar('0'));
84}
85
87 m_pkt(av_packet_alloc()),
88 m_mpeg2_seq(), m_mpeg2_gop(), m_mpeg2_pic()
89{
90 av_new_packet(m_pkt, size);
91}
92
94{
95 av_packet_free(&m_pkt);
96}
97
98void MPEG2frame::ensure_size(int size) const
99{
100 if (m_pkt->size < size)
101 {
102 int oldSize = m_pkt->size;
103 if ((av_grow_packet(m_pkt, size - m_pkt->size) < 0) || m_pkt->size < size)
104 {
105 LOG(VB_GENERAL, LOG_CRIT, QString("MPEG2frame::ensure_size(): "
106 "Failed to grow packet size "
107 "from %1 to %2, result was %3")
108 .arg(oldSize).arg(size)
109 .arg(m_pkt->size));
110 }
111 }
112}
113
114void MPEG2frame::set_pkt(AVPacket *newpkt) const
115{
116 // TODO: Don't free + copy, attempt to re-use existing buffer
117 av_packet_unref(m_pkt);
118 av_packet_ref(m_pkt, newpkt);
119}
120
121PTSOffsetQueue::PTSOffsetQueue(int vidid, QList<int> keys, int64_t initPTS)
122 : m_keyList(std::move(keys)),
123 m_vidId(vidid)
124{
125 poq_idx_t idx {};
126 m_keyList.append(m_vidId);
127
128 idx.newPTS = initPTS;
129 idx.pos_pts = 0;
130 idx.framenum = 0;
131 idx.type = false;
132
133 for (const int key : std::as_const(m_keyList))
134 m_offset[key].push_back(idx);
135}
136
137int64_t PTSOffsetQueue::Get(int idx, AVPacket *pkt)
138{
139 QList<poq_idx_t>::iterator it;
140 int64_t value = m_offset[idx].first().newPTS;
141 bool done = false;
142
143 if (!pkt)
144 return value;
145
146 //Be aware: the key for offset can be either a file position OR a PTS
147 //The type is defined by type (0==PTS, 1==Pos)
148 while (m_offset[idx].count() > 1 && !done)
149 {
150 it = ++m_offset[idx].begin();
151 if (((static_cast<int>((*it).type) == 0) &&
152 (pkt->pts >= (*it).pos_pts) /* PTS type */) ||
153 (((*it).type) /* Pos type */ &&
154 ((pkt->pos >= (*it).pos_pts) || (pkt->duration > (*it).framenum))))
155 {
156 m_offset[idx].pop_front();
157 value = m_offset[idx].first().newPTS;
158 }
159 else
160 {
161 done = true;
162 }
163 }
164 return value;
165}
166
167void PTSOffsetQueue::SetNextPTS(int64_t newPTS, int64_t atPTS)
168{
169 poq_idx_t idx {};
170
171 idx.newPTS = newPTS;
172 idx.pos_pts = atPTS;
173 idx.type = false;
174 idx.framenum = 0;
175
176 for (const int key : std::as_const(m_keyList))
177 m_offset[key].push_back(idx);
178}
179
180void PTSOffsetQueue::SetNextPos(int64_t newPTS, AVPacket *pkt)
181{
182 int64_t delta = MPEG2fixup::diff2x33(newPTS, m_offset[m_vidId].last().newPTS);
183 poq_idx_t idx {};
184
185 idx.pos_pts = pkt->pos;
186 idx.framenum = pkt->duration;
187 idx.type = true;
188
189 LOG(VB_FRAME, LOG_INFO, QString("Offset %1 -> %2 (%3) at %4")
190 .arg(PtsTime(m_offset[m_vidId].last().newPTS),
191 PtsTime(newPTS),
192 PtsTime(delta), QString::number(pkt->pos)));
193 for (const int key : std::as_const(m_keyList))
194 {
195 idx.newPTS = newPTS;
196 m_offset[key].push_back(idx);
197 idx.newPTS = delta;
198 m_orig[key].push_back(idx);
199 }
200}
201
202int64_t PTSOffsetQueue::UpdateOrigPTS(int idx, int64_t &origPTS, AVPacket *pkt)
203{
204 int64_t delta = 0;
205 QList<poq_idx_t> *dltaList = &m_orig[idx];
206 while (!dltaList->isEmpty() &&
207 (pkt->pos >= dltaList->first().pos_pts ||
208 pkt->duration > dltaList->first().framenum))
209 {
210 if (dltaList->first().newPTS >= 0)
211 ptsinc((uint64_t *)&origPTS, 300 * dltaList->first().newPTS);
212 else
213 ptsdec((uint64_t *)&origPTS, -300 * dltaList->first().newPTS);
214 delta += dltaList->first().newPTS;
215 dltaList->pop_front();
216 LOG(VB_PROCESS, LOG_INFO,
217 QString("Moving PTS offset of stream %1 by %2")
218 .arg(idx).arg(PtsTime(delta)));
219 }
220 return (delta);
221}
222
223MPEG2fixup::MPEG2fixup(const QString &inf, const QString &outf,
224 frm_dir_map_t *deleteMap,
225 const char *fmt, bool norp, bool fixPTS, int maxf,
226 bool showprog, int otype, void (*update_func)(float),
227 int (*check_func)())
228 : m_noRepeat(norp), m_fixPts(fixPTS), m_maxFrames(maxf),
229 m_infile(inf), m_format(fmt)
230{
231 m_rx.m_outfile = outf;
232 m_rx.m_done = 0;
233 m_rx.m_otype = otype;
234 if (deleteMap && !deleteMap->isEmpty())
235 {
236 /* convert MythTV cutlist to mpeg2fix cutlist */
237 frm_dir_map_t::iterator it = deleteMap->begin();
238 for (; it != deleteMap->end(); ++it)
239 {
240 uint64_t mark = it.key();
241 if (mark > 0)
242 {
243 if (it.value() == MARK_CUT_START) // NOLINT(bugprone-branch-clone)
244 mark += 1; // +2 looks good, but keyframes are hit with +1
245 else
246 mark += 1;
247 }
248 m_delMap.insert (mark, it.value());
249 }
250
251 if (m_delMap.contains(0))
252 {
253 m_discard = true;
254 m_delMap.remove(0);
255 }
256 if (m_delMap.begin().value() == MARK_CUT_END)
257 m_discard = true;
258 m_useSecondary = true;
259 }
260
261 m_headerDecoder = mpeg2_init();
262 m_imgDecoder = mpeg2_init();
263
264 av_log_set_callback(my_av_print);
265
266 pthread_mutex_init(&m_rx.m_mutex, nullptr);
267 pthread_cond_init(&m_rx.m_cond, nullptr);
268
269 //await multiplexer initialization (prevent a deadlock race)
270 pthread_mutex_lock(&m_rx.m_mutex);
271 pthread_create(&m_thread, nullptr, ReplexStart, this);
272 pthread_cond_wait(&m_rx.m_cond, &m_rx.m_mutex);
273 pthread_mutex_unlock(&m_rx.m_mutex);
274
275 //initialize progress stats
276 m_showProgress = showprog;
277 m_updateStatus = update_func;
278 m_checkAbort = check_func;
280 {
281 if (m_updateStatus)
282 {
285 }
286 else
287 {
289 }
292
293 const QFileInfo finfo(inf);
294 m_fileSize = finfo.size();
295 }
296}
297
299{
300 mpeg2_close(m_headerDecoder);
301 mpeg2_close(m_imgDecoder);
302
303 if (m_inputFC)
304 avformat_close_input(&m_inputFC);
305 av_frame_free(&m_picture);
306
307 while (!m_vFrame.isEmpty())
308 {
309 MPEG2frame *tmpFrame = m_vFrame.takeFirst();
310 delete tmpFrame;
311 }
312
313 while (!m_vSecondary.isEmpty())
314 {
315 MPEG2frame *tmpFrame = m_vSecondary.takeFirst();
316 delete tmpFrame;
317 }
318
319 for (auto *af : std::as_const(m_aFrame))
320 {
321 while (!af->isEmpty())
322 {
323 MPEG2frame *tmpFrame = af->takeFirst();
324 delete tmpFrame;
325 }
326 delete af;
327 }
328
329 while (!m_framePool.isEmpty())
330 delete m_framePool.dequeue();
331}
332
333//#define MPEG2trans_DEBUG
334static constexpr bool MATCH_HEADER(const uint8_t *ptr)
335 { return (ptr[0] == 0x00) && (ptr[1] == 0x00) && (ptr[2] == 0x01); };
336
337static void SETBITS(unsigned char *ptr, long value, int num)
338{
339 static int s_sbPos = 0;
340 static unsigned char *s_sbPtr = nullptr;
341
342 if (ptr != nullptr)
343 {
344 s_sbPtr = ptr;
345 s_sbPos = 0;
346 }
347
348 if (s_sbPtr == nullptr)
349 return;
350
351 int offset = s_sbPos >> 3;
352 int offset_r = s_sbPos & 0x07;
353 int offset_b = 32 - offset_r;
354 uint32_t mask = ~(((1 << num) - 1) << (offset_b - num));
355 uint32_t sb_long = ntohl(*((uint32_t *) (s_sbPtr + offset)));
356 value = value << (offset_b - num);
357 sb_long = (sb_long & mask) + value;
358 *((uint32_t *)(s_sbPtr + offset)) = htonl(sb_long);
359}
360
361void MPEG2fixup::dec2x33(int64_t *pts1, int64_t pts2)
362{
363 *pts1 = udiff2x33(*pts1, pts2);
364}
365
366void MPEG2fixup::inc2x33(int64_t *pts1, int64_t pts2)
367{
368 *pts1 = (*pts1 + pts2) % MAX_PTS;
369}
370
371int64_t MPEG2fixup::udiff2x33(int64_t pts1, int64_t pts2)
372{
373 int64_t diff = pts1 - pts2;
374
375 if (diff < 0)
376 {
377 diff = MAX_PTS + diff;
378 }
379 return (diff % MAX_PTS);
380}
381
382int64_t MPEG2fixup::diff2x33(int64_t pts1, int64_t pts2)
383{
384 switch (cmp2x33(pts1, pts2))
385 {
386 case 0:
387 return 0;
388 break;
389
390 case 1:
391 case -2:
392 return (pts1 - pts2);
393 break;
394
395 case 2:
396 return (pts1 + MAX_PTS - pts2);
397 break;
398
399 case -1:
400 return (pts1 - (pts2 + MAX_PTS));
401 break;
402 }
403
404 return 0;
405}
406
407int64_t MPEG2fixup::add2x33(int64_t pts1, int64_t pts2)
408{
409 int64_t tmp = pts1 + pts2;
410 if (tmp >= 0)
411 return (pts1 + pts2) % MAX_PTS;
412 return (tmp + MAX_PTS);
413}
414
415int MPEG2fixup::cmp2x33(int64_t pts1, int64_t pts2)
416{
417 int ret = 0;
418
419 if (pts1 > pts2)
420 {
421 if ((uint64_t)(pts1 - pts2) > MAX_PTS/2ULL)
422 ret = -1;
423 else
424 ret = 1;
425 }
426 else if (pts1 == pts2)
427 {
428 ret = 0;
429 }
430 else
431 {
432 if ((uint64_t)(pts2 - pts1) > MAX_PTS/2ULL)
433 ret = 2;
434 else
435 ret = -2;
436 }
437 return ret;
438}
439
440int MPEG2fixup::FindMPEG2Header(const uint8_t *buf, int size, uint8_t code)
441{
442 for (int i = 0; i < size; i++)
443 {
444 if (MATCH_HEADER(buf + i) && buf[i + 3] == code)
445 return i;
446 }
447
448 return 0;
449}
450
451//fill_buffers will signal the main thread to start decoding again as soon
452//as it runs out of buffers. It will then wait for the buffer to completely
453//fill before returning. In this way, the 2 threads are never running
454// concurrently
455static int fill_buffers(void *r, int finish)
456{
457 auto *rx = (MPEG2replex *)r;
458
459 if (finish)
460 return 0;
461
462 return (rx->WaitBuffers());
463}
464
466{
467 if (m_vrBuf.size)
469 if (m_indexVrbuf.size)
471
472 for (int i = 0; i < m_extCount; i++)
473 {
474 if (m_extrbuf[i].size)
476 if (m_indexExtrbuf[i].size)
478 }
479}
480
482{
483 pthread_mutex_lock( &m_mutex );
484 while (true)
485 {
486 int ok = 1;
487
488 if (ring_avail(&m_indexVrbuf) < sizeof(index_unit))
489 ok = 0;
490
491 for (int i = 0; i < m_extCount; i++)
492 if (ring_avail(&m_indexExtrbuf[i]) < sizeof(index_unit))
493 ok = 0;
494
495 if (ok || m_done)
496 break;
497
498 pthread_cond_signal(&m_cond);
499 pthread_cond_wait(&m_cond, &m_mutex);
500 }
501 pthread_mutex_unlock(&m_mutex);
502
503 if (m_done)
504 {
506 // mythtv#244: thread exit must return static, not stack
507 static int errorcount = 0;
508 errorcount = m_mplex->error;
509 if (m_mplex->error) {
510 LOG(VB_GENERAL, LOG_ERR,
511 QString("thread finished with %1 write errors")
512 .arg(m_mplex->error));
513 }
514 pthread_exit(&errorcount);
515 }
516
517 return 0;
518}
519
520void *MPEG2fixup::ReplexStart(void *data)
521{
522 MThread::ThreadSetup("MPEG2Replex");
523 auto *m2f = static_cast<MPEG2fixup *>(data);
524 if (!m2f)
525 return nullptr;
526 m2f->m_rx.Start();
528 return nullptr;
529}
530
532{
533 int start = 1;
534 multiplex_t mx {};
535
536 //array defines number of allowed audio streams
537 // note that although only 1 stream is currently supported, multiplex.c
538 // expects the size to by N_AUDIO
539 aok_arr ext_ok {};
540 int video_ok = 0;
541
542 //seq_head should be set only for the 1st sequence header. If a new
543 // seq header comes which is different, we are screwed.
544
545
546 int video_delay = 0;
547 int audio_delay = 0;
548
549 mx.priv = (void *)this;
550
551 int fd_out = open(m_outfile.toLocal8Bit().constData(),
552 O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
553
554 //await buffer fill
555 pthread_mutex_lock(&m_mutex);
556 pthread_cond_signal(&m_cond);
557 pthread_cond_wait(&m_cond, &m_mutex);
558 pthread_mutex_unlock(&m_mutex);
559
560 m_mplex = &mx;
561
562 init_multiplex(&mx, &m_seq_head, m_extframe.data(), m_exttype.data(), m_exttypcnt.data(),
563 video_delay, audio_delay, fd_out, fill_buffers,
565 setup_multiplex(&mx);
566
567 while (true)
568 {
569 check_times( &mx, &video_ok, ext_ok, &start);
570 if (write_out_packs( &mx, video_ok, ext_ok)) {
571 // mythtv#244: exiting here blocks the reading thread indefinitely;
572 // maybe there is a way to fail it also?
573 // LOG(VB_GENERAL, LOG_ERR, // or comment all this to fail until close
574 // QString("exiting thread after %1 write errors")
575 // .arg(m_mplex->error));
576 // pthread_exit(&m_mplex->error);
577 }
578 }
579}
580
581#define INDEX_BUF (sizeof(index_unit) * 200)
583{
584 // index_vrbuf contains index_units which describe a video frame
585 // it also contains the start pos of the next frame
586 // index_arbuf only uses, pts, framesize, length, start, (active, err)
587
588 if (m_vFrame.first()->m_mpeg2_seq.height >= 720)
589 {
590 LOG(VB_GENERAL, LOG_NOTICE, "MPEG2fixup::InitReplex(): High Definition input, increasing replex buffers");
593 else if (m_rx.m_otype == REPLEX_TS_SD)
595 else
596 {
597 LOG(VB_GENERAL, LOG_WARNING, "MPEG2fixup::InitReplex(): Using '--ostream=dvd' with HD video is an invalid combination");
598 }
599 }
600
601 //this should support > 100 frames
602 uint32_t memsize = m_vFrame.first()->m_mpeg2_seq.width *
603 m_vFrame.first()->m_mpeg2_seq.height * 10;
604 ring_init(&m_rx.m_vrBuf, memsize);
606
607 m_rx.m_exttype.fill(0);
608 m_rx.m_exttypcnt.fill(0);
609 int mp2_count = 0;
610 int ac3_count = 0;
611 for (auto it = m_aFrame.begin(); it != m_aFrame.end(); it++)
612 {
613 if (it.key() < 0)
614 continue; // will never happen in practice
615 uint index = it.key();
616 if (index > m_inputFC->nb_streams)
617 continue; // will never happen in practice
618 AVCodecContext *avctx = getCodecContext(index);
619 if (avctx == nullptr)
620 continue;
621 int i = m_audMap[index];
622 AVDictionaryEntry *metatag =
623 av_dict_get(m_inputFC->streams[index]->metadata,
624 "language", nullptr, 0);
625 char *lang = metatag ? metatag->value : (char *)"";
626 ring_init(&m_rx.m_extrbuf[i], memsize / 5);
628 m_rx.m_extframe[i].set = 1;
629 m_rx.m_extframe[i].bit_rate = avctx->bit_rate;
630 m_rx.m_extframe[i].framesize = (*it)->first()->m_pkt->size;
631 strncpy(m_rx.m_extframe[i].language, lang, 4);
632 switch(GetStreamType(index))
633 {
634 case AV_CODEC_ID_MP2:
635 case AV_CODEC_ID_MP3:
636 m_rx.m_exttype[i] = 2;
637 m_rx.m_exttypcnt[i] = mp2_count++;
638 break;
639 case AV_CODEC_ID_AC3:
640 m_rx.m_exttype[i] = 1;
641 m_rx.m_exttypcnt[i] = ac3_count++;
642 break;
643 }
644 }
645
646 //bit_rate/400
647 m_rx.m_seq_head.bit_rate = m_vFrame.first()->m_mpeg2_seq.byte_rate / 50;
648 m_rx.m_seq_head.frame_rate = (m_vFrame.first()->m_mpeg2_seq.frame_period +
649 26999999ULL) / m_vFrame.first()->m_mpeg2_seq.frame_period;
650
652}
653
655{
656 QString msg = QString("Id:%1 %2 V:%3").arg(f->m_pkt->stream_index)
657 .arg(PtsTime(f->m_pkt->pts))
658 .arg(ring_free(&m_rx.m_indexVrbuf) / sizeof(index_unit));
659
660 if (m_extCount)
661 {
662 msg += " EXT:";
663 for (int i = 0; i < m_extCount; i++)
664 msg += QString(" %2")
665 .arg(ring_free(&m_rx.m_indexExtrbuf[i]) / sizeof(index_unit));
666 }
667 LOG(VB_RPLXQUEUE, LOG_INFO, msg);
668}
669
671{
672 index_unit iu {};
673 ringbuffer *rb = nullptr;
674 ringbuffer *rbi = nullptr;
675 int id = f->m_pkt->stream_index;
676
677 memset(&iu, 0, sizeof(index_unit));
678 iu.frame_start = 1;
679
680 if (id == m_vidId)
681 {
682 rb = &m_rx.m_vrBuf;
683 rbi = &m_rx.m_indexVrbuf;
684 iu.frame = GetFrameTypeN(f);
685 iu.seq_header = static_cast<uint8_t>(f->m_isSequence);
686 iu.gop = static_cast<uint8_t>(f->m_isGop);
687
688 iu.gop_off = f->m_gopPos - f->m_pkt->data;
689 iu.frame_off = f->m_framePos - f->m_pkt->data;
690 iu.dts = f->m_pkt->dts * 300;
691 }
692 else if (GetStreamType(id) == AV_CODEC_ID_MP2 ||
693 GetStreamType(id) == AV_CODEC_ID_MP3 ||
694 GetStreamType(id) == AV_CODEC_ID_AC3)
695 {
696 rb = &m_rx.m_extrbuf[m_audMap[id]];
697 rbi = &m_rx.m_indexExtrbuf[m_audMap[id]];
698 iu.framesize = f->m_pkt->size;
699 }
700
701 if (!rb || !rbi)
702 {
703 LOG(VB_GENERAL, LOG_ERR, "Ringbuffer pointers empty. No stream found");
704 return 1;
705 }
706
707 iu.active = 1;
708 iu.length = f->m_pkt->size;
709 iu.pts = f->m_pkt->pts * 300;
710 pthread_mutex_lock( &m_rx.m_mutex );
711
712 FrameInfo(f);
713 while (ring_free(rb) < (unsigned int)f->m_pkt->size ||
714 ring_free(rbi) < sizeof(index_unit))
715 {
716 int ok = 1;
717
718 if (rbi != &m_rx.m_indexVrbuf &&
720 ok = 0;
721
722 for (int i = 0; i < m_extCount; i++)
723 {
724 if (rbi != &m_rx.m_indexExtrbuf[i] &&
726 ok = 0;
727 }
728
729 if (!ok && ring_free(rb) < (unsigned int)f->m_pkt->size &&
730 ring_free(rbi) >= sizeof(index_unit))
731 {
732 // increase memory to avoid deadlock
733 unsigned int inc_size = 10 * (unsigned int)f->m_pkt->size;
734 LOG(VB_GENERAL, LOG_NOTICE,
735 QString("Increasing ringbuffer size by %1 to avoid deadlock")
736 .arg(inc_size));
737
738 if (!ring_reinit(rb, rb->size + inc_size))
739 ok = 1;
740 }
741 if (!ok)
742 {
743 pthread_mutex_unlock( &m_rx.m_mutex );
744 //deadlock
745 LOG(VB_GENERAL, LOG_ERR,
746 "Deadlock detected. One buffer is full when "
747 "the other is empty! Aborting");
748 return 1;
749 }
750
751 pthread_cond_signal(&m_rx.m_cond);
752 pthread_cond_wait(&m_rx.m_cond, &m_rx.m_mutex);
753
754 FrameInfo(f);
755 }
756
757 if (ring_write(rb, f->m_pkt->data, f->m_pkt->size)<0)
758 {
759 pthread_mutex_unlock( &m_rx.m_mutex );
760 LOG(VB_GENERAL, LOG_ERR,
761 QString("Ring buffer overflow %1").arg(rb->size));
762 return 1;
763 }
764
765 if (ring_write(rbi, (uint8_t *)&iu, sizeof(index_unit))<0)
766 {
767 pthread_mutex_unlock( &m_rx.m_mutex );
768 LOG(VB_GENERAL, LOG_ERR,
769 QString("Ring buffer overflow %1").arg(rbi->size));
770 return 1;
771 }
772 pthread_mutex_unlock(&m_rx.m_mutex);
773 m_lastWrittenPos = f->m_pkt->pos;
774 return 0;
775}
776
777bool MPEG2fixup::InitAV(const QString& inputfile, const char *type, int64_t offset)
778{
779 QByteArray ifarray = inputfile.toLocal8Bit();
780 const char *ifname = ifarray.constData();
781
782 const AVInputFormat *fmt = nullptr;
783
784 if (type)
785 fmt = av_find_input_format(type);
786
787 // Open recording
788 LOG(VB_GENERAL, LOG_INFO, QString("Opening %1").arg(inputfile));
789
790 if (m_inputFC)
791 {
792 avformat_close_input(&m_inputFC);
793 m_inputFC = nullptr;
794 }
795
796 int ret = avformat_open_input(&m_inputFC, ifname, fmt, nullptr);
797 if (ret)
798 {
799 LOG(VB_GENERAL, LOG_ERR,
800 QString("Couldn't open input file, error #%1").arg(ret));
801 return false;
802 }
803
804 if (m_inputFC->iformat && strcmp(m_inputFC->iformat->name, "mpegts") == 0 &&
805 gCoreContext->GetBoolSetting("FFMPEGTS", false))
806 {
807 fmt = av_find_input_format("mpegts-ffmpeg");
808 if (fmt)
809 {
810 LOG(VB_PLAYBACK, LOG_INFO, "Using FFmpeg MPEG-TS demuxer (forced)");
811 avformat_close_input(&m_inputFC);
812 ret = avformat_open_input(&m_inputFC, ifname, fmt, nullptr);
813 if (ret)
814 {
815 LOG(VB_GENERAL, LOG_ERR,
816 QString("Couldn't open input file, error #%1").arg(ret));
817 return false;
818 }
819 }
820 }
821
822 m_mkvFile = m_inputFC->iformat && strcmp(m_inputFC->iformat->name, "mkv") == 0;
823
824 if (offset)
825 av_seek_frame(m_inputFC, m_vidId, offset, AVSEEK_FLAG_BYTE);
826
827 // Getting stream information
828 ret = avformat_find_stream_info(m_inputFC, nullptr);
829 if (ret < 0)
830 {
831 LOG(VB_GENERAL, LOG_ERR,
832 QString("Couldn't get stream info, error #%1").arg(ret));
833 avformat_close_input(&m_inputFC);
834 m_inputFC = nullptr;
835 return false;
836 }
837
838 // Dump stream information
839 if (VERBOSE_LEVEL_CHECK(VB_GENERAL, LOG_INFO))
840 av_dump_format(m_inputFC, 0, ifname, 0);
841
842 for (unsigned int i = 0; i < m_inputFC->nb_streams; i++)
843 {
844 switch (m_inputFC->streams[i]->codecpar->codec_type)
845 {
846 case AVMEDIA_TYPE_VIDEO:
847 if (m_vidId == -1)
848 m_vidId = i;
849 break;
850
851 case AVMEDIA_TYPE_AUDIO:
852 if (!m_allAudio && m_extCount > 0 &&
853 m_inputFC->streams[i]->codecpar->ch_layout.nb_channels < 2 &&
854 m_inputFC->streams[i]->codecpar->sample_rate < 100000)
855 {
856 LOG(VB_GENERAL, LOG_ERR,
857 QString("Skipping audio stream: %1").arg(i));
858 break;
859 }
860 if (m_inputFC->streams[i]->codecpar->codec_id == AV_CODEC_ID_AC3 ||
861 m_inputFC->streams[i]->codecpar->codec_id == AV_CODEC_ID_MP3 ||
862 m_inputFC->streams[i]->codecpar->codec_id == AV_CODEC_ID_MP2)
863 {
864 m_audMap[i] = m_extCount++;
865 m_aFrame[i] = new FrameList();
866 }
867 else
868 {
869 LOG(VB_GENERAL, LOG_ERR,
870 QString("Skipping unsupported audio stream: %1")
871 .arg(m_inputFC->streams[i]->codecpar->codec_id));
872 }
873 break;
874 default:
875 LOG(VB_GENERAL, LOG_ERR,
876 QString("Skipping unsupported codec %1 on stream %2")
877 .arg(m_inputFC->streams[i]->codecpar->codec_type).arg(i));
878 break;
879 }
880 }
881
882 return true;
883}
884
885void MPEG2fixup::SetFrameNum(uint8_t *ptr, int num)
886{
887 SETBITS(ptr + 4, num, 10);
888}
889
891{
892 if (frame1->m_isSequence || !frame2->m_isSequence)
893 return;
894
895 int head_size = (frame2->m_framePos - frame2->m_pkt->data);
896
897 int oldPktSize = frame1->m_pkt->size;
898 frame1->ensure_size(frame1->m_pkt->size + head_size); // Changes pkt.size
899 memmove(frame1->m_pkt->data + head_size, frame1->m_pkt->data, oldPktSize);
900 memcpy(frame1->m_pkt->data, frame2->m_pkt->data, head_size);
902#if 0
903 if (VERBOSE_LEVEL_CHECK(VB_PROCESS, LOG_ANY))
904 {
905 static int count = 0;
906 QString filename = QString("hdr%1.yuv").arg(count++);
907 WriteFrame(filename, &frame1->m_pkt);
908 }
909#endif
910}
911
912int MPEG2fixup::ProcessVideo(MPEG2frame *vf, mpeg2dec_t *dec)
913{
914 int state = -1;
915 int last_pos = 0;
916
917 if (dec == m_headerDecoder)
918 {
919 mpeg2_reset(dec, 0);
920 vf->m_isSequence = false;
921 vf->m_isGop = false;
922 }
923
924 auto *info = (mpeg2_info_t *)mpeg2_info(dec);
925
926 mpeg2_buffer(dec, vf->m_pkt->data, vf->m_pkt->data + vf->m_pkt->size);
927
928 while (state != STATE_PICTURE)
929 {
930 state = mpeg2_parse(dec);
931
932 if (dec == m_headerDecoder)
933 {
934 switch (state)
935 {
936
937 case STATE_SEQUENCE:
938 case STATE_SEQUENCE_MODIFIED:
939 case STATE_SEQUENCE_REPEATED:
940 memcpy(&vf->m_mpeg2_seq, info->sequence,
941 sizeof(mpeg2_sequence_t));
942 vf->m_isSequence = true;
943 break;
944
945 case STATE_GOP:
946 memcpy(&vf->m_mpeg2_gop, info->gop, sizeof(mpeg2_gop_t));
947 vf->m_isGop = true;
948 vf->m_gopPos = vf->m_pkt->data + last_pos;
949 //pd->adjustFrameCount=0;
950 break;
951
952 case STATE_PICTURE:
953 memcpy(&vf->m_mpeg2_pic, info->current_picture,
954 sizeof(mpeg2_picture_t));
955 vf->m_framePos = vf->m_pkt->data + last_pos;
956 break;
957
958 case STATE_BUFFER:
959 LOG(VB_GENERAL, LOG_WARNING,
960 "Warning: partial frame found!");
961 return 1;
962 }
963 }
964 else if (state == STATE_BUFFER)
965 {
966 WriteData("abort.dat", vf->m_pkt->data, vf->m_pkt->size);
967 LOG(VB_GENERAL, LOG_ERR,
968 QString("Failed to decode frame. Position was: %1")
969 .arg(last_pos));
970 return -1;
971 }
972 last_pos = (vf->m_pkt->size - mpeg2_getpos(dec)) - 4;
973 }
974
975 if (dec != m_headerDecoder)
976 {
977 while (state != STATE_BUFFER)
978 state = mpeg2_parse(dec);
979 if (info->display_picture)
980 {
981 // This is a hack to force libmpeg2 to finish writing out the slice
982 // without it, the final row doesn't get put into the disp_pic
983 // (for B-frames only).
984 // 0xb2 is 'user data' and is actually illegal between pic
985 // headers, but it is just discarded by libmpeg2
986 std::array<uint8_t,8> tmp {0x00, 0x00, 0x01, 0xb2, 0xff, 0xff, 0xff, 0xff};
987 mpeg2_buffer(dec, tmp.data(), tmp.data() + 8);
988 mpeg2_parse(dec);
989 }
990 }
991
992 if (VERBOSE_LEVEL_CHECK(VB_DECODE, LOG_INFO))
993 {
994 QString msg = QString("");
995#if 0
996 msg += QString("unused:%1 ") .arg(vf->m_pkt->size - mpeg2_getpos(dec));
997#endif
998
999 if (vf->m_isSequence)
1000 msg += QString("%1x%2 P:%3 ").arg(info->sequence->width)
1001 .arg(info->sequence->height).arg(info->sequence->frame_period);
1002
1003 if (info->gop)
1004 {
1005 QString gop = QString("%1:%2:%3:%4 ")
1006 .arg(info->gop->hours, 2, 10, QChar('0')).arg(info->gop->minutes, 2, 10, QChar('0'))
1007 .arg(info->gop->seconds, 2, 10, QChar('0')).arg(info->gop->pictures, 3, 10, QChar('0'));
1008 msg += gop;
1009 }
1010 if (info->current_picture)
1011 {
1012 int ct = info->current_picture->flags & PIC_MASK_CODING_TYPE;
1013 char coding_type { 'X' };
1014 if (ct == PIC_FLAG_CODING_TYPE_I)
1015 coding_type = 'I';
1016 else if (ct == PIC_FLAG_CODING_TYPE_P)
1017 coding_type = 'P';
1018 else if (ct == PIC_FLAG_CODING_TYPE_B)
1019 coding_type = 'B';
1020 else if (ct == PIC_FLAG_CODING_TYPE_D)
1021 coding_type = 'D';
1022 char top_bottom = (info->current_picture->flags &
1023 PIC_FLAG_TOP_FIELD_FIRST) ? 'T' : 'B';
1024 char progressive = (info->current_picture->flags &
1025 PIC_FLAG_PROGRESSIVE_FRAME) ? 'P' : '_';
1026 msg += QString("#%1 fl:%2%3%4%5%6 ")
1027 .arg(info->current_picture->temporal_reference)
1028 .arg(info->current_picture->nb_fields)
1029 .arg(coding_type)
1030 .arg(top_bottom)
1031 .arg(progressive)
1032 .arg(info->current_picture->flags >> 4, 0, 16);
1033 }
1034 msg += QString("pos: %1").arg(vf->m_pkt->pos);
1035 LOG(VB_DECODE, LOG_INFO, msg);
1036 }
1037
1038 return 0;
1039}
1040
1042{
1043 MPEG2frame *tmpFrame = GetPoolFrame(f);
1044 if (tmpFrame == nullptr)
1045 return;
1046 if (!tmpFrame->m_isSequence)
1047 {
1048 for (const auto & vf : std::as_const(m_vFrame))
1049 {
1050 if (vf->m_isSequence)
1051 {
1052 AddSequence(tmpFrame, vf);
1053 break;
1054 }
1055 }
1056 }
1057 WriteFrame(filename, tmpFrame->m_pkt);
1058 m_framePool.enqueue(tmpFrame);
1059}
1060
1061void MPEG2fixup::WriteFrame(const QString& filename, AVPacket *pkt)
1062{
1063 MPEG2frame *tmpFrame = GetPoolFrame(pkt);
1064 if (tmpFrame == nullptr)
1065 return;
1066
1067 QString fname = filename + ".enc";
1068 WriteData(fname, pkt->data, pkt->size);
1069
1070 mpeg2dec_t *tmp_decoder = mpeg2_init();
1071 auto *info = (mpeg2_info_t *)mpeg2_info(tmp_decoder);
1072
1073 while (!info->display_picture)
1074 {
1075 if (ProcessVideo(tmpFrame, tmp_decoder))
1076 {
1077 delete tmpFrame;
1078 return;
1079 }
1080 }
1081
1083 m_framePool.enqueue(tmpFrame);
1084 mpeg2_close(tmp_decoder);
1085}
1086
1087void MPEG2fixup::WriteYUV(const QString& filename, const mpeg2_info_t *info)
1088{
1089 int fh = open(filename.toLocal8Bit().constData(),
1090 O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
1091 if (fh == -1)
1092 {
1093 LOG(VB_GENERAL, LOG_ERR,
1094 QString("Couldn't open file %1: ").arg(filename) + ENO);
1095 return;
1096 }
1097
1098 // Automatically close file at function exit
1099 auto close_fh = [](const int *fh2) { close(*fh2); };
1100 std::unique_ptr<int,decltype(close_fh)> cleanup { &fh, close_fh };
1101
1102 ssize_t ret = write(fh, info->display_fbuf->buf[0],
1103 static_cast<size_t>(info->sequence->width) *
1104 static_cast<size_t>(info->sequence->height));
1105 if (ret < 0)
1106 {
1107 LOG(VB_GENERAL, LOG_ERR, QString("write failed %1: ").arg(filename) +
1108 ENO);
1109 return;
1110 }
1111 ret = write(fh, info->display_fbuf->buf[1],
1112 static_cast<size_t>(info->sequence->chroma_width) *
1113 static_cast<size_t>(info->sequence->chroma_height));
1114 if (ret < 0)
1115 {
1116 LOG(VB_GENERAL, LOG_ERR, QString("write failed %1: ").arg(filename) +
1117 ENO);
1118 return;
1119 }
1120 ret = write(fh, info->display_fbuf->buf[2],
1121 static_cast<size_t>(info->sequence->chroma_width) *
1122 static_cast<size_t>(info->sequence->chroma_height));
1123 if (ret < 0)
1124 {
1125 LOG(VB_GENERAL, LOG_ERR, QString("write failed %1: ").arg(filename) +
1126 ENO);
1127 return;
1128 }
1129}
1130
1131void MPEG2fixup::WriteData(const QString& filename, uint8_t *data, int size)
1132{
1133 int fh = open(filename.toLocal8Bit().constData(),
1134 O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
1135 if (fh == -1)
1136 {
1137 LOG(VB_GENERAL, LOG_ERR,
1138 QString("Couldn't open file %1: ").arg(filename) + ENO);
1139 return;
1140 }
1141
1142 int ret = write(fh, data, size);
1143 if (ret < 0)
1144 LOG(VB_GENERAL, LOG_ERR, QString("write failed %1").arg(filename) +
1145 ENO);
1146 close(fh);
1147}
1148
1149bool MPEG2fixup::BuildFrame(AVPacket *pkt, const QString& fname)
1150{
1151 alignas(16) std::array<uint16_t,64> intra_matrix {};
1152 int64_t savedPts = pkt->pts; // save the original pts
1153
1154 const mpeg2_info_t *info = mpeg2_info(m_imgDecoder);
1155 if (!info->display_fbuf)
1156 return true;
1157
1158 int outbuf_size = info->sequence->width * info->sequence->height * 2;
1159
1160 if (!fname.isEmpty())
1161 {
1162 QString tmpstr = fname + ".yuv";
1163 WriteYUV(tmpstr, info);
1164 }
1165
1166 if (!m_picture)
1167 {
1168 m_picture = av_frame_alloc();
1169 if (m_picture == nullptr)
1170 {
1171 return true;
1172 }
1173 }
1174 else
1175 {
1176 av_frame_unref(m_picture);
1177 }
1178
1179 //pkt->data = (uint8_t *)av_malloc(outbuf_size);
1180 if (pkt->size < outbuf_size)
1181 av_grow_packet(pkt, (outbuf_size - pkt->size));
1182
1183 m_picture->data[0] = info->display_fbuf->buf[0];
1184 m_picture->data[1] = info->display_fbuf->buf[1];
1185 m_picture->data[2] = info->display_fbuf->buf[2];
1186
1187 m_picture->linesize[0] = info->sequence->width;
1188 m_picture->linesize[1] = info->sequence->chroma_width;
1189 m_picture->linesize[2] = info->sequence->chroma_width;
1190
1191 m_picture->opaque = info->display_fbuf->id;
1192
1193#if 0 //RUN_ONCE
1194 static constexpr std::array<uint8_t, 64> k_zigzag_scan = {
1195 0, 1, 8, 16, 9, 2, 3, 10,
1196 17, 24, 32, 25, 18, 11, 4, 5,
1197 12, 19, 26, 33, 40, 48, 41, 34,
1198 27, 20, 13, 6, 7, 14, 21, 28,
1199 35, 42, 49, 56, 57, 50, 43, 36,
1200 29, 22, 15, 23, 30, 37, 44, 51,
1201 58, 59, 52, 45, 38, 31, 39, 46,
1202 53, 60, 61, 54, 47, 55, 62, 63
1203 };
1204
1205 static std::array<uint16_t, 64> k_invZigzagDirect16 = {};
1206 for (int i = 0; i < 64; i++)
1207 {
1208 k_invZigzagDirect16[k_zigzag_scan[i]] = i;
1209 }
1210#endif
1211 static constexpr std::array<uint16_t, 64> k_invZigzagDirect16 = {
1212 0, 1, 5, 6, 14, 15, 27, 28,
1213 2, 4, 7, 13, 16, 26, 29, 42,
1214 3, 8, 12, 17, 25, 30, 41, 43,
1215 9, 11, 18, 24, 31, 40, 44, 53,
1216 10, 19, 23, 32, 39, 45, 52, 54,
1217 20, 22, 33, 38, 46, 51, 55, 60,
1218 21, 34, 37, 47, 50, 56, 59, 61,
1219 35, 36, 48, 49, 57, 58, 62, 63,
1220 };
1221
1222 //copy_quant_matrix(m_imgDecoder, intra_matrix);
1223 for (int i = 0; i < 64; i++)
1224 {
1225 intra_matrix[k_invZigzagDirect16[i]] = m_imgDecoder->quantizer_matrix[0][i];
1226 }
1227
1228 if (info->display_picture->nb_fields % 2)
1229 {
1230 if ((info->display_picture->flags & PIC_FLAG_TOP_FIELD_FIRST) != 0)
1231 {
1232 m_picture->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST;
1233 }
1234 else
1235 {
1236 m_picture->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST;
1237 }
1238 }
1239 else
1240 {
1241 if ((info->display_picture->flags & PIC_FLAG_TOP_FIELD_FIRST) != 0)
1242 {
1243 m_picture->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST;
1244 }
1245 else
1246 {
1247 m_picture->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST;
1248 }
1249 }
1250
1251 if ((info->display_picture->flags & PIC_FLAG_PROGRESSIVE_FRAME) != 0)
1252 {
1253 m_picture->flags &= ~AV_FRAME_FLAG_INTERLACED;
1254 }
1255 else
1256 {
1257 m_picture->flags |= AV_FRAME_FLAG_INTERLACED;
1258 }
1259
1260 const AVCodec *out_codec = avcodec_find_encoder(AV_CODEC_ID_MPEG2VIDEO);
1261 if (!out_codec)
1262 {
1263 LOG(VB_GENERAL, LOG_ERR, "Couldn't find MPEG2 encoder");
1264 return true;
1265 }
1266
1267 AVCodecContext *c = avcodec_alloc_context3(nullptr);
1268
1269 //NOTE: The following may seem wrong, but avcodec requires
1270 //sequence->progressive == frame->progressive
1271 //We fix the discrepancy by discarding avcodec's sequence header, and
1272 //replace it with the original
1273 if ((m_picture->flags & AV_FRAME_FLAG_INTERLACED) != 0)
1274 c->flags |= AV_CODEC_FLAG_INTERLACED_DCT;
1275
1276 c->bit_rate = info->sequence->byte_rate << 3; //not used
1277 c->bit_rate_tolerance = c->bit_rate >> 2; //not used
1278 c->width = info->sequence->width;
1279 c->height = info->sequence->height;
1280 av_reduce(&c->time_base.num, &c->time_base.den,
1281 info->sequence->frame_period, 27000000LL, 100000);
1282 c->pix_fmt = AV_PIX_FMT_YUV420P;
1283 c->max_b_frames = 0;
1284 c->has_b_frames = 0;
1285 // c->rc_buffer_aggressivity = 1;
1286 // rc_buf_aggressivity is now "currently useless"
1287
1288 // c->profile = vidCC->profile;
1289 // c->level = vidCC->level;
1290 c->rc_buffer_size = 0;
1291 c->gop_size = 0; // this should force all i-frames
1292 // c->flags=CODEC_FLAG_LOW_DELAY;
1293
1294 if (intra_matrix[0] == 0x08)
1295 c->intra_matrix = intra_matrix.data();
1296
1297 c->qmin = c->qmax = 2;
1298
1299 m_picture->width = info->sequence->width;
1300 m_picture->height = info->sequence->height;
1301 m_picture->format = AV_PIX_FMT_YUV420P;
1302 m_picture->pts = AV_NOPTS_VALUE;
1303 m_picture->flags |= AV_FRAME_FLAG_KEY;
1304 m_picture->pict_type = AV_PICTURE_TYPE_NONE;
1305 m_picture->quality = 0;
1306
1307 if (avcodec_open2(c, out_codec, nullptr) < 0)
1308 {
1309 LOG(VB_GENERAL, LOG_ERR, "could not open codec");
1310 return true;
1311 }
1312
1313 int got_packet = 0;
1314 int ret = avcodec_send_frame(c, m_picture);
1315
1316 bool flushed = false;
1317 while (ret >= 0)
1318 {
1319 // ret = avcodec_encode_video2(c, pkt, m_picture, &got_packet);
1320 ret = avcodec_receive_packet(c, pkt);
1321 if (ret == 0)
1322 got_packet = 1;
1323 if (ret == AVERROR(EAGAIN))
1324 ret = 0;
1325 if (ret < 0)
1326 break;
1327 if (flushed)
1328 break;
1329 // flush
1330 ret = avcodec_send_frame(c, nullptr);
1331 flushed = true;
1332 }
1333
1334 if (ret < 0 || !got_packet)
1335 {
1336 LOG(VB_GENERAL, LOG_ERR,
1337 QString("avcodec_encode_video2 failed (%1)").arg(ret));
1338 return true;
1339 }
1340
1341 if (!fname.isEmpty())
1342 {
1343 QString ename = fname + ".enc";
1344 WriteData(ename, pkt->data, pkt->size);
1345
1346 QString yname = fname + ".enc.yuv";
1347 WriteFrame(yname, pkt);
1348 }
1349 int delta = FindMPEG2Header(pkt->data, pkt->size, 0x00);
1350 // out_size=avcodec_encode_video(c, outbuf, outbuf_size, m_picture);
1351 // HACK: a hack to get to the picture frame
1352 //pkt->size -= delta; // a hack to get to the picture frame
1353 int newSize = pkt->size - delta;
1354 pkt->pts = savedPts; // restore the original pts
1355 memmove(pkt->data, pkt->data + delta, newSize);
1356 av_shrink_packet(pkt, newSize); // Shrink packet to it's new size
1357 // End HACK
1358
1359 SetRepeat(pkt->data, pkt->size, info->display_picture->nb_fields,
1360 ((info->display_picture->flags & PIC_FLAG_TOP_FIELD_FIRST) != 0U));
1361
1362 avcodec_free_context(&c);
1363
1364 return false;
1365}
1366
1367static constexpr int MAX_FRAMES { 20000 };
1369{
1370 MPEG2frame *f = nullptr;
1371
1372 if (m_framePool.isEmpty())
1373 {
1374 static int s_frameCount = 0;
1375 if (s_frameCount >= MAX_FRAMES)
1376 {
1377 LOG(VB_GENERAL, LOG_ERR, "No more queue slots!");
1378 return nullptr;
1379 }
1380 f = new MPEG2frame(pkt->size);
1381 s_frameCount++;
1382 }
1383 else
1384 {
1385 f = m_framePool.dequeue();
1386 }
1387
1388 f->set_pkt(pkt);
1389
1390 return f;
1391}
1392
1394{
1395 MPEG2frame *tmpFrame = GetPoolFrame(f->m_pkt);
1396 if (!tmpFrame)
1397 return tmpFrame;
1398
1399 tmpFrame->m_isSequence = f->m_isSequence;
1400 tmpFrame->m_isGop = f->m_isGop;
1401 tmpFrame->m_mpeg2_seq = f->m_mpeg2_seq;
1402 tmpFrame->m_mpeg2_gop = f->m_mpeg2_gop;
1403 tmpFrame->m_mpeg2_pic = f->m_mpeg2_pic;
1404 return tmpFrame;
1405}
1406
1407int MPEG2fixup::GetFrame(AVPacket *pkt)
1408{
1409 while (true)
1410 {
1411 bool done = false;
1412 if (!m_unreadFrames.isEmpty())
1413 {
1414 m_vFrame.append(m_unreadFrames.dequeue());
1415 if (m_realFileEnd && m_unreadFrames.isEmpty())
1416 m_fileEnd = true;
1417 return static_cast<int>(m_fileEnd);
1418 }
1419
1420 while (!done)
1421 {
1422 pkt->pts = AV_NOPTS_VALUE;
1423 pkt->dts = AV_NOPTS_VALUE;
1424 int ret = av_read_frame(m_inputFC, pkt);
1425
1426 if (ret < 0)
1427 {
1428 // If it is EAGAIN, obey it, dangit!
1429 if (ret == -EAGAIN)
1430 continue;
1431
1432 //insert a bogus frame (this won't be written out)
1433 if (m_vFrame.isEmpty())
1434 {
1435 LOG(VB_GENERAL, LOG_ERR,
1436 "Found end of file without finding any frames");
1437 av_packet_unref(pkt);
1438 return 1;
1439 }
1440
1441 MPEG2frame *tmpFrame = GetPoolFrame(m_vFrame.last()->m_pkt);
1442 if (tmpFrame == nullptr)
1443 {
1444 av_packet_unref(pkt);
1445 return 1;
1446 }
1447
1448 m_vFrame.append(tmpFrame);
1449 m_realFileEnd = true;
1450 m_fileEnd = true;
1451 return 1;
1452 }
1453
1454 if (pkt->stream_index == m_vidId ||
1455 m_aFrame.contains(pkt->stream_index))
1456 done = true;
1457 else
1458 av_packet_unref(pkt);
1459 }
1460 pkt->duration = m_frameNum++;
1461 if ((m_showProgress || m_updateStatus) &&
1463 {
1464 float percent_done = 100.0F * pkt->pos / m_fileSize;
1465 if (m_updateStatus)
1466 m_updateStatus(percent_done);
1467 if (m_showProgress)
1468 LOG(VB_GENERAL, LOG_INFO, QString("%1% complete")
1469 .arg(percent_done, 0, 'f', 1));
1470 if (m_checkAbort && m_checkAbort())
1471 return REENCODE_STOPPED;
1474 }
1475
1476#ifdef DEBUG_AUDIO
1477 LOG(VB_DECODE, LOG_INFO, QString("Stream: %1 PTS: %2 DTS: %3 pos: %4")
1478 .arg(pkt->stream_index)
1479 .arg((pkt->pts == AV_NOPTS_VALUE) ? "NONE" : PtsTime(pkt->pts))
1480 .arg((pkt->dts == AV_NOPTS_VALUE) ? "NONE" : PtsTime(pkt->dts))
1481 .arg(pkt->pos));
1482#endif
1483
1484 MPEG2frame *tmpFrame = GetPoolFrame(pkt);
1485 if (tmpFrame == nullptr)
1486 {
1487 av_packet_unref(pkt);
1488 return 1;
1489 }
1490
1491 switch (m_inputFC->streams[pkt->stream_index]->codecpar->codec_type)
1492 {
1493 case AVMEDIA_TYPE_VIDEO:
1494 m_vFrame.append(tmpFrame);
1495 av_packet_unref(pkt);
1496
1498 return 0;
1499 m_framePool.enqueue(m_vFrame.takeLast());
1500 break;
1501
1502 case AVMEDIA_TYPE_AUDIO:
1503 if (m_aFrame.contains(pkt->stream_index))
1504 {
1505 m_aFrame[pkt->stream_index]->append(tmpFrame);
1506 }
1507 else
1508 {
1509 LOG(VB_GENERAL, LOG_DEBUG,
1510 QString("Invalid stream ID %1, ignoring").arg(pkt->stream_index));
1511 m_framePool.enqueue(tmpFrame);
1512 }
1513 av_packet_unref(pkt);
1514 return 0;
1515
1516 default:
1517 m_framePool.enqueue(tmpFrame);
1518 av_packet_unref(pkt);
1519 return 1;
1520 }
1521 }
1522}
1523
1525{
1526 QMap <int, bool> found;
1527 AVPacket *pkt = av_packet_alloc();
1528 if (pkt == nullptr)
1529 {
1530 LOG(VB_PROCESS, LOG_ERR, "packet allocation failed");
1531 return false;
1532 }
1533
1534 do
1535 {
1536 if (GetFrame(pkt))
1537 {
1538 av_packet_free(&pkt);
1539 return false;
1540 }
1541
1542 if (m_vidId == pkt->stream_index)
1543 {
1544 while (!m_vFrame.isEmpty())
1545 {
1546 if (m_vFrame.first()->m_isSequence)
1547 {
1548 if (pkt->pos != m_vFrame.first()->m_pkt->pos)
1549 break;
1550
1551 if (pkt->pts != AV_NOPTS_VALUE ||
1552 pkt->dts != AV_NOPTS_VALUE)
1553 {
1554 if (pkt->pts == AV_NOPTS_VALUE)
1555 m_vFrame.first()->m_pkt->pts = pkt->dts;
1556
1557 LOG(VB_PROCESS, LOG_INFO,
1558 "Found 1st valid video frame");
1559 break;
1560 }
1561 }
1562
1563 LOG(VB_PROCESS, LOG_INFO, "Dropping V packet");
1564
1565 m_framePool.enqueue(m_vFrame.takeFirst());
1566 }
1567 }
1568
1569 if (m_vFrame.isEmpty())
1570 continue;
1571
1572 for (auto it = m_aFrame.begin(); it != m_aFrame.end(); it++)
1573 {
1574 if (found.contains(it.key()))
1575 continue;
1576
1577 FrameList *af = (*it);
1578
1579 while (!af->isEmpty())
1580 {
1581 int64_t delta = diff2x33(af->first()->m_pkt->pts,
1582 m_vFrame.first()->m_pkt->pts);
1583 if (delta < -180000 || delta > 180000) //2 seconds
1584 {
1585 //Check all video sequence packets against current
1586 //audio packet
1587 MPEG2frame *foundframe = nullptr;
1588 for (auto *currFrame : std::as_const(m_vFrame))
1589 {
1590 if (currFrame->m_isSequence)
1591 {
1592 int64_t dlta1 = diff2x33(af->first()->m_pkt->pts,
1593 currFrame->m_pkt->pts);
1594 if (dlta1 >= -180000 && dlta1 <= 180000)
1595 {
1596 foundframe = currFrame;
1597 delta = dlta1;
1598 break;
1599 }
1600 }
1601 }
1602
1603 while (foundframe && m_vFrame.first() != foundframe)
1604 {
1605 m_framePool.enqueue(m_vFrame.takeFirst());
1606 }
1607 }
1608
1609 if (delta < -180000 || delta > 180000) //2 seconds
1610 {
1611 LOG(VB_PROCESS, LOG_INFO,
1612 QString("Dropping A packet from stream %1")
1613 .arg(it.key()));
1614 LOG(VB_PROCESS, LOG_INFO, QString(" A:%1 V:%2")
1615 .arg(PtsTime(af->first()->m_pkt->pts),
1616 PtsTime(m_vFrame.first()->m_pkt->pts)));
1617 m_framePool.enqueue(af->takeFirst());
1618 continue;
1619 }
1620
1621 if (delta < 0 && af->count() > 1)
1622 {
1623 if (cmp2x33(af->at(1)->m_pkt->pts,
1624 m_vFrame.first()->m_pkt->pts) > 0)
1625 {
1626 LOG(VB_PROCESS, LOG_INFO,
1627 QString("Found useful audio frame from stream %1")
1628 .arg(it.key()));
1629 found[it.key()] = true;
1630 break;
1631 }
1632 LOG(VB_PROCESS, LOG_INFO,
1633 QString("Dropping A packet from stream %1")
1634 .arg(it.key()));
1635 m_framePool.enqueue(af->takeFirst());
1636 continue;
1637 }
1638 if (delta >= 0)
1639 {
1640 LOG(VB_PROCESS, LOG_INFO,
1641 QString("Found useful audio frame from stream %1")
1642 .arg(it.key()));
1643 found[it.key()] = true;
1644 break;
1645 }
1646
1647 if (af->count() == 1)
1648 break;
1649 }
1650 }
1651 } while (found.count() != m_aFrame.count());
1652
1653 av_packet_free(&pkt);
1654 return true;
1655}
1656
1657void MPEG2fixup::SetRepeat(MPEG2frame *vf, int fields, bool topff)
1658{
1659 vf->m_mpeg2_pic.nb_fields = 2;
1660 SetRepeat(vf->m_framePos, vf->m_pkt->data + vf->m_pkt->size - vf->m_framePos,
1661 fields, topff);
1662}
1663
1664void MPEG2fixup::SetRepeat(uint8_t *ptr, int size, int fields, bool topff)
1665{
1666 uint8_t *end = ptr + size;
1667 uint8_t setmask = 0x00;
1668 uint8_t clrmask = 0xff;
1669 if (topff)
1670 setmask |= 0x80;
1671 else
1672 clrmask &= 0x7f;
1673
1674 if (fields == 2)
1675 clrmask &= 0xfd;
1676 else
1677 setmask |= 0x02;
1678
1679 while (ptr < end)
1680 {
1681 if (MATCH_HEADER(ptr) && ptr[3] == 0xB5 && (ptr[4] & 0xF0) == 0x80)
1682 {
1683 //unset repeat_first_field
1684 //set top_field_first
1685 ptr[7] |= setmask;
1686 ptr[7] &= clrmask;
1687 return;
1688 }
1689
1690 ptr++;
1691 }
1692}
1693
1695{
1696 for (const auto & vf : std::as_const(m_vFrame))
1697 {
1698 if (GetFrameNum(vf) == frameNum)
1699 return vf;
1700 }
1701
1702 return nullptr;
1703}
1704
1705void MPEG2fixup::RenumberFrames(int start_pos, int delta)
1706{
1707 int maxPos = m_vFrame.count() - 1;
1708
1709 for (int pos = start_pos; pos < maxPos; pos++)
1710 {
1711 MPEG2frame *frame = m_vFrame.at(pos);
1712 SetFrameNum(frame->m_framePos, GetFrameNum(frame) + delta);
1713 frame->m_mpeg2_pic.temporal_reference += delta;
1714 }
1715}
1716
1718{
1719 while (!m_vSecondary.isEmpty())
1720 {
1721 m_framePool.enqueue(m_vSecondary.takeFirst());
1722 }
1723
1724 while (m_vFrame.count() > 1)
1725 {
1726 if (m_useSecondary && GetFrameTypeT(m_vFrame.first()) != 'B')
1727 m_vSecondary.append(m_vFrame.takeFirst());
1728 else
1729 m_framePool.enqueue(m_vFrame.takeFirst());
1730 }
1731}
1732
1734{
1735 int frame_num = 0;
1736 mpeg2_reset(m_imgDecoder, 1);
1737 for (const auto & vs : std::as_const(m_vSecondary))
1738 {
1739 SetFrameNum(vs->m_framePos, frame_num++);
1740 if (ProcessVideo(vs, m_imgDecoder) < 0)
1741 return 1;
1742 }
1743 return 0;
1744}
1745
1746MPEG2frame *MPEG2fixup::DecodeToFrame(int frameNum, int skip_reset)
1747{
1748 MPEG2frame *spare = nullptr;
1749 int found = 0;
1750 bool skip_first = false;
1751 const mpeg2_info_t * info = mpeg2_info(m_imgDecoder);
1752 int maxPos = m_vFrame.count() - 1;
1753
1754 if (m_vFrame.at(m_displayFrame)->m_isSequence)
1755 {
1756 skip_first = true;
1757 if (!skip_reset && (m_displayFrame != maxPos || m_displayFrame == 0))
1758 mpeg2_reset(m_imgDecoder, 1);
1759 }
1760
1761 spare = FindFrameNum(frameNum);
1762 if (!spare)
1763 return nullptr;
1764
1765 int framePos = m_vFrame.indexOf(spare);
1766
1767 for (int curPos = m_displayFrame; m_displayFrame != maxPos;
1768 curPos++, m_displayFrame++)
1769 {
1771 return nullptr;
1772
1773 if (!skip_first && curPos >= framePos && info->display_picture &&
1774 (int)info->display_picture->temporal_reference >= frameNum)
1775 {
1776 found = 1;
1778 break;
1779 }
1780
1781 skip_first = false;
1782 }
1783
1784 if (!found)
1785 {
1786 int tmpFrameNum = frameNum;
1787 MPEG2frame *tmpFrame = GetPoolFrame(spare->m_pkt);
1788 if (tmpFrame == nullptr)
1789 return nullptr;
1790
1791 tmpFrame->m_framePos = tmpFrame->m_pkt->data +
1792 (spare->m_framePos - spare->m_pkt->data);
1793
1794 while (!info->display_picture ||
1795 (int)info->display_picture->temporal_reference < frameNum)
1796 {
1797 SetFrameNum(tmpFrame->m_framePos, ++tmpFrameNum);
1798 if (ProcessVideo(tmpFrame, m_imgDecoder) < 0)
1799 {
1800 delete tmpFrame;
1801 return nullptr;
1802 }
1803 }
1804
1805 m_framePool.enqueue(tmpFrame);
1806 }
1807
1808 if ((int)info->display_picture->temporal_reference > frameNum)
1809 {
1810 // the frame in question doesn't exist. We have no idea where we are.
1811 // reset the displayFrame so we start searching from the beginning next
1812 // time
1813 m_displayFrame = 0;
1814 LOG(VB_GENERAL, LOG_NOTICE,
1815 QString("Frame %1 > %2. Corruption likely at pos: %3")
1816 .arg(info->display_picture->temporal_reference)
1817 .arg(frameNum).arg(spare->m_pkt->pos));
1818 }
1819
1820 return spare;
1821}
1822
1823int MPEG2fixup::ConvertToI(FrameList *orderedFrames, int headPos)
1824{
1825 MPEG2frame *spare = nullptr;
1826 AVPacket *pkt = av_packet_alloc();
1827 if (pkt == nullptr)
1828 {
1829 LOG(VB_PROCESS, LOG_ERR, "packet allocation failed");
1830 return 0;
1831 }
1832#ifdef SPEW_FILES
1833 static int ins_count = 0;
1834#endif
1835
1836 //head_pos == 0 means that we are decoding B frames after a seq_header
1837 if (headPos == 0)
1838 {
1839 if (PlaybackSecondary())
1840 {
1841 av_packet_free(&pkt);
1842 return 1;
1843 }
1844 }
1845
1846 for (const auto & of : std::as_const(*orderedFrames))
1847 {
1848 int i = GetFrameNum(of);
1849 spare = DecodeToFrame(i, static_cast<int>(headPos == 0));
1850 if (spare == nullptr)
1851 {
1852 LOG(VB_GENERAL, LOG_WARNING,
1853 QString("ConvertToI skipping undecoded frame #%1").arg(i));
1854 continue;
1855 }
1856
1857 if (GetFrameTypeT(spare) == 'I')
1858 continue;
1859
1860 //pkt = spare->m_pkt;
1861 av_packet_ref(pkt, spare->m_pkt);
1862 //pkt->data is a newly malloced area
1863
1864 QString fname;
1865
1866#ifdef SPEW_FILES
1867 if (VERBOSE_LEVEL_CHECK(VB_PROCESS, LOG_ANY))
1868 fname = QString("cnv%1").arg(ins_count++);
1869#endif
1870
1871 if (BuildFrame(pkt, fname))
1872 {
1873 av_packet_free(&pkt);
1874 return 1;
1875 }
1876
1877 LOG(VB_GENERAL, LOG_INFO,
1878 QString("Converting frame #%1 from %2 to I %3")
1879 .arg(i).arg(GetFrameTypeT(spare)).arg(fname));
1880
1881 spare->set_pkt(pkt);
1882 av_packet_unref(pkt);
1883 SetFrameNum(spare->m_pkt->data, GetFrameNum(spare));
1884 ProcessVideo(spare, m_headerDecoder); //process this new frame
1885 }
1886
1887 //reorder frames
1888 m_vFrame.move(headPos, headPos + orderedFrames->count() - 1);
1889 av_packet_free(&pkt);
1890 return 0;
1891}
1892
1893int MPEG2fixup::InsertFrame(int frameNum, int64_t deltaPTS,
1894 int64_t ptsIncrement, int64_t initPTS)
1895{
1896 MPEG2frame *spare = nullptr;
1897 int increment = 0;
1898 int index = 0;
1899
1900 AVPacket *pkt = av_packet_alloc();
1901 if (pkt == nullptr)
1902 {
1903 LOG(VB_PROCESS, LOG_ERR, "packet allocation failed");
1904 return 0;
1905 }
1906
1907 spare = DecodeToFrame(frameNum, 0);
1908 if (spare == nullptr)
1909 {
1910 av_packet_free(&pkt);
1911 return -1;
1912 }
1913
1914 av_packet_ref(pkt, spare->m_pkt);
1915 //pkt->data is a newly malloced area
1916
1917 {
1918 QString fname;
1919#ifdef SPEW_FILES
1920 static int ins_count = 0;
1921 fname = (VERBOSE_LEVEL_CHECK(VB_PROCESS, LOG_ANY) ?
1922 (QString("ins%1").arg(ins_count++)) : QString());
1923#endif
1924
1925 if (BuildFrame(pkt, fname))
1926 {
1927 av_packet_free(&pkt);
1928 return -1;
1929 }
1930
1931 LOG(VB_GENERAL, LOG_INFO,
1932 QString("Inserting %1 I-Frames after #%2 %3")
1933 .arg((int)(deltaPTS / ptsIncrement))
1934 .arg(GetFrameNum(spare)).arg(fname));
1935 }
1936
1937 inc2x33(&pkt->pts, (ptsIncrement * GetNbFields(spare) / 2) + initPTS);
1938
1939 index = m_vFrame.indexOf(spare) + 1;
1940 while (index < m_vFrame.count() &&
1941 GetFrameTypeT(m_vFrame.at(index)) == 'B')
1942 spare = m_vFrame.at(index++);
1943
1944 index = m_vFrame.indexOf(spare);
1945
1946 while (deltaPTS > 0)
1947 {
1948 index++;
1949 increment++;
1950 pkt->dts = pkt->pts;
1951 SetFrameNum(pkt->data, ++frameNum);
1952 MPEG2frame *tmpFrame = GetPoolFrame(pkt);
1953 if (tmpFrame == nullptr)
1954 return -1;
1955 m_vFrame.insert(index, tmpFrame);
1956 ProcessVideo(tmpFrame, m_headerDecoder); //process new frame
1957
1958 inc2x33(&pkt->pts, ptsIncrement);
1959 deltaPTS -= ptsIncrement;
1960 }
1961
1962 av_packet_free(&pkt);
1963 // update frame # for all later frames in this group
1964 index++;
1965 RenumberFrames(index, increment);
1966
1967 return increment;
1968}
1969
1970void MPEG2fixup::AddRangeList(const QStringList& rangelist, int type)
1971{
1972 frm_dir_map_t *mapPtr = nullptr;
1973
1974 if (type == MPF_TYPE_CUTLIST)
1975 {
1976 mapPtr = &m_delMap;
1977 m_discard = false;
1978 }
1979 else
1980 {
1981 mapPtr = &m_saveMap;
1982 }
1983
1984 mapPtr->clear();
1985
1986 for (const auto & range : std::as_const(rangelist))
1987 {
1988 QStringList tmp = range.split(" - ");
1989 if (tmp.size() < 2)
1990 continue;
1991
1992 std::array<bool,2> ok { false, false };
1993
1994 long long start = tmp[0].toLongLong(ok.data());
1995 long long end = tmp[1].toLongLong(&ok[1]);
1996
1997 if (ok[0] && ok[1])
1998 {
1999 if (start == 0)
2000 {
2001 if (type == MPF_TYPE_CUTLIST)
2002 m_discard = true;
2003 }
2004 else
2005 {
2006 mapPtr->insert(start - 1, MARK_CUT_START);
2007 }
2008
2009 mapPtr->insert(end, MARK_CUT_END);
2010 }
2011 }
2012
2013 if (!rangelist.isEmpty())
2014 m_useSecondary = true;
2015}
2016
2017void MPEG2fixup::ShowRangeMap(frm_dir_map_t *mapPtr, QString msg)
2018{
2019 if (!mapPtr->isEmpty())
2020 {
2021 int64_t start = 0;
2022 frm_dir_map_t::iterator it = mapPtr->begin();
2023 for (; it != mapPtr->end(); ++it)
2024 {
2025 if (*it == MARK_CUT_END)
2026 msg += QString("\n\t\t%1 - %2").arg(start).arg(it.key());
2027 else
2028 start = it.key();
2029 }
2030 if (*(--it) == MARK_CUT_START)
2031 msg += QString("\n\t\t%1 - end").arg(start);
2032 LOG(VB_PROCESS, LOG_INFO, msg);
2033 }
2034}
2035
2037{
2038 FrameList Lreorder;
2039 int maxPos = dtsOrder->count() - 1;
2040
2041 if (pos >= maxPos)
2042 return Lreorder;
2043
2044 MPEG2frame *frame = dtsOrder->at(pos);
2045
2046 for (pos++; pos < maxPos && GetFrameTypeT(dtsOrder->at(pos)) == 'B'; pos++)
2047 Lreorder.append(dtsOrder->at(pos));
2048
2049 Lreorder.append(frame);
2050 return Lreorder;
2051}
2052
2053void MPEG2fixup::InitialPTSFixup(MPEG2frame *curFrame, int64_t &origvPTS,
2054 int64_t &PTSdiscrep, int numframes, bool fix) const
2055{
2056 int64_t tmpPTS = diff2x33(curFrame->m_pkt->pts,
2057 origvPTS / 300);
2058
2059 if (curFrame->m_pkt->pts == AV_NOPTS_VALUE)
2060 {
2061 LOG(VB_PROCESS, LOG_INFO,
2062 QString("Found frame %1 with missing PTS at %2")
2063 .arg(GetFrameNum(curFrame))
2064 .arg(PtsTime(origvPTS / 300)));
2065 if (fix)
2066 curFrame->m_pkt->pts = origvPTS / 300;
2067 else
2068 PTSdiscrep = AV_NOPTS_VALUE;
2069 }
2070 else if (tmpPTS < -m_ptsIncrement ||
2071 tmpPTS > m_ptsIncrement*numframes)
2072 {
2073 if (tmpPTS != PTSdiscrep)
2074 {
2075 PTSdiscrep = tmpPTS;
2076 LOG(VB_PROCESS, LOG_INFO,
2077 QString("Found invalid PTS (off by %1) at %2")
2078 .arg(PtsTime(tmpPTS),
2079 PtsTime(origvPTS / 300)));
2080 }
2081 if (fix)
2082 curFrame->m_pkt->pts = origvPTS / 300;
2083 }
2084 else
2085 {
2086 origvPTS = curFrame->m_pkt->pts * 300;
2087 }
2088 ptsinc((uint64_t *)&origvPTS,
2089 (uint64_t)(150 * m_ptsIncrement * GetNbFields(curFrame)));
2090}
2091
2093{
2094 LOG(VB_GENERAL, LOG_INFO, "=========================================");
2095 LOG(VB_GENERAL, LOG_INFO, QString("List contains %1 items")
2096 .arg(list->count()));
2097
2098 for (auto *curFrame : std::as_const(*list))
2099 {
2100 LOG(VB_GENERAL, LOG_INFO,
2101 QString("VID: %1 #:%2 nb: %3 pts: %4 dts: %5 pos: %6")
2102 .arg(GetFrameTypeT(curFrame))
2103 .arg(GetFrameNum(curFrame))
2104 .arg(GetNbFields(curFrame))
2105 .arg(PtsTime(curFrame->m_pkt->pts),
2106 PtsTime(curFrame->m_pkt->dts),
2107 QString::number(curFrame->m_pkt->pos)));
2108 }
2109 LOG(VB_GENERAL, LOG_INFO, "=========================================");
2110}
2111
2113{
2114 // NOTE: expectedvPTS/DTS are in units of SCR (300*PTS) to allow for better
2115 // accounting of rounding errors (still won't be right, but better)
2116 int64_t lastPTS = 0;
2117 int64_t deltaPTS = 0;
2118 std::array<int64_t,N_AUDIO> origaPTS {};
2119 int64_t cutStartPTS = 0;
2120 int64_t cutEndPTS = 0;
2121 uint64_t frame_count = 0;
2122 int new_discard_state = 0;
2123 QMap<int, int> af_dlta_cnt;
2124 QMap<int, int> cutState;
2125
2126 AVPacket *pkt = av_packet_alloc();
2127 AVPacket *lastRealvPkt = av_packet_alloc();
2128 if ((pkt == nullptr) || (lastRealvPkt == nullptr))
2129 {
2130 LOG(VB_GENERAL, LOG_ERR, "packet allocation failed");
2131 return GENERIC_EXIT_NOT_OK;
2132 }
2133
2134 if (!InitAV(m_infile, m_format, 0))
2135 {
2136 av_packet_free(&pkt);
2137 av_packet_free(&lastRealvPkt);
2138 return GENERIC_EXIT_NOT_OK;
2139 }
2140
2141 if (!FindStart())
2142 {
2143 av_packet_free(&pkt);
2144 av_packet_free(&lastRealvPkt);
2145 return GENERIC_EXIT_NOT_OK;
2146 }
2147
2148 m_ptsIncrement = m_vFrame.first()->m_mpeg2_seq.frame_period / 300;
2149
2150 int64_t initPTS = m_vFrame.first()->m_pkt->pts;
2151
2152 LOG(VB_GENERAL, LOG_INFO, QString("#%1 PTS:%2 Delta: 0.0ms queue: %3")
2153 .arg(m_vidId).arg(PtsTime(m_vFrame.first()->m_pkt->pts))
2154 .arg(m_vFrame.count()));
2155
2156 for (auto it = m_aFrame.begin(); it != m_aFrame.end(); it++)
2157 {
2158 FrameList *af = (*it);
2159 deltaPTS = diff2x33(m_vFrame.first()->m_pkt->pts, af->first()->m_pkt->pts);
2160 LOG(VB_GENERAL, LOG_INFO,
2161 QString("#%1 PTS:%2 Delta: %3ms queue: %4")
2162 .arg(it.key()) .arg(PtsTime(af->first()->m_pkt->pts))
2163 .arg(1000.0*deltaPTS / 90000.0).arg(af->count()));
2164
2165 if (cmp2x33(af->first()->m_pkt->pts, initPTS) < 0)
2166 initPTS = af->first()->m_pkt->pts;
2167 }
2168
2169 initPTS -= 16200; //0.18 seconds back to prevent underflow
2170
2171 PTSOffsetQueue poq(m_vidId, m_aFrame.keys(), initPTS);
2172
2173 LOG(VB_PROCESS, LOG_INFO,
2174 QString("ptsIncrement: %1 Frame #: %2 PTS-adjust: %3")
2175 .arg(m_ptsIncrement).arg(GetFrameNum(m_vFrame.first()))
2176 .arg(PtsTime(initPTS)));
2177
2178
2179 int64_t origvPTS = 300 * udiff2x33(m_vFrame.first()->m_pkt->pts,
2181 int64_t expectedvPTS = 300 * (udiff2x33(m_vFrame.first()->m_pkt->pts, initPTS) -
2182 (m_ptsIncrement * GetFrameNum(m_vFrame.first())));
2183 int64_t expectedDTS = expectedvPTS - (300 * m_ptsIncrement);
2184
2185 if (m_discard)
2186 {
2187 cutStartPTS = origvPTS / 300;
2188 }
2189
2190 for (auto it = m_aFrame.begin(); it != m_aFrame.end(); it++)
2191 {
2192 FrameList *af = (*it);
2193 origaPTS[it.key()] = af->first()->m_pkt->pts * 300;
2194 //expectedPTS[it.key()] = udiff2x33(af->first()->m_pkt->pts, initPTS);
2195 af_dlta_cnt[it.key()] = 0;
2196 cutState[it.key()] = static_cast<int>(m_discard);
2197 }
2198
2199 ShowRangeMap(&m_delMap, "Cutlist:");
2200 ShowRangeMap(&m_saveMap, "Same Range:");
2201
2202 InitReplex();
2203
2204 while (!m_fileEnd)
2205 {
2206 /* read packet */
2207 int ret = GetFrame(pkt);
2208 if (ret < 0)
2209 {
2210 av_packet_free(&pkt);
2211 av_packet_free(&lastRealvPkt);
2212 return ret;
2213 }
2214
2215 if (!m_vFrame.isEmpty() && (m_fileEnd || m_vFrame.last()->m_isSequence))
2216 {
2217 m_displayFrame = 0;
2218
2219 // since we might reorder the frames when coming out of a cutpoint
2220 // me need to save the first frame here, as it is guaranteed to
2221 // have a sequence header.
2222 MPEG2frame *seqFrame = m_vFrame.first();
2223
2224 if (!seqFrame->m_isSequence)
2225 {
2226 LOG(VB_GENERAL, LOG_WARNING,
2227 QString("Problem: Frame %1 (type %2) doesn't contain "
2228 "sequence header!")
2229 .arg(frame_count) .arg(GetFrameTypeT(seqFrame)));
2230 }
2231
2232 if (m_ptsIncrement != seqFrame->m_mpeg2_seq.frame_period / 300)
2233 {
2234 LOG(VB_GENERAL, LOG_WARNING,
2235 QString("WARNING - Unsupported FPS change from %1 to %2")
2236 .arg(90000.0 / m_ptsIncrement, 0, 'f', 2)
2237 .arg(27000000.0 / seqFrame->m_mpeg2_seq.frame_period,
2238 0, 'f', 2));
2239 }
2240
2241 for (int frame_pos = 0; frame_pos < m_vFrame.count() - 1;)
2242 {
2243 bool ptsorder_eq_dtsorder = false;
2244 int64_t PTSdiscrep = 0;
2245 FrameList Lreorder;
2246 MPEG2frame *markedFrame = nullptr;
2247 MPEG2frame *markedFrameP = nullptr;
2248
2249 if (expectedvPTS != expectedDTS + (m_ptsIncrement * 300))
2250 {
2251 LOG(VB_GENERAL, LOG_ERR,
2252 QString("expectedPTS != expectedDTS + ptsIncrement"));
2253 LOG(VB_GENERAL, LOG_ERR, QString("%1 != %2 + %3")
2254 .arg(PtsTime(expectedvPTS / 300),
2255 PtsTime(expectedDTS / 300),
2257 LOG(VB_GENERAL, LOG_ERR, QString("%1 != %2 + %3")
2258 .arg(expectedvPTS)
2259 .arg(expectedDTS)
2260 .arg(m_ptsIncrement));
2261 av_packet_free(&pkt);
2262 av_packet_free(&lastRealvPkt);
2263 return GENERIC_EXIT_NOT_OK;
2264 }
2265
2266 //reorder frames in presentation order (to the next I/P frame)
2267 Lreorder = ReorderDTStoPTS(&m_vFrame, frame_pos);
2268
2269 //First pass at fixing PTS values (fixes gross errors only)
2270 for (auto *curFrame : std::as_const(Lreorder))
2271 {
2272 poq.UpdateOrigPTS(m_vidId, origvPTS, curFrame->m_pkt);
2273 InitialPTSFixup(curFrame, origvPTS, PTSdiscrep,
2274 m_maxFrames, true);
2275 }
2276
2277 // if there was a PTS jump, find the largest change
2278 // in the next x frames
2279 // At the end of this, vFrame should look just like it did
2280 // beforehand
2281 if (PTSdiscrep && !m_fileEnd)
2282 {
2283 int pos = m_vFrame.count();
2284 int count = Lreorder.count();
2285 while (m_vFrame.count() - frame_pos - count < 20 && !m_fileEnd)
2286 {
2287 ret = GetFrame(pkt);
2288 if (ret < 0)
2289 {
2290 av_packet_free(&pkt);
2291 av_packet_free(&lastRealvPkt);
2292 return ret;
2293 }
2294 }
2295
2296 if (!m_fileEnd)
2297 {
2298 int64_t tmp_origvPTS = origvPTS;
2299 int numframes = (m_maxFrames > 1) ? m_maxFrames - 1 : 1;
2300 bool done = false;
2301 while (!done &&
2302 (frame_pos + count + 1) < m_vFrame.count())
2303 {
2304 FrameList tmpReorder;
2305 tmpReorder = ReorderDTStoPTS(&m_vFrame,
2306 frame_pos + count);
2307 for (auto *curFrame : std::as_const(tmpReorder))
2308 {
2309 int64_t tmpPTSdiscrep = 0;
2310 InitialPTSFixup(curFrame, tmp_origvPTS,
2311 tmpPTSdiscrep, numframes, false);
2312 if (!tmpPTSdiscrep)
2313 {
2314 //discrepancy was short-lived, continue on
2315 done = true;
2316 PTSdiscrep = 0;
2317 break;
2318 }
2319 if (tmpPTSdiscrep != AV_NOPTS_VALUE &&
2320 tmpPTSdiscrep != PTSdiscrep)
2321 PTSdiscrep = tmpPTSdiscrep;
2322 }
2323 count += tmpReorder.count();
2324 }
2325 }
2326
2327 // push extra read frames onto 'unreadFrames' queue
2328 while (m_vFrame.count() > pos)
2329 {
2330 m_unreadFrames.enqueue(m_vFrame.takeAt(pos));
2331 }
2332 m_fileEnd = false;
2333 }
2334
2335 //check for cutpoints and convert to I-frames if needed
2336 for (int curIndex = 0; curIndex < Lreorder.count(); curIndex++)
2337 {
2338 MPEG2frame *curFrame = Lreorder.at(curIndex);
2339 if (!m_saveMap.isEmpty())
2340 {
2341 if (m_saveMap.begin().key() <= frame_count)
2342 m_saveMap.remove(m_saveMap.begin().key());
2343 if (!m_saveMap.empty() && m_saveMap.begin().value() == 0)
2344 {
2345 LOG(VB_GENERAL, LOG_INFO,
2346 QString("Saving frame #%1") .arg(frame_count));
2347
2348 if (GetFrameTypeT(curFrame) != 'I' &&
2349 ConvertToI(&Lreorder, frame_pos))
2350 {
2351 av_packet_free(&pkt);
2352 av_packet_free(&lastRealvPkt);
2354 }
2355
2356 WriteFrame(QString("save%1.yuv").arg(frame_count),
2357 curFrame);
2358 }
2359 }
2360
2361 if (!m_delMap.empty() && m_delMap.begin().key() <= frame_count)
2362 {
2363 new_discard_state = m_delMap.begin().value();
2364 LOG(VB_GENERAL, LOG_INFO,
2365 QString("Del map found %1 at %2 (%3)")
2366 .arg(new_discard_state) .arg(frame_count)
2367 .arg(m_delMap.begin().key()));
2368
2369 m_delMap.remove(m_delMap.begin().key());
2370 markedFrameP = curFrame;
2371
2372 if (!new_discard_state)
2373 {
2374 cutEndPTS = markedFrameP->m_pkt->pts;
2375 poq.SetNextPTS(
2376 diff2x33(cutEndPTS, expectedvPTS / 300),
2377 cutEndPTS);
2378 }
2379 else
2380 {
2381 cutStartPTS =
2382 add2x33(markedFrameP->m_pkt->pts,
2384 GetNbFields(markedFrameP) / 2);
2385 for (auto it3 = m_aFrame.begin();
2386 it3 != m_aFrame.end(); it3++)
2387 {
2388 cutState[it3.key()] = 1;
2389 }
2390 }
2391
2392 // Rebuild when 'B' frame, or completing a cut, and the
2393 // marked frame is a 'P' frame.
2394 // After conversion, frames will be in linear order.
2395 if ((GetFrameTypeT(curFrame) == 'B') ||
2396 (!new_discard_state &&
2397 (GetFrameTypeT(curFrame) == 'P')))
2398 {
2399 if (ConvertToI(&Lreorder, frame_pos))
2400 {
2401 av_packet_free(&pkt);
2402 av_packet_free(&lastRealvPkt);
2404 }
2405 ptsorder_eq_dtsorder = true;
2406 }
2407 else if (!new_discard_state &&
2408 GetFrameTypeT(curFrame) == 'I')
2409 {
2410 m_vFrame.move(frame_pos, frame_pos + curIndex);
2411 ptsorder_eq_dtsorder = true;
2412 }
2413
2414 //convert from presentation-order to decode-order
2415 markedFrame = m_vFrame.at(frame_pos + curIndex);
2416
2417 if (!new_discard_state)
2418 {
2419 AddSequence(markedFrame, seqFrame);
2420 RenumberFrames(frame_pos + curIndex,
2421 - GetFrameNum(markedFrame));
2422 }
2423 }
2424
2425 frame_count++;
2426 }
2427
2428 if (!Lreorder.isEmpty())
2429 {
2430 av_packet_unref(lastRealvPkt);
2431 av_packet_ref(lastRealvPkt, Lreorder.last()->m_pkt);
2432 }
2433
2434 if (markedFrame || !m_discard)
2435 {
2436 int64_t dtsExtra = 0;
2437 //check for PTS discontinuity
2438 for (auto *curFrame : std::as_const(Lreorder))
2439 {
2440 if (markedFrameP && m_discard)
2441 {
2442 if (curFrame != markedFrameP)
2443 continue;
2444
2445 markedFrameP = nullptr;
2446 }
2447
2448 dec2x33(&curFrame->m_pkt->pts,
2449 poq.Get(m_vidId, curFrame->m_pkt));
2450 deltaPTS = diff2x33(curFrame->m_pkt->pts,
2451 expectedvPTS / 300);
2452
2453 if (deltaPTS < -2 || deltaPTS > 2)
2454 {
2455 LOG(VB_PROCESS, LOG_INFO,
2456 QString("PTS discrepancy: %1 != %2 on "
2457 "%3-Type (%4)")
2458 .arg(curFrame->m_pkt->pts)
2459 .arg(expectedvPTS / 300)
2460 .arg(GetFrameTypeT(curFrame))
2461 .arg(GetFrameNum(curFrame)));
2462 }
2463
2464 //remove repeat_first_field if necessary
2465 if (m_noRepeat)
2466 SetRepeat(curFrame, 2, false);
2467
2468 //force PTS to stay in sync (this could be a bad idea!)
2469 if (m_fixPts)
2470 curFrame->m_pkt->pts = expectedvPTS / 300;
2471
2472 if (deltaPTS > m_ptsIncrement*m_maxFrames)
2473 {
2474 LOG(VB_GENERAL, LOG_NOTICE,
2475 QString("Need to insert %1 frames > max "
2476 "allowed: %2. Assuming bad PTS")
2477 .arg((int)(deltaPTS / m_ptsIncrement))
2478 .arg(m_maxFrames));
2479 curFrame->m_pkt->pts = expectedvPTS / 300;
2480 deltaPTS = 0;
2481 }
2482
2483 lastPTS = expectedvPTS;
2484 expectedvPTS += 150 * m_ptsIncrement *
2485 GetNbFields(curFrame);
2486
2487 if (curFrame == markedFrameP && new_discard_state)
2488 break;
2489 }
2490
2491 // dtsExtra is applied at the end of this block if the
2492 // current tail has repeat_first_field set
2493 if (ptsorder_eq_dtsorder)
2494 dtsExtra = 0;
2495 else
2496 dtsExtra = 150 * m_ptsIncrement *
2497 (GetNbFields(m_vFrame.at(frame_pos)) - 2);
2498
2499 if (!markedFrame && deltaPTS > (4 * m_ptsIncrement / 5))
2500 {
2501 // if we are off by more than 1/2 frame, it is time to
2502 // add a frame
2503 // The frame(s) will be added right after lVpkt_tail,
2504 // and lVpkt_head will be adjusted accordingly
2505
2506 m_vFrame.at(frame_pos)->m_pkt->pts = lastPTS / 300;
2507 ret = InsertFrame(GetFrameNum(m_vFrame.at(frame_pos)),
2508 deltaPTS, m_ptsIncrement, 0);
2509
2510 if (ret < 0)
2511 {
2512 av_packet_free(&pkt);
2513 av_packet_free(&lastRealvPkt);
2515 }
2516
2517 for (int index = frame_pos + Lreorder.count();
2518 ret && index < m_vFrame.count(); index++, --ret)
2519 {
2520 lastPTS = expectedvPTS;
2521 expectedvPTS += 150 * m_ptsIncrement *
2522 GetNbFields(m_vFrame.at(index));
2523 Lreorder.append(m_vFrame.at(index));
2524 }
2525 }
2526
2527 // Set DTS (ignore any current values), and send frame to
2528 // multiplexer
2529
2530 for (int i = 0; i < Lreorder.count(); i++, frame_pos++)
2531 {
2532 MPEG2frame *curFrame = m_vFrame.at(frame_pos);
2533 if (m_discard)
2534 {
2535 if (curFrame != markedFrame)
2536 continue;
2537
2538 m_discard = false;
2539 markedFrame = nullptr;
2540 }
2541
2542 // Make clang-tidy null dereference checker happy.
2543 if (curFrame == nullptr)
2544 continue;
2545 curFrame->m_pkt->dts = (expectedDTS / 300);
2546#if 0
2547 if (GetFrameTypeT(curFrame) == 'B')
2548 curFrame->m_pkt->pts = (expectedDTS / 300);
2549#endif
2550 expectedDTS += 150 * m_ptsIncrement *
2551 ((!ptsorder_eq_dtsorder && i == 0) ? 2 :
2552 GetNbFields(curFrame));
2553 LOG(VB_FRAME, LOG_INFO,
2554 QString("VID: %1 #:%2 nb: %3 pts: %4 dts: %5 "
2555 "pos: %6")
2556 .arg(GetFrameTypeT(curFrame))
2557 .arg(GetFrameNum(curFrame))
2558 .arg(GetNbFields(curFrame))
2559 .arg(PtsTime(curFrame->m_pkt->pts),
2560 PtsTime(curFrame->m_pkt->dts),
2561 QString::number(curFrame->m_pkt->pos)));
2562 if (AddFrame(curFrame))
2563 {
2564 av_packet_free(&pkt);
2565 av_packet_free(&lastRealvPkt);
2566 return GENERIC_EXIT_DEADLOCK;
2567 }
2568
2569 if (curFrame == markedFrame)
2570 {
2571 markedFrame = nullptr;
2572 m_discard = true;
2573 }
2574 }
2575
2576 expectedDTS += dtsExtra;
2577 }
2578 else
2579 {
2580 frame_pos += Lreorder.count();
2581 }
2582 if (PTSdiscrep)
2583 poq.SetNextPos(add2x33(poq.Get(m_vidId, lastRealvPkt),
2584 PTSdiscrep), lastRealvPkt);
2585 }
2586
2587 if (m_discard)
2588 cutEndPTS = lastRealvPkt->pts;
2589
2590 if (m_fileEnd)
2591 m_useSecondary = false;
2592 if (m_vFrame.count() > 1 || m_fileEnd)
2594 }
2595
2596 for (auto it = m_aFrame.begin(); it != m_aFrame.end(); it++)
2597 {
2598 FrameList *af = (*it);
2599 AVCodecContext *CC = getCodecContext(it.key());
2600 AVCodecParserContext *CPC = getCodecParserContext(it.key());
2601 bool backwardsPTS = false;
2602
2603 while (!af->isEmpty())
2604 {
2605 if (!CC || !CPC)
2606 {
2607 m_framePool.enqueue(af->takeFirst());
2608 continue;
2609 }
2610 // What to do if the CC is corrupt?
2611 // Just wait and hope it repairs itself
2612 if (CC->sample_rate == 0 || !CPC || CPC->duration == 0)
2613 break;
2614
2615 // The order of processing frames is critical to making
2616 // everything work. Backwards PTS discrepancies complicate
2617 // the processing significantly
2618 // Processing works as follows:
2619 // detect whether there is a discontinuous PTS (tmpPTS != 0)
2620 // in the audio stream only.
2621 // next check if a cutpoint is active, and discard frames
2622 // as needed
2623 // next check that the current PTS < last video PTS
2624 // if we get this far, update the expected PTS, and write out
2625 // the audio frame
2626 int64_t incPTS =
2627 90000LL * (int64_t)CPC->duration / CC->sample_rate;
2628
2629 if (poq.UpdateOrigPTS(it.key(), origaPTS[it.key()],
2630 af->first()->m_pkt) < 0)
2631 {
2632 backwardsPTS = true;
2633 af_dlta_cnt[it.key()] = 0;
2634 }
2635
2636 int64_t tmpPTS = diff2x33(af->first()->m_pkt->pts,
2637 origaPTS[it.key()] / 300);
2638
2639 if (tmpPTS < -incPTS)
2640 {
2641#ifdef DEBUG_AUDIO
2642 LOG(VB_PROCESS, LOG_INFO,
2643 QString("Aud discard: PTS %1 < %2")
2644 .arg(PtsTime(af->first()->m_pkt->pts))
2645 .arg(PtsTime(origaPTS[it.key()] / 300)));
2646#endif
2647 m_framePool.enqueue(af->takeFirst());
2648 af_dlta_cnt[it.key()] = 0;
2649 continue;
2650 }
2651
2652 if (tmpPTS > incPTS * m_maxFrames)
2653 {
2654 LOG(VB_PROCESS, LOG_INFO,
2655 QString("Found invalid audio PTS (off by %1) at %2")
2656 .arg(PtsTime(tmpPTS),
2657 PtsTime(origaPTS[it.key()] / 300)));
2658 if (backwardsPTS && tmpPTS < 90000LL)
2659 {
2660 //there are missing audio frames
2661 LOG(VB_PROCESS, LOG_INFO,
2662 "Fixing missing audio frames");
2663 ptsinc((uint64_t *)&origaPTS[it.key()], 300 * tmpPTS);
2664 backwardsPTS = false;
2665 }
2666 else if (tmpPTS < 90000LL * 4) // 4 seconds
2667 {
2668 if (af_dlta_cnt[it.key()] >= 20)
2669 {
2670 //If there are 20 consecutive frames with an
2671 //offset < 4sec, assume a mismatch and correct.
2672 //Note: if we allow too much discrepancy,
2673 //we could overrun the video queue
2674 ptsinc((uint64_t *)&origaPTS[it.key()],
2675 300 * tmpPTS);
2676 af_dlta_cnt[it.key()] = 0;
2677 }
2678 else
2679 {
2680 af_dlta_cnt[it.key()]++;
2681 }
2682 }
2683 af->first()->m_pkt->pts = origaPTS[it.key()] / 300;
2684 }
2685 else if (tmpPTS > incPTS) //correct for small discrepancies
2686 {
2687 incPTS += incPTS;
2688 backwardsPTS = false;
2689 af_dlta_cnt[it.key()] = 0;
2690 }
2691 else
2692 {
2693 backwardsPTS = false;
2694 af_dlta_cnt[it.key()] = 0;
2695 }
2696
2697 int64_t nextPTS = add2x33(af->first()->m_pkt->pts,
2698 90000LL * (int64_t)CPC->duration / CC->sample_rate);
2699
2700 if ((cutState[it.key()] == 1 &&
2701 cmp2x33(nextPTS, cutStartPTS) > 0) ||
2702 (cutState[it.key()] == 2 &&
2703 cmp2x33(af->first()->m_pkt->pts, cutEndPTS) < 0))
2704 {
2705#ifdef DEBUG_AUDIO
2706 LOG(VB_PROCESS, LOG_INFO,
2707 QString("Aud in cutpoint: %1 > %2 && %3 < %4")
2708 .arg(PtsTime(nextPTS)).arg(PtsTime(cutStartPTS))
2709 .arg(PtsTime(af->first()->m_pkt->pts))
2710 .arg(PtsTime(cutEndPTS)));
2711#endif
2712 m_framePool.enqueue(af->takeFirst());
2713 cutState[it.key()] = 2;
2714 ptsinc((uint64_t *)&origaPTS[it.key()], incPTS * 300);
2715 continue;
2716 }
2717
2718 int64_t deltaPTS2 = poq.Get(it.key(), af->first()->m_pkt);
2719
2720 if (udiff2x33(nextPTS, deltaPTS2) * 300 > expectedDTS &&
2721 cutState[it.key()] != 1)
2722 {
2723#ifdef DEBUG_AUDIO
2724 LOG(VB_PROCESS, LOG_INFO, QString("Aud not ready: %1 > %2")
2725 .arg(PtsTime(udiff2x33(nextPTS, deltaPTS2)))
2726 .arg(PtsTime(expectedDTS / 300)));
2727#endif
2728 break;
2729 }
2730
2731 if (cutState[it.key()] == 2)
2732 cutState[it.key()] = 0;
2733
2734 ptsinc((uint64_t *)&origaPTS[it.key()], incPTS * 300);
2735
2736 dec2x33(&af->first()->m_pkt->pts, deltaPTS2);
2737
2738#if 0
2739 expectedPTS[it.key()] = udiff2x33(nextPTS, initPTS);
2740 write_audio(lApkt_tail->m_pkt, initPTS);
2741#endif
2742 LOG(VB_FRAME, LOG_INFO, QString("AUD #%1: pts: %2 pos: %3")
2743 .arg(it.key())
2744 .arg(PtsTime(af->first()->m_pkt->pts))
2745 .arg(af->first()->m_pkt->pos));
2746 if (AddFrame(af->first()))
2747 {
2748 av_packet_free(&pkt);
2749 av_packet_free(&lastRealvPkt);
2750 return GENERIC_EXIT_DEADLOCK;
2751 }
2752 m_framePool.enqueue(af->takeFirst());
2753 }
2754 }
2755 }
2756
2757 m_rx.m_done = 1;
2758 pthread_mutex_lock( &m_rx.m_mutex );
2759 pthread_cond_signal(&m_rx.m_cond);
2760 pthread_mutex_unlock( &m_rx.m_mutex );
2761 int ex = REENCODE_OK;
2762 void *errors = nullptr; // mythtv#244: return error if any write or close failures
2763 pthread_join(m_thread, &errors);
2764 if (*(int *)errors) {
2765 LOG(VB_GENERAL, LOG_ERR,
2766 QString("joined thread failed with %1 write errors")
2767 .arg(*(int *)errors));
2768 ex = REENCODE_ERROR;
2769 }
2770
2771 av_packet_free(&pkt);
2772 av_packet_free(&lastRealvPkt);
2773 avformat_close_input(&m_inputFC);
2774 m_inputFC = nullptr;
2775 return ex;
2776}
2777
2778#ifdef NO_MYTH
2779int verboseMask = VB_GENERAL;
2780
2781void usage(char *s)
2782{
2783 fprintf(stderr, "%s usage:\n", s);
2784 fprintf(stderr, "\t--infile <file> -i <file> : Input mpg file\n");
2785 fprintf(stderr, "\t--outfile <file> -o <file> : Output mpg file\n");
2786 fprintf(stderr, "\t--dbg_lvl # -d # : Debug level\n");
2787 fprintf(stderr, "\t--maxframes # -m # : Max frames to insert at once (default=10)\n");
2788 fprintf(stderr, "\t--cutlist \"start - end\" -c : Apply a cutlist. Specify on e'-c' per cut\n");
2789 fprintf(stderr, "\t--no3to2 -t : Remove 3:2 pullup\n");
2790 fprintf(stderr, "\t--fixup -f : make PTS continuous\n");
2791 fprintf(stderr, "\t--ostream <dvd|ps> -e : Output stream type (defaults to ps)\n");
2792 fprintf(stderr, "\t--showprogress -p : show progress\n");
2793 fprintf(stderr, "\t--help -h : This screen\n");
2794 exit(0);
2795}
2796
2797int main(int argc, char **argv)
2798{
2799 QStringList cutlist;
2800 QStringList savelist;
2801 char *infile = nullptr, *outfile = nullptr, *format = nullptr;
2802 int no_repeat = 0, fix_PTS = 0, max_frames = 20, otype = REPLEX_MPEG2;
2803 bool showprogress = 0;
2804 const struct option long_options[] =
2805 {
2806 {"infile", required_argument, nullptr, 'i'},
2807 {"outfile", required_argument, nullptr, 'o'},
2808 {"format", required_argument, nullptr, 'r'},
2809 {"dbg_lvl", required_argument, nullptr, 'd'},
2810 {"cutlist", required_argument, nullptr, 'c'},
2811 {"saveframe", required_argument, nullptr, 's'},
2812 {"ostream", required_argument, nullptr, 'e'},
2813 {"no3to2", no_argument, nullptr, 't'},
2814 {"fixup", no_argument, nullptr, 'f'},
2815 {"showprogress", no_argument, nullptr, 'p'},
2816 {"help", no_argument , nullptr, 'h'},
2817 {0, 0, 0, 0}
2818 };
2819
2820 while (1)
2821 {
2822 int option_index = 0;
2823 char c;
2824 c = getopt_long (argc, argv, "i:o:d:r:m:c:s:e:tfph",
2825 long_options, &option_index);
2826
2827 if (c == -1)
2828 break;
2829
2830 switch (c)
2831 {
2832
2833 case 'i':
2834 infile = optarg;
2835 break;
2836
2837 case 'o':
2838 outfile = optarg;
2839 break;
2840
2841 case 'r':
2842 format = optarg;
2843 break;
2844
2845 case 'e':
2846 if (strlen(optarg) == 3 && strncmp(optarg, "dvd", 3) == 0)
2847 otype = REPLEX_DVD;
2848 break;
2849
2850 case 'd':
2851 verboseMask = atoi(optarg);
2852 break;
2853
2854 case 'm':
2855 max_frames = atoi(optarg);
2856 break;
2857
2858 case 'c':
2859 cutlist.append(optarg);
2860 break;
2861
2862 case 't':
2863 no_repeat = 1;
2864
2865 case 'f':
2866 fix_PTS = 1;
2867 break;
2868
2869 case 's':
2870 savelist.append(optarg);
2871 break;
2872
2873 case 'p':
2874 showprogress = true;
2875 break;
2876
2877 case 'h':
2878
2879 case '?':
2880
2881 default:
2882 usage(argv[0]);
2883 }
2884 }
2885
2886 if (infile == nullptr || outfile == nullptr)
2887 usage(argv[0]);
2888
2889 MPEG2fixup m2f(infile, outfile, nullptr, format,
2890 no_repeat, fix_PTS, max_frames,
2891 showprogress, otype);
2892
2893 if (cutlist.count())
2894 m2f.AddRangeList(cutlist, MPF_TYPE_CUTLIST);
2895 if (savelist.count())
2896 m2f.AddRangeList(savelist, MPF_TYPE_SAVELIST);
2897 return m2f.Start();
2898}
2899#endif
2900
2902 frm_pos_map_t &posMap,
2903 frm_pos_map_t &durMap)
2904{
2905 LOG(VB_GENERAL, LOG_INFO, "Generating Keyframe Index");
2906
2907 int count = 0;
2908
2909 /*============ initialise AV ===============*/
2910 m_vidId = -1;
2911 if (!InitAV(file, nullptr, 0))
2912 return GENERIC_EXIT_NOT_OK;
2913
2914 if (m_mkvFile)
2915 {
2916 LOG(VB_GENERAL, LOG_INFO, "Seek tables are not required for MKV");
2917 return GENERIC_EXIT_NOT_OK;
2918 }
2919
2920 AVPacket *pkt = av_packet_alloc();
2921 if (pkt == nullptr)
2922 {
2923 LOG(VB_GENERAL, LOG_ERR, "packet allocation failed");
2924 return GENERIC_EXIT_NOT_OK;
2925 }
2926
2927 uint64_t totalDuration = 0;
2928 while (av_read_frame(m_inputFC, pkt) >= 0)
2929 {
2930 if (pkt->stream_index == m_vidId)
2931 {
2932 if (pkt->flags & AV_PKT_FLAG_KEY)
2933 {
2934 posMap[count] = pkt->pos;
2935 durMap[count] = totalDuration;
2936 }
2937
2938 // XXX totalDuration untested. Results should be the same
2939 // as from mythcommflag --rebuild.
2940
2941 // totalDuration calculation based on
2942 // AvFormatDecoder::PreProcessVideoPacket()
2943 totalDuration +=
2944 av_q2d(m_inputFC->streams[pkt->stream_index]->time_base) *
2945 pkt->duration * 1000; // msec
2946 count++;
2947 }
2948 av_packet_unref(pkt);
2949 }
2950
2951 // Close input file
2952 av_packet_free(&pkt);
2953 avformat_close_input(&m_inputFC);
2954 m_inputFC = nullptr;
2955
2956 LOG(VB_GENERAL, LOG_NOTICE, "Transcode Completed");
2957
2958 return REENCODE_OK;
2959}
2960
2961/*
2962 * vim:ts=4:sw=4:ai:et:si:sts=4
2963 */
bool InitAV(const QString &inputfile, const char *type, int64_t offset)
Definition: mpeg2fix.cpp:777
bool BuildFrame(AVPacket *pkt, const QString &fname)
Definition: mpeg2fix.cpp:1149
MPEG2frame * DecodeToFrame(int frameNum, int skip_reset)
Definition: mpeg2fix.cpp:1746
QString m_infile
Definition: mpeg2fix.h:255
static int GetFrameNum(const MPEG2frame *frame)
Definition: mpeg2fix.h:178
void AddSequence(MPEG2frame *frame1, MPEG2frame *frame2)
Definition: mpeg2fix.cpp:890
bool m_fileEnd
Definition: mpeg2fix.h:260
QMap< int, int > m_audMap
Definition: mpeg2fix.h:246
void RenumberFrames(int start_pos, int delta)
Definition: mpeg2fix.cpp:1705
bool m_allAudio
Definition: mpeg2fix.h:257
static void SetRepeat(MPEG2frame *vf, int nb_fields, bool topff)
Definition: mpeg2fix.cpp:1657
MPEG2replex m_rx
Definition: mpeg2fix.h:144
static void WriteYUV(const QString &filename, const mpeg2_info_t *info)
Definition: mpeg2fix.cpp:1087
static int64_t diff2x33(int64_t pts1, int64_t pts2)
Definition: mpeg2fix.cpp:382
QDateTime m_statusTime
Definition: mpeg2fix.h:264
pthread_t m_thread
Definition: mpeg2fix.h:238
void WriteFrame(const QString &filename, MPEG2frame *f)
Definition: mpeg2fix.cpp:1041
bool m_discard
Definition: mpeg2fix.h:250
static int GetNbFields(const MPEG2frame *frame)
Definition: mpeg2fix.h:197
static FrameList ReorderDTStoPTS(FrameList *dtsOrder, int pos)
Definition: mpeg2fix.cpp:2036
static char GetFrameTypeT(const MPEG2frame *frame)
Definition: mpeg2fix.h:186
uint64_t m_lastWrittenPos
Definition: mpeg2fix.h:269
void InitialPTSFixup(MPEG2frame *curFrame, int64_t &origvPTS, int64_t &PTSdiscrep, int numframes, bool fix) const
Definition: mpeg2fix.cpp:2053
int Start()
Definition: mpeg2fix.cpp:2112
static int FindMPEG2Header(const uint8_t *buf, int size, uint8_t code)
Definition: mpeg2fix.cpp:440
bool m_mkvFile
Definition: mpeg2fix.h:248
FrameQueue m_framePool
Definition: mpeg2fix.h:229
static void WriteData(const QString &filename, uint8_t *data, int size)
Definition: mpeg2fix.cpp:1131
static int GetFrameTypeN(const MPEG2frame *frame)
Definition: mpeg2fix.h:182
AVCodecParserContext * getCodecParserContext(uint id)
Definition: mpeg2fix.h:212
int m_vidId
Definition: mpeg2fix.h:244
int InsertFrame(int frameNum, int64_t deltaPTS, int64_t ptsIncrement, int64_t initPTS)
Definition: mpeg2fix.cpp:1893
AVCodecContext * getCodecContext(uint id)
Definition: mpeg2fix.h:206
static void inc2x33(int64_t *pts1, int64_t pts2)
Definition: mpeg2fix.cpp:366
int AddFrame(MPEG2frame *f)
Definition: mpeg2fix.cpp:670
static void * ReplexStart(void *data)
Definition: mpeg2fix.cpp:520
MPEG2frame * FindFrameNum(int frameNum)
Definition: mpeg2fix.cpp:1694
FrameList m_vSecondary
Definition: mpeg2fix.h:224
static int64_t add2x33(int64_t pts1, int64_t pts2)
Definition: mpeg2fix.cpp:407
static int64_t udiff2x33(int64_t pts1, int64_t pts2)
Definition: mpeg2fix.cpp:371
bool m_noRepeat
Definition: mpeg2fix.h:252
int m_extCount
Definition: mpeg2fix.h:245
mpeg2dec_t * m_headerDecoder
Definition: mpeg2fix.h:232
bool FindStart()
Definition: mpeg2fix.cpp:1524
static int cmp2x33(int64_t pts1, int64_t pts2)
Definition: mpeg2fix.cpp:415
MPEG2frame * GetPoolFrame(AVPacket *pkt)
Definition: mpeg2fix.cpp:1368
int ProcessVideo(MPEG2frame *vf, mpeg2dec_t *dec)
Definition: mpeg2fix.cpp:912
uint64_t m_fileSize
Definition: mpeg2fix.h:266
FrameList m_vFrame
Definition: mpeg2fix.h:227
void(* m_updateStatus)(float percent_done)
Definition: mpeg2fix.h:222
static void ShowRangeMap(frm_dir_map_t *mapPtr, QString msg)
Definition: mpeg2fix.cpp:2017
int BuildKeyframeIndex(const QString &file, frm_pos_map_t &posMap, frm_pos_map_t &durMap)
Definition: mpeg2fix.cpp:2901
int PlaybackSecondary()
Definition: mpeg2fix.cpp:1733
bool m_realFileEnd
Definition: mpeg2fix.h:261
int64_t m_ptsIncrement
Definition: mpeg2fix.h:247
bool m_useSecondary
Definition: mpeg2fix.h:225
static void SetFrameNum(uint8_t *ptr, int num)
Definition: mpeg2fix.cpp:885
void FrameInfo(MPEG2frame *f)
Definition: mpeg2fix.cpp:654
static void dumpList(FrameList *list)
Definition: mpeg2fix.cpp:2092
int m_frameNum
Definition: mpeg2fix.h:267
FrameQueue m_unreadFrames
Definition: mpeg2fix.h:230
int GetFrame(AVPacket *pkt)
Definition: mpeg2fix.cpp:1407
frm_dir_map_t m_saveMap
Definition: mpeg2fix.h:236
int GetStreamType(int id) const
Definition: mpeg2fix.h:201
frm_dir_map_t m_delMap
Definition: mpeg2fix.h:235
int m_displayFrame
Definition: mpeg2fix.h:231
int m_maxFrames
Definition: mpeg2fix.h:254
void AddRangeList(const QStringList &rangelist, int type)
Definition: mpeg2fix.cpp:1970
void InitReplex()
Definition: mpeg2fix.cpp:582
mpeg2dec_t * m_imgDecoder
Definition: mpeg2fix.h:233
MPEG2fixup(const QString &inf, const QString &outf, frm_dir_map_t *deleteMap, const char *fmt, bool norp, bool fixPTS, int maxf, bool showprog, int otype, void(*update_func)(float)=nullptr, int(*check_func)()=nullptr)
Definition: mpeg2fix.cpp:223
bool m_showProgress
Definition: mpeg2fix.h:265
static void dec2x33(int64_t *pts1, int64_t pts2)
Definition: mpeg2fix.cpp:361
int m_statusUpdateTime
Definition: mpeg2fix.h:268
int(* m_checkAbort)()
Definition: mpeg2fix.h:221
FrameMap m_aFrame
Definition: mpeg2fix.h:228
const char * m_format
Definition: mpeg2fix.h:256
AVFrame * m_picture
Definition: mpeg2fix.h:242
int ConvertToI(FrameList *orderedFrames, int headPos)
Definition: mpeg2fix.cpp:1823
AVFormatContext * m_inputFC
Definition: mpeg2fix.h:241
bool m_fixPts
Definition: mpeg2fix.h:253
void StoreSecondary()
Definition: mpeg2fix.cpp:1717
bool m_isGop
Definition: mpeg2fix.h:54
mpeg2_picture_t m_mpeg2_pic
Definition: mpeg2fix.h:59
MPEG2frame(int size)
Definition: mpeg2fix.cpp:86
AVPacket * m_pkt
Definition: mpeg2fix.h:52
void ensure_size(int size) const
Definition: mpeg2fix.cpp:98
bool m_isSequence
Definition: mpeg2fix.h:53
uint8_t * m_framePos
Definition: mpeg2fix.h:55
mpeg2_gop_t m_mpeg2_gop
Definition: mpeg2fix.h:58
void set_pkt(AVPacket *newpkt) const
Definition: mpeg2fix.cpp:114
mpeg2_sequence_t m_mpeg2_seq
Definition: mpeg2fix.h:57
uint8_t * m_gopPos
Definition: mpeg2fix.h:56
int WaitBuffers()
Definition: mpeg2fix.cpp:481
ExtTypeIntArray m_exttypcnt
Definition: mpeg2fix.h:105
ringbuffer m_vrBuf
Definition: mpeg2fix.h:99
pthread_mutex_t m_mutex
Definition: mpeg2fix.h:107
RingbufferArray m_indexExtrbuf
Definition: mpeg2fix.h:102
RingbufferArray m_extrbuf
Definition: mpeg2fix.h:100
multiplex_t * m_mplex
Definition: mpeg2fix.h:113
void Start()
Definition: mpeg2fix.cpp:531
AudioFrameArray m_extframe
Definition: mpeg2fix.h:109
ExtTypeIntArray m_exttype
Definition: mpeg2fix.h:104
sequence_t m_seq_head
Definition: mpeg2fix.h:110
pthread_cond_t m_cond
Definition: mpeg2fix.h:108
QString m_outfile
Definition: mpeg2fix.h:97
ringbuffer m_indexVrbuf
Definition: mpeg2fix.h:101
int m_otype
Definition: mpeg2fix.h:98
int m_extCount
Definition: mpeg2fix.h:103
int m_done
Definition: mpeg2fix.h:96
static void ThreadCleanup(void)
This is to be called on exit in those few threads that haven't been ported to MThread.
Definition: mthread.cpp:226
static void ThreadSetup(const QString &name)
This is to be called on startup in those few threads that haven't been ported to MThread.
Definition: mthread.cpp:221
bool GetBoolSetting(const QString &key, bool defaultval=false)
QList< int > m_keyList
Definition: mpeg2fix.h:80
void SetNextPos(int64_t newPTS, AVPacket *pkt)
Definition: mpeg2fix.cpp:180
void SetNextPTS(int64_t newPTS, int64_t atPTS)
Definition: mpeg2fix.cpp:167
int64_t Get(int idx, AVPacket *pkt)
Definition: mpeg2fix.cpp:137
int64_t UpdateOrigPTS(int idx, int64_t &origPTS, AVPacket *pkt)
Definition: mpeg2fix.cpp:202
QMap< int, QList< poq_idx_t > > m_offset
Definition: mpeg2fix.h:78
PTSOffsetQueue(int vidid, QList< int > keys, int64_t initPTS)
Definition: mpeg2fix.cpp:121
QMap< int, QList< poq_idx_t > > m_orig
Definition: mpeg2fix.h:79
unsigned int uint
Definition: compat.h:68
#define close
Definition: compat.h:31
@ GENERIC_EXIT_DEADLOCK
Transcode deadlock detected.
Definition: exitcodes.h:36
@ GENERIC_EXIT_WRITE_FRAME_ERROR
Frame write error.
Definition: exitcodes.h:35
@ GENERIC_EXIT_NOT_OK
Exited with error.
Definition: exitcodes.h:14
static uint32_t * tmp
Definition: goom_core.cpp:28
uint64_t verboseMask
Definition: logging.cpp:100
static constexpr int MAX_FRAMES
Definition: mpeg2fix.cpp:1367
#define O_LARGEFILE
Definition: mpeg2fix.cpp:50
static constexpr bool MATCH_HEADER(const uint8_t *ptr)
Definition: mpeg2fix.cpp:334
static int fill_buffers(void *r, int finish)
Definition: mpeg2fix.cpp:455
static QString PtsTime(int64_t pts)
Definition: mpeg2fix.cpp:70
static void my_av_print(void *ptr, int level, const char *fmt, va_list vl)
Definition: mpeg2fix.cpp:53
#define INDEX_BUF
Definition: mpeg2fix.cpp:581
static void SETBITS(unsigned char *ptr, long value, int num)
Definition: mpeg2fix.cpp:337
QList< MPEG2frame * > FrameList
Definition: mpeg2fix.h:116
@ MPF_TYPE_CUTLIST
Definition: mpeg2fix.h:40
@ MPF_TYPE_SAVELIST
Definition: mpeg2fix.h:41
int write_out_packs(multiplex_t *mx, int video_ok, aok_arr &ext_ok)
Definition: multiplex.cpp:600
void check_times(multiplex_t *mx, int *video_ok, aok_arr &ext_ok, int *start)
Definition: multiplex.cpp:500
void setup_multiplex(multiplex_t *mx)
Definition: multiplex.cpp:865
void init_multiplex(multiplex_t *mx, sequence_t *seq_head, audio_frame_t *extframe, int *exttype, const int *exttypcnt, uint64_t video_delay, uint64_t audio_delay, int fd, int(*fill_buffers)(void *p, int f), ringbuffer *vrbuffer, ringbuffer *index_vrbuffer, ringbuffer *extrbuffer, ringbuffer *index_extrbuffer, int otype)
Definition: multiplex.cpp:705
int finish_mpg(multiplex_t *mx)
Definition: multiplex.cpp:619
#define REPLEX_MPEG2
Definition: multiplex.h:43
#define REPLEX_TS_HD
Definition: multiplex.h:47
#define REPLEX_HDTV
Definition: multiplex.h:45
#define REPLEX_DVD
Definition: multiplex.h:44
#define REPLEX_TS_SD
Definition: multiplex.h:46
std::array< bool, N_AUDIO > aok_arr
Definition: multiplex.h:39
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
dictionary info
Definition: azlyrics.py:7
std::chrono::duration< CHRONO_TYPE, std::ratio< 1, 90000 > > pts
Definition: mythchrono.h:55
def write(text, progress=True)
Definition: mythburn.py:307
STL namespace.
static void ptsdec(uint64_t *pts1, uint64_t pts2)
Definition: pes.h:122
#define MAX_PTS
Definition: pes.h:52
static void ptsinc(uint64_t *pts1, uint64_t pts2)
Definition: pes.h:127
@ MARK_CUT_START
Definition: programtypes.h:55
@ MARK_CUT_END
Definition: programtypes.h:54
QMap< uint64_t, MarkTypes > frm_dir_map_t
Frame # -> Mark map.
Definition: programtypes.h:117
QMap< long long, long long > frm_pos_map_t
Frame # -> File offset map.
Definition: programtypes.h:44
static QString cleanup(const QString &str)
static void usage(char *progname)
Definition: replex.cpp:2415
void ring_destroy(ringbuffer *rbuf)
Definition: ringbuffer.cpp:86
int ring_reinit(ringbuffer *rbuf, int size)
Definition: ringbuffer.cpp:58
int ring_write(ringbuffer *rbuf, uint8_t *data, int count)
Definition: ringbuffer.cpp:92
int ring_init(ringbuffer *rbuf, int size)
Definition: ringbuffer.cpp:38
static unsigned int ring_avail(ringbuffer *rbuf)
Definition: ringbuffer.h:108
static unsigned int ring_free(ringbuffer *rbuf)
Definition: ringbuffer.h:101
void * priv
Definition: multiplex.h:88
int64_t newPTS
Definition: mpeg2fix.h:63
int64_t pos_pts
Definition: mpeg2fix.h:64
uint32_t size
Definition: ringbuffer.h:42
uint32_t bit_rate
Definition: element.h:94
uint32_t frame_rate
Definition: element.h:93
@ REENCODE_STOPPED
Definition: transcodedefs.h:9
@ REENCODE_OK
Definition: transcodedefs.h:7
@ REENCODE_ERROR
Definition: transcodedefs.h:8