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