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