Ticket #158: mpeg2trans_v3.patch

File mpeg2trans_v3.patch, 56.6 KB (added by Aaron McCarthy <mccarthy.aaron@…>, 15 years ago)

Add some of the changes from Shane Shrybman

  • programs/mythtranscode/mpeg2trans.cpp

     
    1 #include <cstdlib>
    2 #include <cstdio>
    3 #include <sys/types.h>
    4 #include <sys/stat.h>
    5 #include <fcntl.h>
    6 #include <unistd.h>
    7 #include <netinet/in.h>
    8 #include <cstdarg>
    9 #include <cstring>
    10 #include <getopt.h>
     1#include <qapplication.h>
     2#include <qsqldatabase.h>
    113
    124#include <iostream>
     5#include <iomanip>
     6
    137using namespace std;
    148
     9#include "avformat.h"
     10#include "avcodec.h"
     11
     12#include "mythcontext.h"
     13#include "programinfo.h"
     14#include "exitcodes.h"
     15#include "jobqueue.h"
     16
    1517#include "mpeg2trans.h"
    1618
    17 class GetBits
    18 {
    19   public:
    20     GetBits(uint8_t *data) { data_ptr = data; pos = 0; }
    21     uint32_t GetNext(int bits);
    22     void SkipNext(int bits) { pos += bits; }
    23     void MarkerBit(void);
    24   private:
    25     uint8_t *data_ptr;
    26     int pos;
    27 };
     19#define MPEG2trans_DEBUG
    2820
    29 class SetBits
     21MPEG2trans::MPEG2trans(ProgramInfo *pginfo, bool use_db)
    3022{
    31   public:
    32     SetBits(uint8_t *data) { data_ptr = data; pos = 0; }
    33     void SetNext(uint32_t value, int bits);
    34     void SkipNext(int bits) { pos += bits; }
    35   private:
    36     uint8_t *data_ptr;
    37     int pos;
    38 };
     23    m_pginfo = pginfo;
    3924
    40 #define get_pos(file_buf) ((file_buf)->pos - ((file_buf)->last_byte - (file_buf)->blkptr))
    41 #define READ_PTS ((GETBITS(0, 3)<<30) | (marker_bit() & 0) | (GETBITS(0, 15) << 15) | \
    42                   (marker_bit() & 0) | GETBITS(0, 15) | (marker_bit() & 0))
    43 #define READ_PTS_EXT (GETBITS(0, 9) | (marker_bit() & 0))
    44 #define PTS2FLOAT(pts) ((float)(pts).val * (outputContext->time_base.num) / (outputContext->time_base.den))
    45 #define PTS2INT(pts, st) ((int64_t)(pts).val * AV_TIME_BASE * (st->time_base.num) / (st->time_base.den))
    46 #define INT2PTS(val, st) ((val) * (st->time_base.den) / (st->time_base.num) / AV_TIME_BASE)
     25    chkTranscodeDB = use_db;
    4726
    48 const int ac3_ratetable[] = { 32,  40,  48,  56,  64,  80,  96, 112,
    49                          128, 160, 192, 224, 256, 320, 384, 448,
    50                          512, 576, 640};
    51 const uint8_t ac3_halfrate[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3};
     27    inputFC = NULL;
     28    outputFC = NULL;
    5229
    53 int mp2_debug = 0;
    54 void DPRINTF(int level, const char *format, ...)
    55 {
    56   if (level < mp2_debug) {
    57     va_list ap;
    58     va_start(ap, format);
    59     vprintf(format, ap);
    60     va_end(ap);
    61   }
     30    // Register all formats and codecs
     31    av_register_all();
    6232}
    6333
    64 void FrameBuffer::setPkt(AVPacket *newpkt, int64_t del)
     34MPEG2trans::~MPEG2trans()
    6535{
    66     if (newpkt->size + pkt.size > size)
    67     {
    68         pkt.data = (uint8_t *)realloc(pkt.data, pkt.size + newpkt->size);
    69         size = pkt.size + newpkt->size;
    70     }
    71     memcpy(pkt.data + pkt.size, newpkt->data, newpkt->size);
    72     pkt.size += newpkt->size;
    73     pkt.pts = newpkt->pts;
    74     pkt.flags = newpkt->flags;
    75     delta = del;
    7636}
    7737
    78 void FrameBuffer::setPkt(uint8_t *data, uint32_t newsize, int64_t newpts,
    79                          int64_t del)
     38bool MPEG2trans::InitAV(QString &inputfile)
    8039{
    81     AVPacket newpkt;
    82     newpkt.size = newsize;
    83     newpkt.pts = newpts;
    84     newpkt.data = data;
    85     newpkt.flags = 0;
    86     setPkt(&newpkt, del);
    87 }
     40    int ret;
    8841
    89 uint32_t GetBits::GetNext(int num)
    90 {
    91     uint32_t value = 0, gb_long;
    92     int offset, offset_r, offset_b;
     42    // Open recording
     43    ret = av_open_input_file(&inputFC, inputfile.ascii(), NULL, 0, NULL);
     44    if (ret != 0)
     45    {
     46        cerr << "Couldn't open input file, error #" << ret << endl;
     47        return false;
     48    }
    9349
    94     offset = pos >> 3;
    95     offset_r = pos & 0x07;
    96     offset_b = 32 - offset_r;
    97     gb_long = ntohl(*((uint32_t *) (data_ptr + offset)));
    98     value = gb_long >> (offset_b - num);
    99     value = value & (0xffffffff >> (32 - num));
    100     pos+=num;
    101     return value;
    102 }
    103 
    104 void GetBits::MarkerBit(void)
    105 {
    106     if (!GetNext(1))
     50    // Getting stream information
     51    ret = av_find_stream_info(inputFC);
     52    if (ret < 0)
    10753    {
    108         VERBOSE(VB_IMPORTANT, "Marker bit was not valid!");
    109         exit(TRANSCODE_BUGGY_EXIT_INVALID_MARKER);
     54        cerr << "Couldn't get stream info, error #" << ret << endl;
     55        av_close_input_file(inputFC);
     56        inputFC = NULL;
     57        return false;
    11058    }
    111 }
    11259
    113 void SetBits::SetNext(uint32_t value, int num)
    114 {
    115     uint32_t sb_long, mask;
    116     int offset, offset_r, offset_b;
     60    // Dump stream information
     61    dump_format(inputFC, 0, inputfile.ascii(), false);
    11762
    118     offset = pos >> 3;
    119     offset_r = pos & 0x07;
    120     offset_b = 32 - offset_r;
    121     mask = ~((uint32_t)((1 << num) -1) << (offset_b - num));
    122     sb_long = ntohl(*((unsigned long *) (data_ptr + offset)));
    123     value = value << (offset_b - num);
    124     sb_long = (sb_long & mask) | value;
    125     *((unsigned long *)(data_ptr + offset)) = htonl(sb_long);
    126     pos += num;
     63    return true;
    12764}
    12865
    129 MPEG2trans::MPEG2trans(QMap<long long, int> *map)
     66bool MPEG2trans::InitOutput(QString &outfile)
    13067{
    131     int i;
    132     video_width = 0;
    133     video_height = 0;
    134     video_aspect = 0;
    135     video_frame_rate = 29970;
    136     video_bitrate = 0;
    137     pic_frame_count = 0;
     68    int ret;
    13869
    139     audio_freq = 48000;
    140     audio_bitrate = 0;
     70    // Allocate output format context
     71    outputFC = av_alloc_format_context();
     72    if (!outputFC)
     73    {
     74        cerr << "Couldn't allocate output context\n";
     75        av_close_input_file(inputFC);
     76        inputFC = NULL;
     77        return false;
     78    }
    14179
    142     goppts_delta = 0;
     80    // Getting output file format
     81    AVOutputFormat *fmt = guess_format("dvd", outfile.ascii(), "video/mpeg");
     82//    AVOutputFormat *fmt = guess_format("vob", outfile.ascii(), "video/mpeg");
     83    if (!fmt)
     84    {
     85        cerr << "Couldn't get VOB output context\n";
     86        av_close_input_file(inputFC);
     87        inputFC = NULL;
     88        return false;
     89    }
    14390
    144     fixed_gop_ts = NULL;
     91    outputFC->oformat = fmt;
    14592
    146     chopping = false;
    147     chop_end = false;
    148     skipped_frames = 0;
     93    // Allocate output streams
     94    for (int i = 0; i < inputFC->nb_streams; i++)
     95    {
     96        AVStream *ist = inputFC->streams[i];
     97        AVStream *ost = av_new_stream(outputFC, ist->id);
    14998
    150     got_audio = false;
    151     got_video = false;
    152     init_av = false;
     99        ost->stream_copy = 1;
    153100
    154     outputContext = NULL;
    155     inputContext = NULL;
    156 
    157     audioout_st = NULL;
    158     videoout_st = NULL;
    159 
    160     last_frame_number = 0;
    161     last_gop_pts = 0;
    162     chop_start_pts = 0;
    163     video_frame_state = 0;
    164 
    165     for (i = 0; i < 50; i++)
    166     {
    167         audioPool.push(new FrameBuffer);
    168         videoPool.push(new FrameBuffer);
    169     }
    170     if (map)
    171     {
    172         QMap<long long, int>::Iterator mapIter;
    173         for (mapIter = map->begin(); mapIter != map->end(); mapIter++)
     101        // copy codec data from input streams
     102        if (ost->codec && ist->codec)
    174103        {
    175             if (mapIter.data())
     104            ost->codec->codec_id = ist->codec->codec_id;
     105            ost->codec->codec_type = ist->codec->codec_type;
     106            if (!ost->codec->codec_tag)
     107                ost->codec->codec_tag = ist->codec->codec_tag;
     108            ost->codec->bit_rate = ist->codec->bit_rate;
     109            ost->codec->extradata = ist->codec->extradata;
     110            ost->codec->extradata_size = ist->codec->extradata_size;
     111            switch (ost->codec->codec_type)
    176112            {
    177                 long long start;
    178                 start = mapIter.key();
    179                 mapIter++;
    180                 if (mapIter != map->end() && ! mapIter.data())
    181                     cutlistMap[start] = mapIter.key();
     113                case CODEC_TYPE_AUDIO:
     114                    ost->codec->sample_rate = ist->codec->sample_rate;
     115                    ost->codec->channels = ist->codec->channels;
     116                    ost->codec->frame_size = ist->codec->frame_size;
     117                    ost->codec->block_align = ist->codec->block_align;
     118                    break;
     119                case CODEC_TYPE_VIDEO:
     120                    ost->codec->time_base = ist->codec->time_base;
     121                    ost->codec->width = ist->codec->width;
     122                    ost->codec->height = ist->codec->height;
     123                    ost->codec->has_b_frames = ist->codec->has_b_frames;
     124                    break;
     125                default:
     126                    cerr << "dropping unknown stream type "
     127                         << ost->codec->codec_type << endl;
    182128            }
    183129        }
    184         cutlistIter = cutlistMap.begin();
    185130    }
    186     use_ac3 = false;
    187     audioin_index = -1;
    188     videoin_index = -1;
    189131
    190     av_register_all();
    191 }
     132    strncpy(outputFC->filename, outfile.ascii(), sizeof(outputFC->filename));
    192133
    193 MPEG2trans::~MPEG2trans(void) {
    194 };
     134    // Dump output format
     135    dump_format(outputFC, 0, outfile.ascii(), 1);
    195136
    196 void MPEG2trans::write_muxed_frame(AVStream *stream, AVPacket *pkt, int advance, int64_t delta)
    197 {
    198     static int64_t audpts, vidpts;
    199     if (stream == audioout_st)
     137    // Open output file
     138    ret = url_fopen(&outputFC->pb, outfile.ascii(), URL_WRONLY);
     139    if (ret < 0)
    200140    {
    201         FrameBuffer *buffer = audioPool.pop();
    202         buffer->setPkt(pkt, delta);
    203         audioQueue.enqueue(buffer);
    204         if (audioPool.isEmpty() )
    205         {
    206             VERBOSE(VB_IMPORTANT, "Ran out of audio buffers!");
    207             exit(TRANSCODE_BUGGY_EXIT_NO_AUDIO_BUFFERS);
    208         }
    209         DPRINTF(1, "AUDMUX : %d buffers left\n", audioPool.count());
     141        cerr << "Couldn't open output file, error #" << ret << endl;
     142        av_close_input_file(inputFC);
     143        inputFC = NULL;
     144        return false;
    210145    }
    211     else
    212     {
    213         FrameBuffer *buffer = videoPool.pop();
    214         buffer->setPkt(pkt, delta);
    215         if (advance)
    216             videoQueue.enqueue(buffer);
    217         else
    218         {
    219             videoPool.push(buffer);
    220             if (videoQueue.isEmpty())
    221                 return;
    222         }
    223         if (videoPool.isEmpty())
    224         {
    225             VERBOSE(VB_IMPORTANT, "Ran out of video buffers!");
    226             exit(TRANSCODE_BUGGY_EXIT_NO_VIDEO_BUFFERS);
    227         }
    228         DPRINTF(1, "VIDMUX : %d buffers left\n", videoPool.count());
    229     }
    230     if (! got_audio || ! got_video)
    231         return;
    232     if (! init_av)
    233     {
    234         if (videoQueue.isEmpty())
    235             return;
    236         while (! audioQueue.isEmpty() &&
    237             audioQueue.head()->getPTS() < videoQueue.head()->getPTS())
    238         {
    239             FrameBuffer *buffer = audioQueue.dequeue();
    240             buffer->Reset();
    241             audioPool.push(buffer);
    242         }
    243         if (audioQueue.isEmpty())
    244             return;
    245         init_av = 1;
    246         init_videoout_stream();
    247         init_audioout_stream();
    248         /* set the output parameters (must be done even if no
    249            parameters). */
    250         if (av_set_parameters(outputContext, NULL) < 0)
    251         {
    252             VERBOSE(VB_IMPORTANT, "Invalid output format parameters");
    253             exit(TRANSCODE_BUGGY_EXIT_INVALID_OUT_PARAMS);
    254         }
    255         /* write the stream header, if any */
    256         av_write_header(outputContext);
    257         dump_format(outputContext, 0, outputContext->filename, 1);
    258         videoout_st->pts.val = INT2PTS(videoQueue.head()->getPTS(), videoout_st);
    259     }
    260146
    261     while ((vidpts < audpts && ! videoQueue.isEmpty()) ||
    262           (audpts <= vidpts && ! audioQueue.isEmpty()))
     147    // Set output parameters
     148    ret = av_set_parameters(outputFC, NULL);
     149    if (ret < 0)
    263150    {
    264         AVFrame avframe;
    265         memset(&avframe, 0, sizeof(AVFrame));
    266         DPRINTF(1, "audpts: %lld vidpts: %lld\n", audpts, vidpts);
    267         if (audpts <= vidpts) {
    268             FrameBuffer *buf = audioQueue.dequeue();
    269             int64_t bufpts = buf->getPTS() - buf->getDelta();
    270             audioout_st->pts.val = INT2PTS(bufpts, audioout_st);
    271 //            long long orig = PTS2INT(audioout_st->pts, audioout_st);
    272             audioout_st->codec->coded_frame= &avframe;
    273             if (av_write_frame(outputContext, buf->getPkt()) != 0)
    274             {
    275                 VERBOSE(VB_IMPORTANT, "Error while writing audio frame");
    276                 exit(TRANSCODE_BUGGY_EXIT_WRITE_FRAME_ERROR);
    277             }
    278             DPRINTF(1, "AUDIO PTS (initial): %lld (stored): %lld\n", buf->getPTS(), PTS2INT(audioout_st->pts, audioout_st));
    279             audpts += PTS2INT(audioout_st->pts, audioout_st) - bufpts;
    280             buf->Reset();
    281             audioPool.push(buf);
    282         }
    283         else
    284         {
    285             FrameBuffer *buf = videoQueue.dequeue();
    286             int64_t bufpts = buf->getPTS() - buf->getDelta();
    287             videoout_st->pts.val = INT2PTS(bufpts, videoout_st);
    288 //            int64_t oldpts = PTS2INT(videoout_st->pts, videoout_st);
    289             videoout_st->codec->coded_frame= &avframe;
    290             if (av_write_frame(outputContext, buf->getPkt()) != 0)
    291             {
    292                 VERBOSE(VB_IMPORTANT, "Error while writing video frame");
    293                 exit(TRANSCODE_BUGGY_EXIT_WRITE_FRAME_ERROR);
    294             }
    295             DPRINTF(1, "VIDEO PTS (initial): %lld (stored): %lld\n", buf->getPTS(), PTS2INT(videoout_st->pts, videoout_st));
    296             vidpts += PTS2INT(videoout_st->pts, videoout_st) - bufpts;
    297             buf->Reset();
    298             videoPool.push(buf);
    299         }
     151        cerr << "Couldn't set parameters, error #" << ret << endl;
     152        av_close_input_file(inputFC);
     153        inputFC = NULL;
     154        return false;
    300155    }
    301     // empty audio buffer after writing the last video/audio frames
    302     if (chopping && videoQueue.isEmpty())
    303     {
    304         while(! audioQueue.isEmpty())
    305         {
    306             FrameBuffer *buf = audioQueue.dequeue();
    307             buf->Reset();
    308             audioPool.push(buf);
    309         }
    310     }
    311 }
    312156
    313 bool MPEG2trans::check_ac3_header(uint8_t *buf)
    314 {
    315     if ((buf[0] == 0x0b) && (buf[1] == 0x77))
    316         return true;
    317     return false;
    318 }
    319  
    320 bool MPEG2trans::check_mp2_header(uint8_t *buf)
    321 {
    322     if (buf[0] == 0xff && (buf[1] & 0xf0) == 0xf0)
    323         return true;
    324     return false;
    325 }
     157    outputFC->max_delay = 700000;
    326158
    327 bool MPEG2trans::check_video_header(uint8_t *buf)
    328 {
    329     if (buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01)
    330         return true;
    331     return false;
     159    return true;
    332160}
    333161
    334 uint32_t MPEG2trans::process_ac3_audio(AVPacket *pkt)
     162AVPacketList *MPEG2trans::ReadGop(int index, int64_t *min_dts, int64_t *min_pts, int64_t *end_pts)
    335163{
    336     uint32_t frame_len;
    337     uint8_t *buf = pkt->data;
    338     if (check_ac3_header(buf)) {
    339         uint32_t freq, bitrate, pad, bsid, flags;
    340         GetBits *gb = new GetBits(pkt->data + 4);
    341         freq = gb->GetNext(2);
    342         bitrate = gb->GetNext(5);
    343         pad = gb->GetNext(1);
    344         bsid = gb->GetNext(5);
    345         gb->SkipNext(3); //???
    346         flags = gb->GetNext(4);
    347         delete gb;
    348         if (bitrate < 19 && bsid < 12 && freq != 3) {
    349             int half;
    350             half = ac3_halfrate[bsid];
    351             audio_bitrate = (1000 * ac3_ratetable[bitrate]) >> half;
    352             switch(freq) {
    353                 case 0 : audio_freq = 48000 >> half; break;
    354                 case 1 : audio_freq = 44100 >> half; break;
    355                 case 2 : audio_freq = 32000 >> half; break;
    356             }
    357             frame_len = 192 * audio_bitrate / audio_freq + pad;
    358             return frame_len;
    359         }
    360     }
    361     return 0;
    362 }
     164    AVPacketList *pkts = NULL;
     165    AVPacket pkt;
     166    int iframe_count = 0;
    363167
    364 uint32_t MPEG2trans::process_mp2_audio(AVPacket *pkt)
    365 {
    366     uint32_t frame_len = 0;
    367     uint8_t *buf = pkt->data;
     168    av_init_packet(&pkt);
    368169
    369     if (check_mp2_header(buf))
     170    do
    370171    {
    371         uint8_t pad;
    372         if (buf[1] != 0xfd)
    373         {
    374             VERBOSE(VB_IMPORTANT, "Only MP2 audio is currently supported");
    375             exit(TRANSCODE_BUGGY_EXIT_INVALID_AUDIO);
    376         }
    377         GetBits *gb = new GetBits(buf + 2);
    378         audio_bitrate = gb->GetNext(4);
    379         audio_freq = gb->GetNext(2);
    380         pad = gb->GetNext(1);
    381         delete gb;
    382         switch (audio_bitrate) {
    383             case 1:  audio_bitrate = 32000; break;
    384             case 2:  audio_bitrate = 48000; break;
    385             case 3:  audio_bitrate = 56000; break;
    386             case 4:  audio_bitrate = 64000; break;
    387             case 5:  audio_bitrate = 80000; break;
    388             case 6:  audio_bitrate = 96000; break;
    389             case 7:  audio_bitrate = 112000; break;
    390             case 8:  audio_bitrate = 128000; break;
    391             case 9:  audio_bitrate = 160000; break;
    392             case 10: audio_bitrate = 192000; break;
    393             case 11: audio_bitrate = 224000; break;
    394             case 12: audio_bitrate = 256000; break;
    395             case 13: audio_bitrate = 320000; break;
    396             case 14: audio_bitrate = 384000; break;
    397             default:
    398                fprintf(stderr, "Unsupported bitrate 0x%02x\n", audio_bitrate);
    399                exit(TRANSCODE_BUGGY_EXIT_INVALID_AUDIO);
    400         }
    401         switch (audio_freq) {
    402             case 0: audio_freq = 44100; break;
    403             case 1: audio_freq = 48000; break;
    404             case 2: audio_freq = 32000; break;
    405             default:
    406                 fprintf(stderr, "Unsupported frequency 0x%02x\n", audio_freq);
    407                 exit(TRANSCODE_BUGGY_EXIT_INVALID_AUDIO);
    408         }
    409         frame_len = (144 * audio_bitrate / audio_freq) + pad;
    410         return frame_len;
    411     }
    412     return 0;
    413 }
     172        int ret = av_read_frame(inputFC, &pkt);
     173        if (ret < 0) break;
    414174
    415 void MPEG2trans::process_audio(AVPacket *pkt)
    416 {
    417     int32_t frame_len;
    418     audioFrame.setPkt(pkt);
    419     AVPacket *thispkt = audioFrame.getPkt();
    420     if (use_ac3)
    421         frame_len = process_ac3_audio(thispkt);
    422     else
    423         frame_len = process_mp2_audio(thispkt);
    424     if (frame_len == pkt->size) {
    425         got_audio = 1;
    426         bool processed_frame = false;
     175        if ((inputFC->streams[pkt.stream_index]->codec->codec_type == CODEC_TYPE_VIDEO) && (pkt.flags & PKT_FLAG_KEY))
     176            iframe_count++;
    427177
    428         if (chopping && thispkt->pts >= chop_start_pts || chop_end)
     178        if (iframe_count < 2)
    429179        {
    430             while(! audskipQueue.isEmpty() &&
    431                   audskipQueue.head()->pts < last_gop_pts)
     180            if (!pkts)
    432181            {
    433                 AVPacket *store = audskipQueue.dequeue();
    434                 delete store;
     182                pkts = new AVPacketList;
     183                pkts->next = NULL;
     184                pkts->pkt = pkt;
    435185            }
    436             if (chopping && thispkt->pts >= last_gop_pts ||
    437                 chop_end && pic_frame_count < 2)
     186            else
    438187            {
    439                 // store audio frame
    440                 AVPacket *store = new AVPacket;
    441                 store->data = new uint8_t[frame_len];
    442                 memcpy(store->data, thispkt->data, frame_len);
    443                 store->size = frame_len;
    444                 store->pts = thispkt->pts;
    445                 audskipQueue.enqueue(store);
     188                AVPacketList *t = pkts;
     189
     190                while (t->next != NULL) t = t->next;
     191
     192                t->next = new AVPacketList;
     193                t->next->next = NULL;
     194                t->next->pkt = pkt;
    446195            }
    447             if (! chop_end || pic_frame_count < 2)
    448                 processed_frame = true;
    449         }
    450         if (! processed_frame)
    451         {
    452             while(! audskipQueue.isEmpty())
     196
     197            if (pkt.stream_index == index)
    453198            {
    454                 AVPacket *store = audskipQueue.dequeue();
    455                 write_muxed_frame(audioout_st, store, true, goppts_delta);
    456                 delete [] store->data;
    457                 delete store;
     199                if (pkt.dts != (int64_t)AV_NOPTS_VALUE && min_dts)
     200                    if ((pkt.dts < *min_dts) || (*min_dts == (int64_t)AV_NOPTS_VALUE)) *min_dts = pkt.dts;
     201                if (pkt.pts != (int64_t)AV_NOPTS_VALUE && min_pts)
     202                    if ((pkt.pts < *min_pts) || (*min_pts == (int64_t)AV_NOPTS_VALUE))  *min_pts = pkt.pts;
     203                if (pkt.pts != (int64_t)AV_NOPTS_VALUE && end_pts)
     204                    if ((pkt.pts + pkt.duration > *end_pts) || (*end_pts == (int64_t)AV_NOPTS_VALUE)) *end_pts = pkt.pts + pkt.duration;
    458205            }
    459             write_muxed_frame(audioout_st, thispkt, true, goppts_delta);
    460206        }
    461         DPRINTF(1, "Audio frame: %d bytes freq: %d bitrate: %d ptsdel: %lld\n",
    462                    frame_len, audio_freq, audio_bitrate, goppts_delta);
    463         if (thispkt->size != frame_len)
    464         {
    465             memmove(thispkt->data, thispkt->data + frame_len, (thispkt->size - frame_len));
    466             thispkt->size -= frame_len;
    467         }
    468         else
    469         {
    470             thispkt->size = 0;
    471             return;
    472         }
    473     }
    474     uint8_t *frame_start = thispkt->data;
    475     while (thispkt->size &&
    476          (use_ac3 && !check_ac3_header(frame_start) ||
    477           ! use_ac3 && check_mp2_header(frame_start)))
     207        else if (pkt.destruct) pkt.destruct(&pkt);
     208
     209    } while (iframe_count < 2);
     210   
     211    return pkts;
     212}
     213
     214void MPEG2trans::FreeGop(AVPacketList *pkts)
     215{
     216    AVPacketList *t = pkts;
     217    AVPacketList *u;
     218
     219    while (t)
    478220    {
    479         frame_start++;
    480         thispkt->size--;
     221        if (t->pkt.destruct) t->pkt.destruct(&t->pkt);
     222
     223        u = t;
     224        t = t->next;
     225        delete u;
    481226    }
    482     if (thispkt->size) {
    483         memmove(thispkt->data, frame_start, thispkt->size);
    484         DPRINTF(1, "Found %d bytes of garbage data before header\n",
    485                    frame_start - thispkt->data);
    486     } else {
    487         DPRINTF(1, "Found %d bytes of garbage data, but no header\n",
    488                    frame_start - thispkt->data);
    489     }
    490227}
    491228
    492 bool MPEG2trans::process_video(AVPacket *pkt, bool gopsearch)
     229int MPEG2trans::DoTranscode(QString &infile, QString &tmpfile, bool useCutlist)
    493230{
    494     bool skip_pic_frame = false;
    495     bool found_gop = false;
    496     uint8_t *vidblkptr, *frame_start = NULL;
    497     uint32_t len;
     231    QMap<long long, int> deleteMap;
     232    QMap<long long, long long> posMap;
    498233
    499     videoFrame.Reset();
    500     videoFrame.setPkt(pkt);
    501     AVPacket *thispkt = videoFrame.getPkt();
     234    AVPacketList *pkts;
     235    AVPacket pkt;
    502236
    503     vidblkptr = thispkt->data;
    504     len = thispkt->size;
     237    int video_index = -1;
    505238
    506     // a pkt can contain SEQ and GOP frames, but it is gauranteed that
    507     // there is one and only one PIC frame in the pkt, and that will be
    508     // the last frame in the pkt.
    509     while (len >= 4 && check_video_header(vidblkptr))
     239    int64_t start_pts;
     240    int64_t end_pts;
     241    int64_t min_dts = AV_NOPTS_VALUE;
     242    int64_t delta_pts;
     243    int64_t next_start_pts = AV_NOPTS_VALUE;
     244
     245    int total_frames = 0;
     246    int completed_frames = 0;
     247
     248    int ret;
     249
     250    int jobID = -1;
     251    if (chkTranscodeDB)
    510252    {
    511         uint32_t frame_len = 0;
    512         uint32_t pos = vidblkptr - thispkt->data;
    513         if (vidblkptr[3] == 0xb3) // sequence header
     253        jobID = JobQueue::GetJobID(JOB_TRANSCODE, m_pginfo->chanid, m_pginfo->startts);
     254
     255        if (jobID < 0)
    514256        {
    515             if (gopsearch)
    516                 return true;
    517             if (video_width == 0)
    518             {
    519                 frame_len = 4 + parse_seq_header(vidblkptr + 4, true);
    520                 video_frame_state |= 0x01;
    521                 got_video = 1;
    522             }
    523             else
    524                 frame_len = 4 + parse_seq_header(vidblkptr + 4, false);
    525             if (! chopping && cutlistIter != cutlistMap.end() &&
    526                last_frame_number >= cutlistIter.key())
    527             {
    528                 chopping = 1;
    529                 chop_start_pts = thispkt->pts;
    530             }
    531             seqFrame.Reset();
    532             seqFrame.setPkt(vidblkptr, frame_len, thispkt->pts);
    533             DPRINTF(1, "pos: 0x%lx SEQ-HEADER: w: %d "
    534                        "h: %d a/r: %d rate: %d br: %d\n",
    535                        pos, video_width, video_height, video_aspect,
    536                        video_frame_rate, video_bitrate);
     257            VERBOSE(VB_IMPORTANT, "ERROR, Transcode called from JobQueue but "
     258                                  "no jobID found!");
     259            return TRANSCODE_EXIT_INVALID_CMDLINE;
    537260        }
    538         else if (vidblkptr[3] == 0xb8)
    539         {
    540             if (gopsearch)
    541                 return true;
    542             gop_timestamp_t gop_ts;
    543             bool closed, broken;
    544             if (! (video_frame_state | 0x01))
    545                 continue;
    546             video_frame_state |= 0x02;
    547             found_gop = true;
    548             last_gop_pts = thispkt->pts;
    549             frame_start = vidblkptr;
    550             if (chop_end)
    551                 chop_end = false;
    552             last_frame_number += pic_frame_count;
    553             if (! chopping && cutlistIter != cutlistMap.end() &&
    554                last_frame_number >= cutlistIter.key() &&
    555                last_frame_number < cutlistIter.data())
    556             {
    557                 chopping = 1;
    558                 chop_start_pts = thispkt->pts;
    559             }
    560             else if (chopping && cutlistIter != cutlistMap.end() &&
    561                last_frame_number >= cutlistIter.data())
    562             {
    563                 chopping = 0;
    564                 chop_end = true;
    565                 cutlistIter++;
    566             }
    567             frame_len = 4+ get_gop_data(vidblkptr + 4, &gop_ts,
    568                                         &closed, &broken);
    569             if (fixed_gop_ts)
    570             {
    571                 if (! chopping)
    572                     update_timestamp(fixed_gop_ts, &gop_ts);
    573             }
    574             else
    575             {
    576                 fixed_gop_ts = new gop_timestamp_t;
    577                 *fixed_gop_ts = gop_ts;
    578             }
    579             int64_t goppts;
    580             goppts = (fixed_gop_ts->hour * 3600 +
    581                       fixed_gop_ts->min * 60 +
    582                       fixed_gop_ts->sec) * AV_TIME_BASE +
    583                      fixed_gop_ts->pic * AV_TIME_BASE / video_frame_rate;
    584             goppts_delta = (gop_ts.hour * 3600 + gop_ts.min * 60 +
    585                             gop_ts.sec ) * AV_TIME_BASE +
    586                            gop_ts.pic * AV_TIME_BASE / video_frame_rate -
    587                            goppts;
    588             if (chop_end)
    589             {
    590                 closed = 0;
    591                 broken = 1;
    592             }
    593             set_gop_data(vidblkptr + 4, fixed_gop_ts, closed, broken);
    594             pic_frame_count = 0;
    595             skipped_frames = 0;
    596             DPRINTF(1, "pos: 0x%lx GOP-HEADER %02d:%02d:%02d "
    597                        "#: %d c: %d b: %d\n", pos, fixed_gop_ts->hour,
    598                        fixed_gop_ts->min, fixed_gop_ts->sec,
    599                        fixed_gop_ts->pic, closed, broken);
    600         }
    601         else if (vidblkptr[3] == 0x00)
    602         {
    603             if (gopsearch)
    604                 return false;
    605             int num, type;
    606             char pictype;
    607             frame_len = len;
    608             if (video_frame_state != 0x03)
    609                 continue;
    610             if (! frame_start)
    611                 frame_start = vidblkptr;
    612             GetBits *gb = new GetBits(vidblkptr + 4);
    613             num = gb->GetNext(10);
    614             type = gb->GetNext(3);
    615             delete gb;
    616             switch (type) {
    617                 case 1: pictype = 'I'; break;
    618                 case 2: pictype = 'P'; break;
    619                 case 3: pictype = 'B'; break;
    620                 case 4: pictype = 'D'; break;
    621                 default: pictype = 'X'; break;
    622             }
    623             if (0 && chop_end) {
    624                 if (pic_frame_count < 2 && pictype == 'B') {
    625                     skip_pic_frame = 1;
    626                     skipped_frames++;
    627                 } else {
    628                     if (pic_frame_count == 0)
    629                         num = 0;
    630                     else
    631                         num -= skipped_frames;
    632                     update_pic_frame_num(vidblkptr + 4,num);
    633                     pic_frame_count++;
    634                 }
    635             }
    636             else
    637                 pic_frame_count++;
    638             DPRINTF(1, "pos: 0x%lx PIC-HEADER %c-Frame %s#: %d\n", pos,
    639                        pictype, ((skip_pic_frame)? "SKIPPED ":""), num);
    640             if (pictype == 'X') DPRINTF(1, "Frame type: %d\n", type);
    641         }
    642         else if (vidblkptr[3] == 0xbe)
    643         {
    644             uint32_t skip = (vidblkptr[4] << 8) + vidblkptr[5];
    645             frame_len = skip + 6;
    646             DPRINTF(1, "pos: 0x%lx PAD-HEADER (%d bytes)\n", pos, skip);
    647         }
    648         vidblkptr += frame_len;
    649         len -= frame_len;
     261
     262        JobQueue::ChangeJobComment(jobID, "0% " + QObject::tr("Completed"));
    650263    }
    651     if (gopsearch)
    652         return false;
    653     if ((uint32_t)(vidblkptr - thispkt->data) == videoFrame.getSize())
     264
     265    cerr << "Title: " << m_pginfo->title.ascii() << "     Subtitle: "
     266                      << m_pginfo->subtitle.ascii() << endl << flush;
     267
     268    m_pginfo->GetPositionMap(posMap, MARK_GOP_BYFRAME);
     269    if (posMap.empty())
    654270    {
    655         if (! chopping && !skip_pic_frame && frame_start)
     271#ifdef MPEG2trans_DEBUG
     272        cerr << "no MARK_GOP_BYFRAME entries" << endl;
     273#endif
     274
     275        // no MARK_GOP_BYFRAME entries in the recordedmarkup table
     276        // trying MARK_GOP_START
     277        m_pginfo->GetPositionMap(posMap, MARK_GOP_START);
     278        if (posMap.empty())
    656279        {
    657             if (found_gop)
    658                 write_muxed_frame(videoout_st, seqFrame.getPkt(), false);
    659             AVPacket picpkt;
    660             picpkt.pts = thispkt->pts;
    661             picpkt.data = frame_start;
    662             picpkt.size = thispkt->size - (frame_start - thispkt->data);
    663             write_muxed_frame(videoout_st, &picpkt, true, goppts_delta);
     280#ifdef MPEG2trans_DEBUG
     281            cerr << "no MARK_GOP_START entries" << endl;
     282#endif
    664283        }
    665284    }
    666     else // something went terribly wrong!
    667     {
    668         exit(TRANSCODE_BUGGY_EXIT_INVALID_VIDEO);
    669     }
    670     return(false);
    671 }
    672285
    673 int MPEG2trans::DoTranscode(QString inputFilename, QString outputFilename)
    674 {
    675     AVPacket pkt1, *pkt = &pkt1;
    676     AVFormatParameters params, *ap = &params;
    677     int i, err;
     286    if (useCutlist)
     287        m_pginfo->GetCutList(deleteMap);
    678288
    679     inputContext = NULL; //av_mallocz(sizeof(AVFormatContext));
    680     ap = (AVFormatParameters*) calloc(sizeof(AVFormatParameters), 1);
    681     err = av_open_input_file(&inputContext, inputFilename, NULL, 0, ap);
    682     if (err < 0)
     289    // keep frames from beginning of file if
     290    // delete map hasn't marked them for removal
     291    QMap<long long, long long>::Iterator pMapIter;
     292
     293    pMapIter = posMap.begin();
     294    deleteMap.insert(pMapIter.key(), 0, false);
     295
     296    pMapIter = posMap.end();
     297    pMapIter--;
     298    deleteMap.insert(pMapIter.key(), 1, false);
     299
     300#ifdef MPEG2trans_DEBUG
     301    // Iterate over the position map
     302    for (pMapIter = posMap.begin(); pMapIter != posMap.end(); pMapIter++)
    683303    {
    684         VERBOSE(VB_IMPORTANT, QString("Failed to open file '%1', error code %2")
    685                 .arg(inputFilename).arg(err));
    686         return 1;
     304        cerr << "posMap[" << pMapIter.key() << "] = " << pMapIter.data() << endl;
    687305    }
    688     err = av_find_stream_info(inputContext);
    689     if (err < 0)
     306#endif
     307
     308    // Iterate over the cut list
     309    QMap<long long, int>::Iterator dMapIter;
     310    for (dMapIter = deleteMap.begin(); dMapIter != deleteMap.end(); )
    690311    {
    691         VERBOSE(VB_IMPORTANT,
    692                 QString("Could not find stream parameters for '%1',"
    693                         " error code %2").arg(inputFilename).arg(err));
    694         return 2;
    695     }
    696     for (i = 0; i < inputContext->nb_streams; i++)
    697     {
    698         AVCodecContext *enc = inputContext->streams[i]->codec;
    699         VERBOSE(VB_GENERAL, QString("Stream: %1 Type: %2")
    700                 .arg(i).arg(enc->codec_type));
    701         switch(enc->codec_type) {
    702             case CODEC_TYPE_AUDIO:
    703                 if (!use_ac3 &&
    704                     (inputContext->streams[i])->codec->codec_id == CODEC_ID_AC3) {
    705                     use_ac3 = 1;
    706                     audioin_index = i;
    707                 } else if (audioin_index == -1)
    708                     audioin_index = i;
    709                 break;
    710             case CODEC_TYPE_VIDEO:
    711                 videoin_index = i;
    712                 break;
    713             default:
    714                 break;
     312#ifdef MPEG2trans_DEBUG
     313        cerr << "delMap[" << dMapIter.key() << "] = " << dMapIter.data() << endl;
     314#endif
     315        if (dMapIter.data() == 0)
     316        {
     317            int start_frame = dMapIter.key();
     318            dMapIter++;
     319
     320#ifdef MPEG2trans_DEBUG
     321            cerr << "delMap[" << dMapIter.key() << "] = " << dMapIter.data() << endl;
     322#endif
     323            if (dMapIter != deleteMap.end())
     324            {
     325                int end_frame = dMapIter.key();
     326                total_frames += end_frame - start_frame;
     327            }
     328            else break;
    715329        }
     330
     331        dMapIter++;
    716332    }
    717     dump_format(inputContext, 0, inputFilename, 0);
    718333
    719         AVOutputFormat *fmt;
    720         /* initialize libavcodec, and register all codecs and formats */
    721    
    722         /* auto detect the output format from the name. default is
    723            mpeg. */
    724         fmt = guess_format("vob", NULL, NULL);
    725         if (!fmt)
    726         {
    727             VERBOSE(VB_IMPORTANT, "Could not find suitable output format.");
    728             return 3;
    729         }
    730    
    731         /* allocate the output media context */
    732         outputContext = (AVFormatContext *)av_mallocz(sizeof(AVFormatContext));
    733         if (!outputContext)
    734         {
    735             VERBOSE(VB_IMPORTANT, "Could not allocate memory.");
    736             return 4;
    737         }
    738         outputContext->oformat = fmt;
    739         snprintf(outputContext->filename,
    740                  sizeof(outputContext->filename), "%s", outputFilename.ascii());
     334    /*============ initialise AV ===============*/
     335    if (!InitAV(infile))
     336        return TRANSCODE_EXIT_UNKNOWN_ERROR;
    741337
    742         /* add the audio and video streams using the default format codecs
    743            and initialize the codecs */
    744         videoout_st = av_new_stream(outputContext, 0);
    745         audioout_st = av_new_stream(outputContext, 1);
     338    if (!InitOutput(tmpfile))
     339        return TRANSCODE_EXIT_UNKNOWN_ERROR;
    746340
    747         if (!videoout_st || !audioout_st)
    748         {
    749             VERBOSE(VB_IMPORTANT,
    750                     "Could not find relevant audio and video streams!");
    751             return 5;
    752         }
     341    /*=========== transcoding/cuting ===========*/
     342    av_init_packet(&pkt);
    753343
    754         /* open the output file, if needed */
    755         if (url_fopen(&outputContext->pb, outputFilename, URL_WRONLY) < 0)
    756         {
    757             VERBOSE(VB_IMPORTANT, QString("Could not open output file '%1'")
    758                     .arg(outputFilename));
    759             return 6;
    760         }
     344    // initialise cut list iterator
     345    dMapIter = deleteMap.begin();
    761346
    762     while (av_read_frame(inputContext, pkt) >= 0)
     347    // seek to first entry in deleteMap
     348#ifdef MPEG2trans_DEBUG
     349    cerr << "first entry in dMap: " << dMapIter.key() << endl;
     350    cerr << "infile offset: " << posMap[dMapIter.key()] << endl;
     351#endif
     352
     353    // write stream header
     354    ret = av_write_header(outputFC);
     355    if (ret < 0)
    763356    {
    764         DPRINTF(1, "PKT (%s): %lu %llu\n",((pkt->stream_index == audioin_index) ? "AUDIO" : "VIDEO"),
    765                 pkt->size, pkt->pts);
    766         if (pkt->stream_index == audioin_index)
    767             process_audio(pkt);
    768         else if (pkt->stream_index == videoin_index)
    769             process_video(pkt);
     357        cerr << "Error writing output header\n";
     358        return TRANSCODE_EXIT_UNKNOWN_ERROR;
    770359    }
    771     av_close_input_file(inputContext);
    772 
    773     /* write the trailer, if any */
    774     av_write_trailer(outputContext);
    775360   
    776     /* free the streams */
    777     while (outputContext->nb_streams)
     361    /* find the index of the first video stream */
     362    for (int i = 0; i < inputFC->nb_streams; i++)
    778363    {
    779         av_freep(&outputContext->streams[--outputContext->nb_streams]);
    780     }
    781 
    782     /* close the output file */
    783     url_fclose(&outputContext->pb);
    784 
    785     /* free the stream */
    786     av_free(outputContext);
    787     return 0;
    788 }
    789 
    790 uint32_t MPEG2trans::parse_seq_header(uint8_t *PES_data, bool store)
    791 {
    792     uint32_t len = 0;
    793     GetBits *gb = new GetBits(PES_data);
    794     if (store)
    795     {
    796         int rate;
    797         video_width  = gb->GetNext(12);
    798         video_height = gb->GetNext(12);
    799         video_aspect = gb->GetNext(4);
    800         rate = gb->GetNext(4);
    801         video_bitrate = gb->GetNext(18) * 400;
    802         switch (rate) {
    803             case 1: video_frame_rate = 23976; break;
    804             case 2: video_frame_rate = 24000; break;
    805             case 3: video_frame_rate = 25000; break;
    806             case 4: video_frame_rate = 29970; break;
    807             case 5: video_frame_rate = 30000; break;
    808             case 6: video_frame_rate = 50000; break;
    809             case 7: video_frame_rate = 59940; break;
    810             case 8: video_frame_rate = 60000; break;
     364        if (inputFC->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
     365        {
     366            video_index = i;
     367            break;
    811368        }
    812369    }
    813     else
    814     {
    815         gb->SkipNext(50);
    816     }
    817     gb->SkipNext(12);
    818     if (gb->GetNext(1))
    819     {
    820         gb->SkipNext(8*64);
    821         len += 64;
    822     }
    823     if (gb->GetNext(1))
    824     {
    825         gb->SkipNext(8*64);
    826         len += 64;
    827     }
    828     delete gb;
    829     len += 8;
    830     len += get_ext_frame(PES_data + len);
    831     return len;
    832 }
    833370
    834 // Must be careful using get_ext_frame. There is no bounds checking
    835 // This is ok, because a SEQ or GOP frame must contain a pic frame
    836 // in the same pkt.  get_ext_frame should only be used when it is
    837 // gauranteed that there is another video frame in the packet!
    838 uint32_t MPEG2trans::get_ext_frame(uint8_t *PES_data)
    839 {
    840     uint32_t len = 0;
    841     bool done = false;
    842     while (! done)
     371    /* check if no video stream */
     372    if (video_index == -1)
     373        return TRANSCODE_BUGGY_EXIT_INVALID_VIDEO;
     374   
     375    /* iterate over the entries in the delete map */
     376    while (dMapIter != deleteMap.end())
    843377    {
    844         if (! check_video_header(PES_data))
     378        if (dMapIter.data() == 0)   // keep frames
    845379        {
    846             len++;
    847             PES_data++;
    848         }
    849         else if (PES_data[3] == 0xb2 || PES_data[3] == 0xb5)
    850         {
    851             len += 4;
    852             PES_data +=4;
    853         }
    854         else
    855             done = true;
    856     }
    857     return len;
    858 }
     380            long long start_key = dMapIter.key();
     381            while ((posMap.find(start_key) == posMap.end()) && (start_key >= 0)) start_key--;
     382            if (posMap.find(start_key) == posMap.end())
     383            {
     384#ifdef MPEG2trans_DEBUG
     385                cerr << "Couldn't find keyframe to start from\n";
     386#endif
     387                continue;
     388            }
     389#ifdef MPEG2trans_DEBUG
     390            cerr << "Saving from frame #" << start_key;
     391#endif
    859392
    860 uint32_t MPEG2trans::get_gop_data(uint8_t *PES_data, gop_timestamp_t *ts,
    861                                   bool *closed, bool *broken)
    862 {
    863     uint32_t len = 4;
    864     GetBits *gb = new GetBits(PES_data);
    865     ts->drop = gb->GetNext(1);
    866     ts->hour = gb->GetNext(5);
    867     ts->min = gb->GetNext(6);
    868     gb->MarkerBit();
    869     ts->sec = gb->GetNext(6);
    870     ts->pic = gb->GetNext(6);
    871     *closed = gb->GetNext(1);
    872     *broken = gb->GetNext(1);
    873     gb->SkipNext(5);
    874     delete gb;
    875     len += get_ext_frame(PES_data + len);
    876     return len;
    877 }
     393            dMapIter++;
     394            if (dMapIter == deleteMap.end())
     395            {
     396#ifdef MPEG2trans_DEBUG
     397                cerr << "Reached end of delete map\n";
     398#endif
     399                continue;
     400            }
     401            long long end_key = dMapIter.key();
     402            while ((posMap.find(end_key) == posMap.end()) && (end_key >= 0)) end_key--;
     403            if (posMap.find(end_key) == posMap.end())
     404            {
     405#ifdef MPEG2trang_DEBUG
     406                cerr << "Couldn't find keyframe to start from\n";
     407#endif
     408                continue;
     409            }
     410#ifdef MPEG2trans_DEBUG
     411            cerr << " to end of GOP starting at frame #" << end_key << endl;
     412#endif
    878413
    879 void MPEG2trans::set_gop_data(uint8_t *PES_data, gop_timestamp_t *ts,
    880                               bool closed, bool broken)
    881 {
    882     SetBits *sb = new SetBits(PES_data);
    883     sb->SetNext(ts->drop, 1);
    884     sb->SetNext(ts->hour, 5);
    885     sb->SetNext(ts->min, 6);
    886     sb->SetNext(1, 1);
    887     sb->SetNext(ts->sec, 6);
    888     sb->SetNext(ts->pic, 6);
    889     sb->SetNext(closed, 1);
    890     sb->SetNext(broken, 1);
    891     delete sb;
    892 }
     414            /* seek to end gop */
     415            av_read_frame_flush(inputFC);
     416            url_fseek(&inputFC->pb, posMap[end_key], SEEK_SET);
    893417
    894 void MPEG2trans::update_timestamp(gop_timestamp_t *last_ts, gop_timestamp_t *ts)
    895 {
    896     uint32_t framerate;
     418            /* read in last gop of the save section */
     419            end_pts = AV_NOPTS_VALUE;
     420            pkts = ReadGop(video_index, NULL, NULL, &end_pts);
     421            FreeGop(pkts);
    897422
    898     framerate = (video_frame_rate + 500) / 1000;
    899     last_ts->pic += pic_frame_count;
    900     while (last_ts->pic >= framerate) {
    901       last_ts->pic -= framerate;
    902       last_ts->sec += 1;
    903     }
    904     while (last_ts->sec >= 60) {
    905       last_ts->sec -=60;
    906       last_ts->min +=1;
    907     }
    908     while (last_ts->min >= 60) {
    909       last_ts->min -=60;
    910       last_ts->hour +=1;
    911     }
    912     DPRINTF(1, "Computed time: %02d:%02d:%02d.%02d "
    913                "Actual time: %02d:%02d:%02d.%02d %d\n",
    914                last_ts->hour, last_ts->min, last_ts->sec, last_ts->pic,   
    915                ts->hour, ts->min, ts->sec, ts->pic, pic_frame_count);
    916 }
     423            /* seek to start gop */
     424            av_read_frame_flush(inputFC);
     425            url_fseek(&inputFC->pb, posMap[start_key], SEEK_SET);
    917426
    918 void MPEG2trans::update_pic_frame_num(uint8_t *PES_data, int num)
    919 {
    920       uint8_t val[2];
    921       val[0] = (num >> 2) && 0xff;
    922       val[1] = (num & 0x03) << 6;
    923       val[1] |= (PES_data[1] & 0x3f);
    924       PES_data[0] = val[0];
    925       PES_data[1] = val[1];
    926 }
     427            /* read in the first gop of the save section */
     428            delta_pts = AV_NOPTS_VALUE;
     429            start_pts = AV_NOPTS_VALUE;
     430            pkts = ReadGop(video_index, &min_dts, &start_pts, NULL);
     431            FreeGop(pkts);
    927432
    928 /**************************************************************/
    929 /* audio output */
    930 void MPEG2trans::init_audioout_stream(void)
    931 {
    932     AVCodecContext *c;
     433            /* calcuate actual delta used in pts/dts fixup */
     434            if (next_start_pts == (int64_t)AV_NOPTS_VALUE)
     435            {
     436                /* first section */
     437                /* minimum dts in first gop should translate to a dts of 0 */
     438                delta_pts = min_dts;
     439            }
     440            else
     441            {
     442                /* not first section */
     443                /* minimum pts of video_index should translate to next_start_pts */
     444                delta_pts = start_pts - next_start_pts;
     445            }
     446            next_start_pts = end_pts;
     447           
     448#ifdef MPEG2trans_DEBUG
     449            cerr << "Minimum PTS = " << start_pts << endl;
     450            cerr << "Maximum PTS = " << end_pts << endl;
     451            cerr << "I/O Delta   = " << delta_pts << endl;
     452#endif
    933453
    934     c = audioout_st->codec;
    935     c->codec_id = (inputContext->streams[audioin_index])->codec->codec_id;
    936     c->codec_type = CODEC_TYPE_AUDIO;
     454            /* seek to start gop */
     455            av_read_frame_flush(inputFC);
     456            url_fseek(&inputFC->pb, posMap[start_key], SEEK_SET);
    937457
    938     /* put sample parameters */
    939     c->bit_rate = audio_bitrate;
    940     c->sample_rate = audio_freq;
    941     c->channels = 2;
    942     DPRINTF(1, "AUDIO: bitrate: %d freq: %d\n", c->bit_rate, c->sample_rate);
    943 }
     458            /* repeat until pts of all streams are past end_pts */
     459            bool done;
     460            bool *dones = new bool[inputFC->nb_streams];
     461            for (int i = 0; i < inputFC->nb_streams; i++) dones[i] = false;
    944462
    945 /**************************************************************/
    946 /* video output */
    947 void MPEG2trans::init_videoout_stream()
    948 {
    949     AVCodecContext *c;
     463            int percentage = -1;
     464            do
     465            {
     466                /* read packet */
     467                ret = av_read_frame(inputFC, &pkt);
     468                if (ret < 0) break;
    950469
    951     c = videoout_st->codec;
    952     c->codec_id = (inputContext->streams[videoin_index])->codec->codec_id;
    953     c->codec_type = CODEC_TYPE_VIDEO;
     470                /* end pts is the pts of the last frame in the last gop + the duration of the frame */
     471                /* ie it is the pts of the first frame in the next gop after the cut point */
     472                /* if the current frame is to be decoded after end_pts we take this as an indication */
     473                /* that we are finished with this stream for the currect section */
     474                if (pkt.dts >= end_pts) dones[pkt.stream_index] = true;
    954475
    955     /* put sample parameters */
    956     c->bit_rate = video_bitrate;
    957     /* resolution must be a multiple of two */
    958     c->width = video_width;
    959     c->height = video_height;
    960     /* frames per second */
    961     c->time_base.den = video_frame_rate;
    962     c->time_base.num = 1000;
    963     c->gop_size = 12; /* emit one intra frame every twelve frames at most */
    964     /* just for testing, we also add B frames */
    965     c->max_b_frames = 2;
    966     DPRINTF(1, "VIDEO: bitrate: %d size: %dx%d framerate: %f\n",
    967                c->bit_rate, c->width, c->height, video_frame_rate / 1000.0);
     476                /* if the presentation time is between start_pts and end_pts save it in the output */
     477                if ((pkt.pts != (int64_t)AV_NOPTS_VALUE && pkt.pts >= start_pts && pkt.pts < end_pts) ||
     478                    (pkt.pts == (int64_t)AV_NOPTS_VALUE && pkt.dts >= min_dts && pkt.dts < end_pts))
     479                {
     480                    if (pkt.stream_index == video_index) completed_frames++;
     481
     482                    if (pkt.pts != (int64_t)AV_NOPTS_VALUE) pkt.pts -= delta_pts;
     483                    if (pkt.dts != (int64_t)AV_NOPTS_VALUE) pkt.dts -= delta_pts;
     484
     485#ifdef MPEG2trans_DEBUG
     486                    cerr << "T: " << pkt.pts << ", " << pkt.dts << endl;
     487#endif
     488                    av_interleaved_write_frame(outputFC, &pkt);
     489                }
     490                else if (pkt.destruct) pkt.destruct(&pkt);
     491
     492                done = true;
     493                for (int i = 0; i < inputFC->nb_streams; i++) done &= dones[i];
     494
     495                int new_percentage = 100 * completed_frames/total_frames;
     496                if (new_percentage != percentage)
     497                {
     498                    percentage = new_percentage;
     499                    if (chkTranscodeDB)
     500                    {
     501                        if (JobQueue::GetJobCmd(jobID) == JOB_STOP)
     502                        {
     503                            unlink(tmpfile.ascii());
     504                            /* should probably clean up here */
     505                            VERBOSE(VB_IMPORTANT, "Transcoding STOPped by JobQueue");
     506                            return TRANSCODE_EXIT_STOPPED;
     507                        }
     508                        JobQueue::ChangeJobComment(jobID, QString("%1% ").arg(percentage) +
     509                                                          QObject::tr("Completed"));
     510                    }
     511                    else
     512                        cerr << "Percent complete: " <<  percentage << "%\r" << flush;
     513                }
     514
     515            } while (!done);
     516        }
     517        else    // delete frames
     518        {
     519            // skip frames
     520            dMapIter++;
     521            if (dMapIter == deleteMap.end()) continue;
     522        }
     523    }
     524
     525    /*============== Close AV ==============*/
     526    // Close output file
     527    url_fclose(&outputFC->pb);
     528    for (int i = 0; i < outputFC->nb_streams; i++) av_free(outputFC->streams[i]);
     529    av_free(outputFC);
     530    outputFC = NULL;
     531
     532    // Close input file
     533    av_close_input_file(inputFC);
     534    inputFC = NULL;
     535
     536    return TRANSCODE_EXIT_OK;
    968537}
    969538
    970 int MPEG2trans::BuildKeyframeIndex(QString filename, QMap <long long, long long> &posMap)
     539int MPEG2trans::BuildKeyframeIndex(QString &file, QMap <long long, long long> &posMap)
    971540{
    972     AVPacket pkt1, *pkt = &pkt1;
    973     AVFormatParameters params, *ap = &params;
    974     int i, err;
    975     uint32_t count = 0;
    976 
    977     inputContext = NULL; //av_mallocz(sizeof(AVFormatContext));
    978     ap = (AVFormatParameters *)calloc(sizeof(AVFormatParameters), 1);   
    979     err = av_open_input_file(&inputContext, filename, NULL, 0, ap);
    980     if (err < 0)
     541    int jobID = -1;
     542    if (chkTranscodeDB)
    981543    {
    982         VERBOSE(VB_IMPORTANT, QString("Failed to open file '%1', error code %2")
    983                 .arg(filename).arg(err));
    984         return 1;
     544        if (JobQueue::GetJobCmd(jobID) == JOB_STOP)
     545        {
     546            unlink(file.ascii());
     547            /* should probably clean up here */
     548            VERBOSE(VB_IMPORTANT, "Transcoding STOPped by JobQueue");
     549            return TRANSCODE_EXIT_STOPPED;
     550        }
     551        JobQueue::ChangeJobComment(jobID, QString(QObject::tr("Generating Keyframe Index")));
    985552    }
    986     err = av_find_stream_info(inputContext);
    987     if (err < 0)
    988     {
    989         VERBOSE(VB_IMPORTANT,
    990                 QString("Could not find stream parameters for '%1',"
    991                         " error code %2").arg(filename).arg(err));
    992         return 2;
    993     }
     553    else
     554        cerr << "Generating Keyframe Index" << endl << flush;
    994555
    995     for (i = 0; i < inputContext->nb_streams; i++)
     556    AVPacket pkt;
     557    int video_index = -1;
     558    int count = 0;
     559
     560    /*============ initialise AV ===============*/
     561    if (!InitAV(file))
     562        return TRANSCODE_EXIT_UNKNOWN_ERROR;
     563
     564    /* find video stream index */
     565    for (int i = 0; i < inputFC->nb_streams; i++)
    996566    {
    997         AVCodecContext *enc = inputContext->streams[i]->codec;
    998         if (enc->codec_type == CODEC_TYPE_VIDEO)
     567        if (inputFC->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
    999568        {
    1000             videoin_index = i;
     569            video_index = i;
    1001570            break;
    1002571        }
    1003572    }
    1004573
    1005     while (av_read_frame(inputContext, pkt) >= 0)
     574    /* no video stream */
     575    if (video_index == -1)
     576        return TRANSCODE_BUGGY_EXIT_INVALID_VIDEO;
     577
     578    av_init_packet(&pkt);
     579
     580    while (av_read_frame(inputFC, &pkt) >= 0)
    1006581    {
    1007         if (pkt->stream_index == videoin_index)
     582        if (pkt.stream_index == video_index)
    1008583        {
    1009             if (process_video(pkt, true))
    1010                 posMap[count] = pkt->pos;
     584            if (pkt.flags & PKT_FLAG_KEY)
     585                posMap[count] = pkt.pos;
    1011586            count++;
    1012587        }
    1013588    }
    1014     av_close_input_file(inputContext);
    1015     return 0;
     589
     590    // Close input file
     591    av_close_input_file(inputFC);
     592    inputFC = NULL;
     593
     594    if (chkTranscodeDB)
     595        JobQueue::ChangeJobComment(jobID, QString(QObject::tr("Transcode Completed")));
     596    else
     597        cerr << "Transcode Completed" << endl << flush;
     598
     599    return TRANSCODE_EXIT_OK;
    1016600}
  • programs/mythtranscode/main.cpp

     
    389389
    390390    if (found_infile)
    391391    {
     392#if 0
    392393        MPEG2trans *mpeg2trans = new MPEG2trans(&deleteMap);
    393         int err = mpeg2trans->DoTranscode(infile, tmpfile);
    394         if (err || mpeg2trans->BuildKeyframeIndex(tmpfile, posMap))
     394        bool succ = mpeg2trans->DoTranscode(infile, tmpfile);
     395        if (!succ || !mpeg2trans->BuildKeyframeIndex(tmpfile, posMap))
    395396            return TRANSCODE_EXIT_UNKNOWN_ERROR;
    396397        UpdatePositionMap(posMap, tmpfile + ".map", NULL);
     398#endif
    397399        return TRANSCODE_EXIT_OK;
    398400    }
    399401    Transcode *transcode = new Transcode(pginfo);
     
    412414    int exitcode = TRANSCODE_EXIT_OK;
    413415    if (result == REENCODE_MPEG2TRANS)
    414416    {
    415         if (useCutlist)
    416             pginfo->GetCutList(deleteMap);
    417         MPEG2trans *mpeg2trans = new MPEG2trans(&deleteMap);
     417        MPEG2trans *mpeg2trans = new MPEG2trans(pginfo, use_db);
    418418        if (build_index)
    419419        {
    420             if (mpeg2trans->BuildKeyframeIndex(infile, posMap))
    421                 return TRANSCODE_EXIT_UNKNOWN_ERROR;
     420            int err = mpeg2trans->BuildKeyframeIndex(infile, posMap);
     421            if (err)
     422                return err;
    422423            UpdatePositionMap(posMap, NULL, pginfo);
    423424        }
    424425        else
    425426        {
    426             int err = mpeg2trans->DoTranscode(infile, tmpfile);
    427             if (err || mpeg2trans->BuildKeyframeIndex(tmpfile, posMap))
    428                 return TRANSCODE_EXIT_UNKNOWN_ERROR;
    429             UpdatePositionMap(posMap, tmpfile + ".map", NULL);
    430             return TRANSCODE_EXIT_OK;
     427            int err = mpeg2trans->DoTranscode(infile, tmpfile, useCutlist);
     428            if (err)
     429                return err;
     430            err = mpeg2trans->BuildKeyframeIndex(tmpfile, posMap);
     431            if (err)
     432                return err;
     433            UpdatePositionMap(posMap, NULL, pginfo);
    431434        }
    432         // this is a hack, since the mpeg2 transcoder doesn't return anything
    433         // useful yet.
    434435        result = REENCODE_OK;
    435436    }
    436437    if (result == REENCODE_OK)
  • programs/mythtranscode/mpeg2trans.h

     
    1 #include <qmap.h>
    2 #include <qptrqueue.h>
    3 #include <qptrstack.h>
    4 #include "programinfo.h"
    5 #include "exitcodes.h"
    6 #include "mythcontext.h"
     1#ifndef MPEG2TRANS_H
     2#define MPEG2TRANS_H
    73
    8 extern "C" {
    94#include "avformat.h"
    10 }
    115
    12 typedef struct {
    13   uint8_t drop;
    14   uint8_t hour;
    15   uint8_t min;
    16   uint8_t sec;
    17   uint8_t pic;
    18 } gop_timestamp_t;
     6#include "mythcontext.h"
     7#include "programinfo.h"
    198
    20 class FrameBuffer
    21 {
    22   public:
    23     FrameBuffer(void) { size = 0; pkt.size = 0; pkt.data = NULL; delta = 0;}
    24     ~FrameBuffer(void) { if (pkt.data) free (pkt.data); }
    25     void setPkt(AVPacket *newpkt, int64_t del = 0);
    26     void setPkt(uint8_t *data, uint32_t newsize, int64_t newpts, int64_t del=0);
    27     AVPacket *getPkt(void) { return &pkt; }
    28     uint8_t *getData(void) { return pkt.data; }
    29     uint32_t getSize(void) { return pkt.size; }
    30     int64_t  getPTS(void)  { return pkt.pts; }
    31     int64_t  getDelta(void)  { return delta; }
    32     void Reset(void) { pkt.size = 0; }
    33   private:
    34     AVPacket pkt;
    35     int32_t size;
    36     int64_t delta;
    37 };
    38 
    399class MPEG2trans
    4010{
    41   public:
    42     MPEG2trans(QMap<long long, int> *map);
    43     ~MPEG2trans(void);
    44     int DoTranscode(QString inputFilename, QString outputFilename);
    45     int BuildKeyframeIndex(QString filename, QMap <long long, long long> &posMap);
    46   private:
    47     uint32_t parse_seq_header(uint8_t *PES_data, bool store);
    48     uint32_t get_gop_data(uint8_t *PES_data, gop_timestamp_t *gopts,
    49                           bool *closed, bool *store);
    50     void set_gop_data(uint8_t *PES_data, gop_timestamp_t *gopts,
    51                       bool closed, bool store);
    52     void update_timestamp(gop_timestamp_t *fixed_gopts, gop_timestamp_t *ts);
    53     void update_pic_frame_num(uint8_t *PES_data, int num);
    54     uint32_t get_ext_frame(uint8_t *PES_data);
     11    public:
     12        MPEG2trans(ProgramInfo *pginfo, bool use_db);
     13        ~MPEG2trans();
    5514
    56     void init_audioout_stream(void);
    57     void init_videoout_stream(void);
     15        int DoTranscode(QString &infile, QString &tmpfile, bool useCutlist);
     16        int BuildKeyframeIndex(QString &file, QMap <long long, long long> &posMap);
    5817
    59     void write_muxed_frame(AVStream *stream, AVPacket *pkt, int advance, int64_t delta = 0);
     18    private:
     19        bool InitAV(QString &inputfile);
     20        bool InitOutput(QString &outfile);
    6021
    61     bool check_ac3_header(uint8_t *buf);
    62     bool check_mp2_header(uint8_t *buf);
    63     bool check_video_header(uint8_t *buf);
     22        AVPacketList *ReadGop(int index, int64_t *min_dts, int64_t *min_pts, int64_t *end_pts);
     23        void FreeGop(AVPacketList *pkts);
     24        void FlushGop(AVPacketList *pkts, int64_t delta, int64_t start, int64_t end);
    6425
    65     uint32_t process_ac3_audio(AVPacket *pkt);
    66     uint32_t process_mp2_audio(AVPacket *pkt);
    67     void process_audio(AVPacket *pkt);
    68     bool process_video(AVPacket *pkt, bool gopsearch = false);
     26        bool chkTranscodeDB;
    6927
     28        ProgramInfo *m_pginfo;
    7029
    71     int video_width;
    72     int video_height;
    73     int video_aspect;
    74     uint32_t video_frame_rate;
    75     uint32_t video_bitrate;
    76     int pic_frame_count;
    77 
    78     bool use_ac3;
    79     uint32_t audio_freq;
    80     uint32_t audio_bitrate;
    81 
    82     int64_t goppts_delta;
    83 
    84     gop_timestamp_t *fixed_gop_ts;
    85 
    86     bool chopping;
    87     bool chop_end;
    88     int skipped_frames;
    89 
    90     bool got_audio;
    91     bool got_video;
    92     bool init_av;
    93 
    94     AVFormatContext *outputContext;
    95     AVFormatContext *inputContext;
    96 
    97     AVStream *audioout_st;
    98     AVStream *videoout_st;
    99 
    100     int audioin_index;
    101     int videoin_index;
    102 
    103     uint32_t last_frame_number;
    104     int64_t last_gop_pts;
    105     int64_t chop_start_pts;
    106     uint8_t video_frame_state;
    107 
    108     FrameBuffer audioFrame;
    109     FrameBuffer videoFrame;
    110     FrameBuffer seqFrame;
    111 
    112     QMap<long long, long long > cutlistMap;
    113     QMap<long long, long long>::Iterator cutlistIter;
    114 
    115     QPtrQueue<FrameBuffer> audioQueue;
    116     QPtrQueue<FrameBuffer> videoQueue;
    117     QPtrStack<FrameBuffer> audioPool;
    118     QPtrStack<FrameBuffer> videoPool;
    119 
    120     QPtrQueue<AVPacket> audskipQueue;
     30        AVFormatContext *inputFC;
     31        AVFormatContext *outputFC;
    12132};
     33
     34#endif