Ticket #158: mpeg2trans.patch

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

improved MPEG2->MPEG2 transcoding patch

  • programs/mythtranscode/main.cpp

    diff -urNad --exclude=CVS --exclude=.svn ./programs/mythtranscode/main.cpp /tmp/dpep-work.gMqn82/mythtv/programs/mythtranscode/main.cpp
    old new  
    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);
    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, use_db);
     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.cpp

    diff -urNad --exclude=CVS --exclude=.svn ./programs/mythtranscode/mpeg2trans.cpp /tmp/dpep-work.gMqn82/mythtv/programs/mythtranscode/mpeg2trans.cpp
    old new  
    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>
    13 using namespace std;
     5#include <iomanip>
    146
    15 #include "mpeg2trans.h"
     7using namespace std;
    168
    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 };
     9#include "avformat.h"
     10#include "avcodec.h"
    2811
    29 class SetBits
    30 {
    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 };
     12#include "mythcontext.h"
     13#include "programinfo.h"
     14#include "exitcodes.h"
     15#include "jobqueue.h"
    3916
    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)
     17#include "mpeg2trans.h"
    4718
    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};
     19//#define MPEG2trans_DEBUG
    5220
    53 int mp2_debug = 0;
    54 void DPRINTF(int level, const char *format, ...)
     21MPEG2trans::MPEG2trans(ProgramInfo *pginfo)
    5522{
    56   if (level < mp2_debug) {
    57     va_list ap;
    58     va_start(ap, format);
    59     vprintf(format, ap);
    60     va_end(ap);
    61   }
    62 }
     23    m_pginfo = pginfo;
    6324
    64 void FrameBuffer::setPkt(AVPacket *newpkt, int64_t del)
    65 {
    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;
     25    inputFC = NULL;
     26    outputFC = NULL;
     27
     28    // Register all formats and codecs
     29    av_register_all();
    7630}
    7731
    78 void FrameBuffer::setPkt(uint8_t *data, uint32_t newsize, int64_t newpts,
    79                          int64_t del)
     32MPEG2trans::~MPEG2trans()
    8033{
    81     AVPacket newpkt;
    82     newpkt.size = newsize;
    83     newpkt.pts = newpts;
    84     newpkt.data = data;
    85     newpkt.flags = 0;
    86     setPkt(&newpkt, del);
    8734}
    8835
    89 uint32_t GetBits::GetNext(int num)
     36bool MPEG2trans::InitAV(QString &inputfile)
    9037{
    91     uint32_t value = 0, gb_long;
    92     int offset, offset_r, offset_b;
     38    int ret;
    9339
    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 }
     40    // Open recording
     41    ret = av_open_input_file(&inputFC, inputfile.ascii(), NULL, 0, NULL);
     42    if (ret != 0)
     43    {
     44        cerr << "Couldn't open input file, error #" << ret << endl;
     45        return false;
     46    }
    10347
    104 void GetBits::MarkerBit(void)
    105 {
    106     if (!GetNext(1))
     48    // Getting stream information
     49    ret = av_find_stream_info(inputFC);
     50    if (ret < 0)
    10751    {
    108         VERBOSE(VB_IMPORTANT, "Marker bit was not valid!");
    109         exit(TRANSCODE_BUGGY_EXIT_INVALID_MARKER);
     52        cerr << "Couldn't get stream info, error #" << ret << endl;
     53        av_close_input_file(inputFC);
     54        inputFC = NULL;
     55        return false;
    11056    }
    111 }
    11257
    113 void SetBits::SetNext(uint32_t value, int num)
    114 {
    115     uint32_t sb_long, mask;
    116     int offset, offset_r, offset_b;
     58    // Dump stream information
     59    dump_format(inputFC, 0, inputfile.ascii(), false);
    11760
    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;
     61    return true;
    12762}
    12863
    129 MPEG2trans::MPEG2trans(QMap<long long, int> *map)
     64bool MPEG2trans::InitOutput(QString &outfile)
    13065{
    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;
    138 
    139     audio_freq = 48000;
    140     audio_bitrate = 0;
    141 
    142     goppts_delta = 0;
    143 
    144     fixed_gop_ts = NULL;
     66    int ret;
    14567
    146     chopping = false;
    147     chop_end = false;
    148     skipped_frames = 0;
     68    // Allocate output format context
     69    outputFC = av_alloc_format_context();
     70    if (!outputFC)
     71    {
     72        cerr << "Couldn't allocate output context\n";
     73        av_close_input_file(inputFC);
     74        inputFC = NULL;
     75        return false;
     76    }
    14977
    150     got_audio = false;
    151     got_video = false;
    152     init_av = false;
     78    // Getting output file format
     79    AVOutputFormat *fmt = guess_format("dvd", outfile.ascii(), "video/mpeg");
     80//    AVOutputFormat *fmt = guess_format("vob", outfile.ascii(), "video/mpeg");
     81    if (!fmt)
     82    {
     83        cerr << "Couldn't get VOB output context\n";
     84        av_close_input_file(inputFC);
     85        inputFC = NULL;
     86        return false;
     87    }
    15388
    154     outputContext = NULL;
    155     inputContext = NULL;
     89    outputFC->oformat = fmt;
    15690
    157     audioout_st = NULL;
    158     videoout_st = NULL;
     91    // Allocate output streams
     92    for (int i = 0; i < inputFC->nb_streams; i++)
     93    {
     94        AVStream *ist = inputFC->streams[i];
     95        AVStream *ost = av_new_stream(outputFC, ist->id);
    15996
    160     last_frame_number = 0;
    161     last_gop_pts = 0;
    162     chop_start_pts = 0;
    163     video_frame_state = 0;
     97        ost->stream_copy = 1;
    16498
    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++)
     99        // copy codec data from input streams
     100        if (ost->codec && ist->codec)
    174101        {
    175             if (mapIter.data())
     102            ost->codec->codec_id = ist->codec->codec_id;
     103            ost->codec->codec_type = ist->codec->codec_type;
     104            if (!ost->codec->codec_tag)
     105                ost->codec->codec_tag = ist->codec->codec_tag;
     106            ost->codec->bit_rate = ist->codec->bit_rate;
     107            ost->codec->extradata = ist->codec->extradata;
     108            ost->codec->extradata_size = ist->codec->extradata_size;
     109            switch (ost->codec->codec_type)
    176110            {
    177                 long long start;
    178                 start = mapIter.key();
    179                 mapIter++;
    180                 if (mapIter != map->end() && ! mapIter.data())
    181                     cutlistMap[start] = mapIter.key();
     111                case CODEC_TYPE_AUDIO:
     112                    ost->codec->sample_rate = ist->codec->sample_rate;
     113                    ost->codec->channels = ist->codec->channels;
     114                    ost->codec->frame_size = ist->codec->frame_size;
     115                    ost->codec->block_align = ist->codec->block_align;
     116                    break;
     117                case CODEC_TYPE_VIDEO:
     118                    ost->codec->time_base = ist->codec->time_base;
     119                    ost->codec->width = ist->codec->width;
     120                    ost->codec->height = ist->codec->height;
     121                    ost->codec->has_b_frames = ist->codec->has_b_frames;
     122                    break;
     123                default:
     124                    cerr << "dropping unknown stream type "
     125                         << ost->codec->codec_type << endl;
    182126            }
    183127        }
    184         cutlistIter = cutlistMap.begin();
    185128    }
    186     use_ac3 = false;
    187     audioin_index = -1;
    188     videoin_index = -1;
    189129
    190     av_register_all();
    191 }
     130    strncpy(outputFC->filename, outfile.ascii(), sizeof(outputFC->filename));
    192131
    193 MPEG2trans::~MPEG2trans(void) {
    194 };
     132    // Dump output format
     133    dump_format(outputFC, 0, outfile.ascii(), 1);
    195134
    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)
    200     {
    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());
    210     }
    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)
     135    // Open output file
     136    ret = url_fopen(&outputFC->pb, outfile.ascii(), URL_WRONLY);
     137    if (ret < 0)
    233138    {
    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);
     139        cerr << "Couldn't open output file, error #" << ret << endl;
     140        av_close_input_file(inputFC);
     141        inputFC = NULL;
     142        return false;
    259143    }
    260144
    261     while ((vidpts < audpts && ! videoQueue.isEmpty()) ||
    262           (audpts <= vidpts && ! audioQueue.isEmpty()))
    263     {
    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         }
    300     }
    301     // empty audio buffer after writing the last video/audio frames
    302     if (chopping && videoQueue.isEmpty())
     145    // Set output parameters
     146    ret = av_set_parameters(outputFC, NULL);
     147    if (ret < 0)
    303148    {
    304         while(! audioQueue.isEmpty())
    305         {
    306             FrameBuffer *buf = audioQueue.dequeue();
    307             buf->Reset();
    308             audioPool.push(buf);
    309         }
     149        cerr << "Couldn't set parameters, error #" << ret << endl;
     150        av_close_input_file(inputFC);
     151        inputFC = NULL;
     152        return false;
    310153    }
    311 }
    312154
    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 }
     155    outputFC->max_delay = 700000;
    326156
    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;
     157    return true;
    332158}
    333159
    334 uint32_t MPEG2trans::process_ac3_audio(AVPacket *pkt)
     160AVPacketList *MPEG2trans::ReadGop(int index, int64_t *min_dts, int64_t *min_pts, int64_t *end_pts)
    335161{
    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 }
     162    AVPacketList *pkts = NULL;
     163    AVPacket pkt;
     164    int iframe_count = 0;
    363165
    364 uint32_t MPEG2trans::process_mp2_audio(AVPacket *pkt)
    365 {
    366     uint32_t frame_len = 0;
    367     uint8_t *buf = pkt->data;
     166    av_init_packet(&pkt);
    368167
    369     if (check_mp2_header(buf))
     168    do
    370169    {
    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 }
     170        int ret = av_read_frame(inputFC, &pkt);
     171        if (ret < 0) break;
    414172
    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;
     173        if ((inputFC->streams[pkt.stream_index]->codec->codec_type == CODEC_TYPE_VIDEO) && (pkt.flags & PKT_FLAG_KEY))
     174            iframe_count++;
    427175
    428         if (chopping && thispkt->pts >= chop_start_pts || chop_end)
     176        if (iframe_count < 2)
    429177        {
    430             while(! audskipQueue.isEmpty() &&
    431                   audskipQueue.head()->pts < last_gop_pts)
     178            if (!pkts)
    432179            {
    433                 AVPacket *store = audskipQueue.dequeue();
    434                 delete store;
     180                pkts = new AVPacketList;
     181                pkts->next = NULL;
     182                pkts->pkt = pkt;
    435183            }
    436             if (chopping && thispkt->pts >= last_gop_pts ||
    437                 chop_end && pic_frame_count < 2)
     184            else
    438185            {
    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);
     186                AVPacketList *t = pkts;
     187
     188                while (t->next != NULL) t = t->next;
     189
     190                t->next = new AVPacketList;
     191                t->next->next = NULL;
     192                t->next->pkt = pkt;
    446193            }
    447             if (! chop_end || pic_frame_count < 2)
    448                 processed_frame = true;
    449         }
    450         if (! processed_frame)
    451         {
    452             while(! audskipQueue.isEmpty())
     194
     195            if (pkt.stream_index == index)
    453196            {
    454                 AVPacket *store = audskipQueue.dequeue();
    455                 write_muxed_frame(audioout_st, store, true, goppts_delta);
    456                 delete [] store->data;
    457                 delete store;
     197                if (pkt.dts != (int64_t)AV_NOPTS_VALUE && min_dts)
     198                    if ((pkt.dts < *min_dts) || (*min_dts == (int64_t)AV_NOPTS_VALUE)) *min_dts = pkt.dts;
     199                if (pkt.pts != (int64_t)AV_NOPTS_VALUE && min_pts)
     200                    if ((pkt.pts < *min_pts) || (*min_pts == (int64_t)AV_NOPTS_VALUE))  *min_pts = pkt.pts;
     201                if (pkt.pts != (int64_t)AV_NOPTS_VALUE && end_pts)
     202                    if ((pkt.pts + pkt.duration > *end_pts) || (*end_pts == (int64_t)AV_NOPTS_VALUE)) *end_pts = pkt.pts + pkt.duration;
    458203            }
    459             write_muxed_frame(audioout_st, thispkt, true, goppts_delta);
    460204        }
    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)))
     205        else if (pkt.destruct) pkt.destruct(&pkt);
     206
     207    } while (iframe_count < 2);
     208   
     209    return pkts;
     210}
     211
     212void MPEG2trans::FreeGop(AVPacketList *pkts)
     213{
     214    AVPacketList *t = pkts;
     215    AVPacketList *u;
     216
     217    while (t)
    478218    {
    479         frame_start++;
    480         thispkt->size--;
    481     }
    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);
     219        if (t->pkt.destruct) t->pkt.destruct(&t->pkt);
     220
     221        u = t;
     222        t = t->next;
     223        delete u;
    489224    }
    490225}
    491226
    492 bool MPEG2trans::process_video(AVPacket *pkt, bool gopsearch)
     227int MPEG2trans::DoTranscode(QString &infile, QString &tmpfile, bool useCutlist, bool chkTranscodeDB)
    493228{
    494     bool skip_pic_frame = false;
    495     bool found_gop = false;
    496     uint8_t *vidblkptr, *frame_start = NULL;
    497     uint32_t len;
     229    QMap<long long, int> deleteMap;
     230    QMap<long long, long long> posMap;
    498231
    499     videoFrame.Reset();
    500     videoFrame.setPkt(pkt);
    501     AVPacket *thispkt = videoFrame.getPkt();
     232    AVPacketList *pkts;
     233    AVPacket pkt;
    502234
    503     vidblkptr = thispkt->data;
    504     len = thispkt->size;
     235    int video_index = -1;
    505236
    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))
    510     {
    511         uint32_t frame_len = 0;
    512         uint32_t pos = vidblkptr - thispkt->data;
    513         if (vidblkptr[3] == 0xb3) // sequence header
    514         {
    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);
    537         }
    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;
    650     }
    651     if (gopsearch)
    652         return false;
    653     if ((uint32_t)(vidblkptr - thispkt->data) == videoFrame.getSize())
     237    int64_t start_pts;
     238    int64_t end_pts;
     239    int64_t min_dts = AV_NOPTS_VALUE;
     240    int64_t delta_pts;
     241    int64_t next_start_pts = AV_NOPTS_VALUE;
     242
     243    int total_frames = 0;
     244    int completed_frames = 0;
     245
     246    int ret;
     247
     248    int jobID = -1;
     249    if (chkTranscodeDB)
    654250    {
    655         if (! chopping && !skip_pic_frame && frame_start)
     251        jobID = JobQueue::GetJobID(JOB_TRANSCODE, m_pginfo->chanid, m_pginfo->startts);
     252
     253        if (jobID < 0)
    656254        {
    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);
     255            VERBOSE(VB_IMPORTANT, "ERROR, Transcode called from JobQueue but "
     256                                  "no jobID found!");
     257            return TRANSCODE_EXIT_INVALID_CMDLINE;
    664258        }
     259
     260        JobQueue::ChangeJobComment(jobID, "0% " + QObject::tr("Completed"));
    665261    }
    666     else // something went terribly wrong!
    667     {
    668         exit(TRANSCODE_BUGGY_EXIT_INVALID_VIDEO);
    669     }
    670     return(false);
    671 }
    672262
    673 int MPEG2trans::DoTranscode(QString inputFilename, QString outputFilename)
    674 {
    675     AVPacket pkt1, *pkt = &pkt1;
    676     AVFormatParameters params, *ap = &params;
    677     int i, err;
     263    cerr << "Title: " << m_pginfo->title.ascii() << endl << flush;
    678264
    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)
    683     {
    684         VERBOSE(VB_IMPORTANT, QString("Failed to open file '%1', error code %2")
    685                 .arg(inputFilename).arg(err));
    686         return 1;
    687     }
    688     err = av_find_stream_info(inputContext);
    689     if (err < 0)
     265    m_pginfo->GetPositionMap(posMap, MARK_GOP_BYFRAME);
     266
     267    if (useCutlist)
     268        m_pginfo->GetCutList(deleteMap);
     269    else
    690270    {
    691         VERBOSE(VB_IMPORTANT,
    692                 QString("Could not find stream paramters for '%1',"
    693                         " error code %2").arg(inputFilename).arg(err));
    694         return 2;
     271        QMap<long long, long long>::Iterator pMapIter;
     272
     273        pMapIter = posMap.begin();
     274        deleteMap[pMapIter.key()] = 0;
     275
     276        pMapIter = posMap.end();
     277        pMapIter--;
     278        deleteMap[pMapIter.key()] = 1;
    695279    }
    696     for (i = 0; i < inputContext->nb_streams; i++)
     280
     281    QMap<long long, long long>::Iterator pMapIter;
     282#ifdef MPEG2trans_DEBUG
     283    // Iterate over the position map
     284    for (pMapIter = posMap.begin(); pMapIter != posMap.end(); pMapIter++)
    697285    {
    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;
    715         }
     286        cerr << "posMap[" << pMapIter.key() << "] = " << pMapIter.data() << endl;
    716287    }
    717     dump_format(inputContext, 0, inputFilename, 0);
    718 
    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());
    741 
    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);
     288#endif
    746289
    747         if (!videoout_st || !audioout_st)
     290    // Iterate over the cut list
     291    QMap<long long, int>::Iterator dMapIter;
     292    for (dMapIter = deleteMap.begin(); dMapIter != deleteMap.end(); )
     293    {
     294#ifdef MPEG2trans_DEBUG
     295        cerr << "delMap[" << dMapIter.key() << "] = " << dMapIter.data() << endl;
     296#endif
     297        if (dMapIter.data() == 0)
    748298        {
    749             VERBOSE(VB_IMPORTANT,
    750                     "Could not find relevant audio and video streams!");
    751             return 5;
    752         }
     299            int start_frame = dMapIter.key();
     300            dMapIter++;
    753301
    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;
     302#ifdef MPEG2trans_DEBUG
     303            cerr << "delMap[" << dMapIter.key() << "] = " << dMapIter.data() << endl;
     304#endif
     305            if (dMapIter != deleteMap.end())
     306            {
     307                int end_frame = dMapIter.key();
     308                total_frames += end_frame - start_frame;
     309            }
     310            else break;
    760311        }
    761312
    762     while (av_read_frame(inputContext, pkt) >= 0)
    763     {
    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);
     313        dMapIter++;
    770314    }
    771     av_close_input_file(inputContext);
    772315
    773     /* write the trailer, if any */
    774     av_write_trailer(outputContext);
    775    
    776     /* free the streams */
    777     while (outputContext->nb_streams)
    778     {
    779         av_freep(&outputContext->streams[--outputContext->nb_streams]);
    780     }
     316    /*============ initialise AV ===============*/
     317    if (!InitAV(infile))
     318        return TRANSCODE_EXIT_UNKNOWN_ERROR;
    781319
    782     /* close the output file */
    783     url_fclose(&outputContext->pb);
     320    if (!InitOutput(tmpfile))
     321        return TRANSCODE_EXIT_UNKNOWN_ERROR;
    784322
    785     /* free the stream */
    786     av_free(outputContext);
    787     return 0;
    788 }
     323    /*=========== transcoding/cuting ===========*/
     324    av_init_packet(&pkt);
    789325
    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;
    811         }
    812     }
    813     else
    814     {
    815         gb->SkipNext(50);
    816     }
    817     gb->SkipNext(12);
    818     if (gb->GetNext(1))
     326    // initialise cut list iterator
     327    dMapIter = deleteMap.begin();
     328
     329    // seek to first entry in deleteMap
     330#ifdef MPEG2trans_DEBUG
     331    cerr << "first entry in dMap: " << dMapIter.key() << endl;
     332    cerr << "infile offset: " << posMap[dMapIter.key()] << endl;
     333#endif
     334
     335    // write stream header
     336    ret = av_write_header(outputFC);
     337    if (ret < 0)
    819338    {
    820         gb->SkipNext(8*64);
    821         len += 64;
     339        cerr << "Error writing output header\n";
     340        return TRANSCODE_EXIT_UNKNOWN_ERROR;
    822341    }
    823     if (gb->GetNext(1))
     342   
     343    /* find the index of the first video stream */
     344    for (int i = 0; i < inputFC->nb_streams; i++)
    824345    {
    825         gb->SkipNext(8*64);
    826         len += 64;
     346        if (inputFC->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
     347        {
     348            video_index = i;
     349            break;
     350        }
    827351    }
    828     delete gb;
    829     len += 8;
    830     len += get_ext_frame(PES_data + len);
    831     return len;
    832 }
    833352
    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)
     353    /* check if no video stream */
     354    if (video_index == -1)
     355        return TRANSCODE_BUGGY_EXIT_INVALID_VIDEO;
     356   
     357    /* iterate over the entries in the delete map */
     358    while (dMapIter != deleteMap.end())
    843359    {
    844         if (! check_video_header(PES_data))
    845         {
    846             len++;
    847             PES_data++;
    848         }
    849         else if (PES_data[3] == 0xb2 || PES_data[3] == 0xb5)
     360        if (dMapIter.data() == 0)   // keep frames
    850361        {
    851             len += 4;
    852             PES_data +=4;
    853         }
    854         else
    855             done = true;
    856     }
    857     return len;
    858 }
     362            long long start_key = dMapIter.key();
     363            while ((posMap.find(start_key) == posMap.end()) && (start_key >= 0)) start_key--;
     364            if (posMap.find(start_key) == posMap.end())
     365            {
     366#ifdef MPEG2trans_DEBUG
     367                cerr << "Couldn't find keyframe to start from\n";
     368#endif
     369                continue;
     370            }
     371#ifdef MPEG2trans_DEBUG
     372            cerr << "Saving from frame #" << start_key;
     373#endif
    859374
    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 }
     375            dMapIter++;
     376            if (dMapIter == deleteMap.end())
     377            {
     378#ifdef MPEG2trans_DEBUG
     379                cerr << "Reached end of delete map\n";
     380#endif
     381                continue;
     382            }
     383            long long end_key = dMapIter.key();
     384            while ((posMap.find(end_key) == posMap.end()) && (end_key >= 0)) end_key--;
     385            if (posMap.find(end_key) == posMap.end())
     386            {
     387#ifdef MPEG2trang_DEBUG
     388                cerr << "Couldn't find keyframe to start from\n";
     389#endif
     390                continue;
     391            }
     392#ifdef MPEG2trans_DEBUG
     393            cerr << " to end of GOP starting at frame #" << end_key << endl;
     394#endif
    878395
    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 }
     396            /* seek to end gop */
     397            av_read_frame_flush(inputFC);
     398            url_fseek(&inputFC->pb, posMap[end_key], SEEK_SET);
    893399
    894 void MPEG2trans::update_timestamp(gop_timestamp_t *last_ts, gop_timestamp_t *ts)
    895 {
    896     uint32_t framerate;
     400            /* read in last gop of the save section */
     401            end_pts = AV_NOPTS_VALUE;
     402            pkts = ReadGop(video_index, NULL, NULL, &end_pts);
     403            FreeGop(pkts);
    897404
    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 }
     405            /* seek to start gop */
     406            av_read_frame_flush(inputFC);
     407            url_fseek(&inputFC->pb, posMap[start_key], SEEK_SET);
    917408
    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 }
     409            /* read in the first gop of the save section */
     410            delta_pts = AV_NOPTS_VALUE;
     411            start_pts = AV_NOPTS_VALUE;
     412            pkts = ReadGop(video_index, &min_dts, &start_pts, NULL);
     413            FreeGop(pkts);
    927414
    928 /**************************************************************/
    929 /* audio output */
    930 void MPEG2trans::init_audioout_stream(void)
    931 {
    932     AVCodecContext *c;
     415            /* calcuate actual delta used in pts/dts fixup */
     416            if (next_start_pts == (int64_t)AV_NOPTS_VALUE)
     417            {
     418                /* first section */
     419                /* minimum dts in first gop should translate to a dts of 0 */
     420                delta_pts = min_dts;
     421            }
     422            else
     423            {
     424                /* not first section */
     425                /* minimum pts of video_index should translate to next_start_pts */
     426                delta_pts = start_pts - next_start_pts;
     427            }
     428            next_start_pts = end_pts;
     429           
     430#ifdef MPEG2trans_DEBUG
     431            cerr << "Minimum PTS = " << start_pts << endl;
     432            cerr << "Maximum PTS = " << end_pts << endl;
     433            cerr << "I/O Delta   = " << delta_pts << endl;
     434#endif
    933435
    934     c = audioout_st->codec;
    935     c->codec_id = (inputContext->streams[audioin_index])->codec->codec_id;
    936     c->codec_type = CODEC_TYPE_AUDIO;
     436            /* seek to start gop */
     437            av_read_frame_flush(inputFC);
     438            url_fseek(&inputFC->pb, posMap[start_key], SEEK_SET);
    937439
    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 }
     440            /* repeat until pts of all streams are past end_pts */
     441            bool done;
     442            bool *dones = new bool[inputFC->nb_streams];
     443            for (int i = 0; i < inputFC->nb_streams; i++) dones[i] = false;
    944444
    945 /**************************************************************/
    946 /* video output */
    947 void MPEG2trans::init_videoout_stream()
    948 {
    949     AVCodecContext *c;
     445            do
     446            {
     447                /* read packet */
     448                ret = av_read_frame(inputFC, &pkt);
     449                if (ret < 0) break;
    950450
    951     c = videoout_st->codec;
    952     c->codec_id = (inputContext->streams[videoin_index])->codec->codec_id;
    953     c->codec_type = CODEC_TYPE_VIDEO;
     451                /* end pts is the pts of the last frame in the last gop + the duration of the frame */
     452                /* ie it is the pts of the first frame in the next gop after the cut point */
     453                /* if the current frame is to be decoded after end_pts we take this as an indication */
     454                /* that we are finished with this stream for the currect section */
     455                if (pkt.dts >= end_pts) dones[pkt.stream_index] = true;
    954456
    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);
     457                /* if the presentation time is between start_pts and end_pts save it in the output */
     458                if ((pkt.pts != (int64_t)AV_NOPTS_VALUE && pkt.pts >= start_pts && pkt.pts < end_pts) ||
     459                    (pkt.pts == (int64_t)AV_NOPTS_VALUE && pkt.dts >= min_dts && pkt.dts < end_pts))
     460                {
     461                    if (pkt.stream_index == video_index) completed_frames++;
     462
     463                    if (pkt.pts != (int64_t)AV_NOPTS_VALUE) pkt.pts -= delta_pts;
     464                    if (pkt.dts != (int64_t)AV_NOPTS_VALUE) pkt.dts -= delta_pts;
     465
     466#ifdef MPEG2trans_DEBUG
     467                    cerr << "T: " << pkt.pts << ", " << pkt.dts << endl;
     468#endif
     469                    av_interleaved_write_frame(outputFC, &pkt);
     470                }
     471                else if (pkt.destruct) pkt.destruct(&pkt);
     472
     473                done = true;
     474                for (int i = 0; i < inputFC->nb_streams; i++) done &= dones[i];
     475
     476                int percentage = 100 * completed_frames/total_frames;
     477                if (chkTranscodeDB)
     478                {
     479                    if (JobQueue::GetJobCmd(jobID) == JOB_STOP)
     480                    {
     481                        unlink(tmpfile.ascii());
     482                        /* should probably clean up here */
     483                        VERBOSE(VB_IMPORTANT, "Transcoding STOPped by JobQueue");
     484                        return TRANSCODE_EXIT_STOPPED;
     485                    }
     486                    JobQueue::ChangeJobComment(jobID, QString("%1% ").arg(percentage) +
     487                                                      QObject::tr("Completed"));
     488                }
     489                else
     490                    cerr << "Percent complete: " <<  percentage << "%\r" << flush;
     491
     492            } while (!done);
     493        }
     494        else    // delete frames
     495        {
     496            // skip frames
     497            dMapIter++;
     498            if (dMapIter == deleteMap.end()) continue;
     499        }
     500    }
     501
     502    /*============== Close AV ==============*/
     503    // Close output file
     504    url_fclose(&outputFC->pb);
     505    for (int i = 0; i < outputFC->nb_streams; i++) av_free(outputFC->streams[i]);
     506    av_free(outputFC);
     507    outputFC = NULL;
     508
     509    // Close input file
     510    av_close_input_file(inputFC);
     511    inputFC = NULL;
     512
     513    return TRANSCODE_EXIT_OK;
    968514}
    969515
    970 int MPEG2trans::BuildKeyframeIndex(QString filename, QMap <long long, long long> &posMap)
     516int MPEG2trans::BuildKeyframeIndex(QString &file, QMap <long long, long long> &posMap)
    971517{
    972     AVPacket pkt1, *pkt = &pkt1;
    973     AVFormatParameters params, *ap = &params;
    974     int i, err;
    975     uint32_t count = 0;
     518    AVPacket pkt;
     519    int video_index = -1;
     520    int count = 0;
    976521
    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)
    981     {
    982         VERBOSE(VB_IMPORTANT, QString("Failed to open file '%1', error code %2")
    983                 .arg(filename).arg(err));
    984         return 1;
    985     }
    986     err = av_find_stream_info(inputContext);
    987     if (err < 0)
    988     {
    989         VERBOSE(VB_IMPORTANT,
    990                 QString("Could not find stream paramters for '%1',"
    991                         " error code %2").arg(filename).arg(err));
    992         return 2;
    993     }
     522    /*============ initialise AV ===============*/
     523    if (!InitAV(file))
     524        return TRANSCODE_EXIT_UNKNOWN_ERROR;
    994525
    995     for (i = 0; i < inputContext->nb_streams; i++)
     526    /* find video stream index */
     527    for (int i = 0; i < inputFC->nb_streams; i++)
    996528    {
    997         AVCodecContext *enc = inputContext->streams[i]->codec;
    998         if (enc->codec_type == CODEC_TYPE_VIDEO)
     529        if (inputFC->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
    999530        {
    1000             videoin_index = i;
     531            video_index = i;
    1001532            break;
    1002533        }
    1003534    }
    1004535
    1005     while (av_read_frame(inputContext, pkt) >= 0)
     536    /* no video stream */
     537    if (video_index == -1)
     538        return TRANSCODE_BUGGY_EXIT_INVALID_VIDEO;
     539
     540    av_init_packet(&pkt);
     541
     542    while (av_read_frame(inputFC, &pkt) >= 0)
    1006543    {
    1007         if (pkt->stream_index == videoin_index)
     544        if (pkt.stream_index == video_index)
    1008545        {
    1009             if (process_video(pkt, true))
    1010                 posMap[count] = pkt->pos;
     546            if (pkt.flags & PKT_FLAG_KEY)
     547                posMap[count] = pkt.pos;
    1011548            count++;
    1012549        }
    1013550    }
    1014     av_close_input_file(inputContext);
    1015     return 0;
     551
     552    // Close input file
     553    av_close_input_file(inputFC);
     554    inputFC = NULL;
     555
     556    return TRANSCODE_EXIT_OK;
    1016557}
  • programs/mythtranscode/mpeg2trans.h

    diff -urNad --exclude=CVS --exclude=.svn ./programs/mythtranscode/mpeg2trans.h /tmp/dpep-work.gMqn82/mythtv/programs/mythtranscode/mpeg2trans.h
    old new  
    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 }
    11 
    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;
    195
    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 };
     6#include "mythcontext.h"
     7#include "programinfo.h"
    388
    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);
    55 
    56     void init_audioout_stream(void);
    57     void init_videoout_stream(void);
    58 
    59     void write_muxed_frame(AVStream *stream, AVPacket *pkt, int advance, int64_t delta = 0);
    60 
    61     bool check_ac3_header(uint8_t *buf);
    62     bool check_mp2_header(uint8_t *buf);
    63     bool check_video_header(uint8_t *buf);
    64 
    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);
    69 
    70 
    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;
     11    public:
     12        MPEG2trans(ProgramInfo *pginfo);
     13        ~MPEG2trans();
    9914
    100     int audioin_index;
    101     int videoin_index;
     15        int DoTranscode(QString &infile, QString &tmpfile, bool useCutlist, bool chkTranscodeDB);
     16        int BuildKeyframeIndex(QString &file, QMap <long long, long long> &posMap);
    10217
    103     uint32_t last_frame_number;
    104     int64_t last_gop_pts;
    105     int64_t chop_start_pts;
    106     uint8_t video_frame_state;
     18    private:
     19        bool InitAV(QString &inputfile);
     20        bool InitOutput(QString &outfile);
    10721
    108     FrameBuffer audioFrame;
    109     FrameBuffer videoFrame;
    110     FrameBuffer seqFrame;
     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);
    11125
    112     QMap<long long, long long > cutlistMap;
    113     QMap<long long, long long>::Iterator cutlistIter;
    11426
    115     QPtrQueue<FrameBuffer> audioQueue;
    116     QPtrQueue<FrameBuffer> videoQueue;
    117     QPtrStack<FrameBuffer> audioPool;
    118     QPtrStack<FrameBuffer> videoPool;
     27        ProgramInfo *m_pginfo;
    11928
    120     QPtrQueue<AVPacket> audskipQueue;
     29        AVFormatContext *inputFC;
     30        AVFormatContext *outputFC;
    12131};
     32
     33#endif