Ticket #158: mpeg2trans_v3.patch
File mpeg2trans_v3.patch, 56.6 KB (added by , 19 years ago) |
---|
-
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> 11 3 12 4 #include <iostream> 5 #include <iomanip> 6 13 7 using namespace std; 14 8 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 15 17 #include "mpeg2trans.h" 16 18 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 28 20 29 class SetBits 21 MPEG2trans::MPEG2trans(ProgramInfo *pginfo, bool use_db) 30 22 { 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; 39 24 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; 47 26 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; 52 29 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(); 62 32 } 63 33 64 void FrameBuffer::setPkt(AVPacket *newpkt, int64_t del)34 MPEG2trans::~MPEG2trans() 65 35 { 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;76 36 } 77 37 78 void FrameBuffer::setPkt(uint8_t *data, uint32_t newsize, int64_t newpts, 79 int64_t del) 38 bool MPEG2trans::InitAV(QString &inputfile) 80 39 { 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; 88 41 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 } 93 49 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) 107 53 { 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; 110 58 } 111 }112 59 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); 117 62 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; 127 64 } 128 65 129 MPEG2trans::MPEG2trans(QMap<long long, int> *map)66 bool MPEG2trans::InitOutput(QString &outfile) 130 67 { 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; 138 69 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 } 141 79 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 } 143 90 144 fixed_gop_ts = NULL;91 outputFC->oformat = fmt; 145 92 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); 149 98 150 got_audio = false; 151 got_video = false; 152 init_av = false; 99 ost->stream_copy = 1; 153 100 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) 174 103 { 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) 176 112 { 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; 182 128 } 183 129 } 184 cutlistIter = cutlistMap.begin();185 130 } 186 use_ac3 = false;187 audioin_index = -1;188 videoin_index = -1;189 131 190 av_register_all(); 191 } 132 strncpy(outputFC->filename, outfile.ascii(), sizeof(outputFC->filename)); 192 133 193 MPEG2trans::~MPEG2trans(void) { 194 };134 // Dump output format 135 dump_format(outputFC, 0, outfile.ascii(), 1); 195 136 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) 200 140 { 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; 210 145 } 211 else212 {213 FrameBuffer *buffer = videoPool.pop();214 buffer->setPkt(pkt, delta);215 if (advance)216 videoQueue.enqueue(buffer);217 else218 {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 no249 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 }260 146 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) 263 150 { 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; 300 155 } 301 // empty audio buffer after writing the last video/audio frames302 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 }312 156 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; 326 158 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; 332 160 } 333 161 334 uint32_t MPEG2trans::process_ac3_audio(AVPacket *pkt)162 AVPacketList *MPEG2trans::ReadGop(int index, int64_t *min_dts, int64_t *min_pts, int64_t *end_pts) 335 163 { 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; 363 167 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); 368 169 369 if (check_mp2_header(buf))170 do 370 171 { 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; 414 174 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++; 427 177 428 if ( chopping && thispkt->pts >= chop_start_pts || chop_end)178 if (iframe_count < 2) 429 179 { 430 while(! audskipQueue.isEmpty() && 431 audskipQueue.head()->pts < last_gop_pts) 180 if (!pkts) 432 181 { 433 AVPacket *store = audskipQueue.dequeue(); 434 delete store; 182 pkts = new AVPacketList; 183 pkts->next = NULL; 184 pkts->pkt = pkt; 435 185 } 436 if (chopping && thispkt->pts >= last_gop_pts || 437 chop_end && pic_frame_count < 2) 186 else 438 187 { 439 // store audio frame440 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; 446 195 } 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) 453 198 { 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; 458 205 } 459 write_muxed_frame(audioout_st, thispkt, true, goppts_delta);460 206 } 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 214 void MPEG2trans::FreeGop(AVPacketList *pkts) 215 { 216 AVPacketList *t = pkts; 217 AVPacketList *u; 218 219 while (t) 478 220 { 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; 481 226 } 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 }490 227 } 491 228 492 bool MPEG2trans::process_video(AVPacket *pkt, bool gopsearch)229 int MPEG2trans::DoTranscode(QString &infile, QString &tmpfile, bool useCutlist) 493 230 { 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; 498 233 499 videoFrame.Reset(); 500 videoFrame.setPkt(pkt); 501 AVPacket *thispkt = videoFrame.getPkt(); 234 AVPacketList *pkts; 235 AVPacket pkt; 502 236 503 vidblkptr = thispkt->data; 504 len = thispkt->size; 237 int video_index = -1; 505 238 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) 510 252 { 511 uint32_t frame_len = 0;512 uint32_t pos = vidblkptr - thispkt->data; 513 if ( vidblkptr[3] == 0xb3) // sequence header253 jobID = JobQueue::GetJobID(JOB_TRANSCODE, m_pginfo->chanid, m_pginfo->startts); 254 255 if (jobID < 0) 514 256 { 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; 537 260 } 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")); 650 263 } 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()) 654 270 { 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()) 656 279 { 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 664 283 } 665 284 } 666 else // something went terribly wrong!667 {668 exit(TRANSCODE_BUGGY_EXIT_INVALID_VIDEO);669 }670 return(false);671 }672 285 673 int MPEG2trans::DoTranscode(QString inputFilename, QString outputFilename) 674 { 675 AVPacket pkt1, *pkt = &pkt1; 676 AVFormatParameters params, *ap = ¶ms; 677 int i, err; 286 if (useCutlist) 287 m_pginfo->GetCutList(deleteMap); 678 288 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++) 683 303 { 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; 687 305 } 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(); ) 690 311 { 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; 715 329 } 330 331 dMapIter++; 716 332 } 717 dump_format(inputContext, 0, inputFilename, 0);718 333 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; 741 337 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; 746 340 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); 753 343 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(); 761 346 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) 763 356 { 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; 770 359 } 771 av_close_input_file(inputContext);772 773 /* write the trailer, if any */774 av_write_trailer(outputContext);775 360 776 /* f ree 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++) 778 363 { 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; 811 368 } 812 369 } 813 else814 {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 }833 370 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()) 843 377 { 844 if ( ! check_video_header(PES_data))378 if (dMapIter.data() == 0) // keep frames 845 379 { 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 859 392 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 878 413 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); 893 417 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); 897 422 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); 917 426 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); 927 432 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 933 453 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); 937 457 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; 944 462 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; 950 469 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; 954 475 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; 968 537 } 969 538 970 int MPEG2trans::BuildKeyframeIndex(QString filename, QMap <long long, long long> &posMap)539 int MPEG2trans::BuildKeyframeIndex(QString &file, QMap <long long, long long> &posMap) 971 540 { 972 AVPacket pkt1, *pkt = &pkt1; 973 AVFormatParameters params, *ap = ¶ms; 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) 981 543 { 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"))); 985 552 } 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; 994 555 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++) 996 566 { 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) 999 568 { 1000 video in_index = i;569 video_index = i; 1001 570 break; 1002 571 } 1003 572 } 1004 573 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) 1006 581 { 1007 if (pkt ->stream_index == videoin_index)582 if (pkt.stream_index == video_index) 1008 583 { 1009 if (p rocess_video(pkt, true))1010 posMap[count] = pkt ->pos;584 if (pkt.flags & PKT_FLAG_KEY) 585 posMap[count] = pkt.pos; 1011 586 count++; 1012 587 } 1013 588 } 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; 1016 600 } -
programs/mythtranscode/main.cpp
389 389 390 390 if (found_infile) 391 391 { 392 #if 0 392 393 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)) 395 396 return TRANSCODE_EXIT_UNKNOWN_ERROR; 396 397 UpdatePositionMap(posMap, tmpfile + ".map", NULL); 398 #endif 397 399 return TRANSCODE_EXIT_OK; 398 400 } 399 401 Transcode *transcode = new Transcode(pginfo); … … 412 414 int exitcode = TRANSCODE_EXIT_OK; 413 415 if (result == REENCODE_MPEG2TRANS) 414 416 { 415 if (useCutlist) 416 pginfo->GetCutList(deleteMap); 417 MPEG2trans *mpeg2trans = new MPEG2trans(&deleteMap); 417 MPEG2trans *mpeg2trans = new MPEG2trans(pginfo, use_db); 418 418 if (build_index) 419 419 { 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; 422 423 UpdatePositionMap(posMap, NULL, pginfo); 423 424 } 424 425 else 425 426 { 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); 431 434 } 432 // this is a hack, since the mpeg2 transcoder doesn't return anything433 // useful yet.434 435 result = REENCODE_OK; 435 436 } 436 437 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 7 3 8 extern "C" {9 4 #include "avformat.h" 10 }11 5 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" 19 8 20 class FrameBuffer21 {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 39 9 class MPEG2trans 40 10 { 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(); 55 14 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); 58 17 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); 60 21 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); 64 25 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; 69 27 28 ProgramInfo *m_pginfo; 70 29 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; 121 32 }; 33 34 #endif