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