Revision a712d725 libavformat/nutenc.c
libavformat/nutenc.c | ||
---|---|---|
21 | 21 |
|
22 | 22 |
#include "nut.h" |
23 | 23 |
#include "tree.h" |
24 |
#include "mpegaudiodata.h" |
|
25 |
|
|
26 |
static int find_expected_header(AVCodecContext *c, int size, int key_frame, uint8_t out[64]){ |
|
27 |
int sample_rate= c->sample_rate; |
|
28 |
|
|
29 |
if(size>4096) |
|
30 |
return 0; |
|
31 |
|
|
32 |
AV_WB24(out, 1); |
|
33 |
|
|
34 |
if(c->codec_id == CODEC_ID_MPEG4){ |
|
35 |
if(key_frame){ |
|
36 |
return 3; |
|
37 |
}else{ |
|
38 |
out[3]= 0xB6; |
|
39 |
return 4; |
|
40 |
} |
|
41 |
}else if(c->codec_id == CODEC_ID_MPEG1VIDEO || c->codec_id == CODEC_ID_MPEG2VIDEO){ |
|
42 |
return 3; |
|
43 |
}else if(c->codec_id == CODEC_ID_H264){ |
|
44 |
return 3; |
|
45 |
}else if(c->codec_id == CODEC_ID_MP3 || c->codec_id == CODEC_ID_MP2){ |
|
46 |
int lsf, mpeg25, sample_rate_index, bitrate_index, frame_size; |
|
47 |
int layer= c->codec_id == CODEC_ID_MP3 ? 3 : 2; |
|
48 |
unsigned int header= 0xFFF00000; |
|
49 |
|
|
50 |
lsf = sample_rate < (24000+32000)/2; |
|
51 |
mpeg25 = sample_rate < (12000+16000)/2; |
|
52 |
sample_rate <<= lsf + mpeg25; |
|
53 |
if (sample_rate < (32000 + 44100)/2) sample_rate_index=2; |
|
54 |
else if(sample_rate < (44100 + 48000)/2) sample_rate_index=0; |
|
55 |
else sample_rate_index=1; |
|
56 |
|
|
57 |
sample_rate= ff_mpa_freq_tab[sample_rate_index] >> (lsf + mpeg25); |
|
58 |
|
|
59 |
for(bitrate_index=2; bitrate_index<30; bitrate_index++){ |
|
60 |
frame_size = ff_mpa_bitrate_tab[lsf][layer-1][bitrate_index>>1]; |
|
61 |
frame_size = (frame_size * 144000) / (sample_rate << lsf) + (bitrate_index&1); |
|
62 |
|
|
63 |
if(frame_size == size) |
|
64 |
break; |
|
65 |
} |
|
66 |
|
|
67 |
header |= (!lsf)<<19; |
|
68 |
header |= (4-layer)<<17; |
|
69 |
header |= 1<<16; //no crc |
|
70 |
AV_WB32(out, header); |
|
71 |
if(size <= 0) |
|
72 |
return 2; //we guess theres no crc, if there is one the user clearly doesnt care about overhead |
|
73 |
if(bitrate_index == 30) |
|
74 |
return -1; //something is wrong ... |
|
75 |
|
|
76 |
header |= (bitrate_index>>1)<<12; |
|
77 |
header |= sample_rate_index<<10; |
|
78 |
header |= (bitrate_index&1)<<9; |
|
79 |
|
|
80 |
return 2; //FIXME actually put the needed ones in build_elision_headers() |
|
81 |
return 3; //we guess that the private bit isnt set |
|
82 |
//FIXME the above asumtations should be checked, if these turn out false too often something should be done |
|
83 |
} |
|
84 |
return 0; |
|
85 |
} |
|
86 |
|
|
87 |
static int find_header_idx(AVFormatContext *s, AVCodecContext *c, int size, int frame_type){ |
|
88 |
NUTContext *nut = s->priv_data; |
|
89 |
uint8_t out[64]; |
|
90 |
int i; |
|
91 |
int len= find_expected_header(c, size, frame_type, out); |
|
92 |
|
|
93 |
//av_log(NULL, AV_LOG_ERROR, "expected_h len=%d size=%d codec_id=%d\n", len, size, c->codec_id); |
|
94 |
|
|
95 |
for(i=1; i<nut->header_count; i++){ |
|
96 |
if( len == nut->header_len[i] |
|
97 |
&& !memcmp(out, nut->header[i], len)){ |
|
98 |
// av_log(NULL, AV_LOG_ERROR, "found %d\n", i); |
|
99 |
return i; |
|
100 |
} |
|
101 |
} |
|
102 |
// av_log(NULL, AV_LOG_ERROR, "nothing found\n"); |
|
103 |
return 0; |
|
104 |
} |
|
105 |
|
|
106 |
static void build_elision_headers(AVFormatContext *s){ |
|
107 |
NUTContext *nut = s->priv_data; |
|
108 |
int i; |
|
109 |
//FIXME this is lame |
|
110 |
//FIXME write a 2pass mode to find the maximal headers |
|
111 |
const static uint8_t headers[][5]={ |
|
112 |
{3, 0x00, 0x00, 0x01}, |
|
113 |
{4, 0x00, 0x00, 0x01, 0xB6}, |
|
114 |
{2, 0xFF, 0xFA}, //mp3+crc |
|
115 |
{2, 0xFF, 0xFB}, //mp3 |
|
116 |
{2, 0xFF, 0xFC}, //mp2+crc |
|
117 |
{2, 0xFF, 0xFD}, //mp2 |
|
118 |
}; |
|
119 |
|
|
120 |
nut->header_count= 7; |
|
121 |
for(i=1; i<nut->header_count; i++){ |
|
122 |
nut->header_len[i]= headers[i-1][0]; |
|
123 |
nut->header [i]= &headers[i-1][1]; |
|
124 |
} |
|
125 |
} |
|
24 | 126 |
|
25 | 127 |
static void build_frame_code(AVFormatContext *s){ |
26 | 128 |
NUTContext *nut = s->priv_data; |
... | ... | |
63 | 165 |
ft->flags|= FLAG_SIZE_MSB | FLAG_CODED_PTS; |
64 | 166 |
ft->stream_id= stream_id; |
65 | 167 |
ft->size_mul=1; |
168 |
if(is_audio) |
|
169 |
ft->header_idx= find_header_idx(s, codec, -1, key_frame); |
|
66 | 170 |
start2++; |
67 | 171 |
} |
68 | 172 |
} |
... | ... | |
80 | 184 |
ft->size_mul=frame_bytes + 2; |
81 | 185 |
ft->size_lsb=frame_bytes + pred; |
82 | 186 |
ft->pts_delta=pts; |
187 |
ft->header_idx= find_header_idx(s, codec, frame_bytes + pred, key_frame); |
|
83 | 188 |
start2++; |
84 | 189 |
} |
85 | 190 |
} |
... | ... | |
123 | 228 |
ft->size_mul= end3-start3; |
124 | 229 |
ft->size_lsb= index - start3; |
125 | 230 |
ft->pts_delta= pred_table[pred]; |
231 |
if(is_audio) |
|
232 |
ft->header_idx= find_header_idx(s, codec, -1, key_frame); |
|
126 | 233 |
} |
127 | 234 |
} |
128 | 235 |
} |
... | ... | |
212 | 319 |
} |
213 | 320 |
|
214 | 321 |
static void write_mainheader(NUTContext *nut, ByteIOContext *bc){ |
215 |
int i, j, tmp_pts, tmp_flags, tmp_stream, tmp_mul, tmp_size, tmp_fields; |
|
322 |
int i, j, tmp_pts, tmp_flags, tmp_stream, tmp_mul, tmp_size, tmp_fields, tmp_head_idx; |
|
323 |
int64_t tmp_match; |
|
216 | 324 |
|
217 | 325 |
put_v(bc, 3); /* version */ |
218 | 326 |
put_v(bc, nut->avf->nb_streams); |
... | ... | |
227 | 335 |
tmp_pts=0; |
228 | 336 |
tmp_mul=1; |
229 | 337 |
tmp_stream=0; |
338 |
tmp_match= 1-(1LL<<62); |
|
339 |
tmp_head_idx= 0; |
|
230 | 340 |
for(i=0; i<256;){ |
231 | 341 |
tmp_fields=0; |
232 | 342 |
tmp_size=0; |
... | ... | |
236 | 346 |
if(tmp_stream != nut->frame_code[i].stream_id) tmp_fields=3; |
237 | 347 |
if(tmp_size != nut->frame_code[i].size_lsb ) tmp_fields=4; |
238 | 348 |
// if(tmp_res != nut->frame_code[i].res ) tmp_fields=5; |
349 |
if(tmp_head_idx!=nut->frame_code[i].header_idx)tmp_fields=8; |
|
239 | 350 |
|
240 | 351 |
tmp_pts = nut->frame_code[i].pts_delta; |
241 | 352 |
tmp_flags = nut->frame_code[i].flags; |
... | ... | |
243 | 354 |
tmp_mul = nut->frame_code[i].size_mul; |
244 | 355 |
tmp_size = nut->frame_code[i].size_lsb; |
245 | 356 |
// tmp_res = nut->frame_code[i].res; |
357 |
tmp_head_idx= nut->frame_code[i].header_idx; |
|
246 | 358 |
|
247 | 359 |
for(j=0; i<256; j++,i++){ |
248 | 360 |
if(i == 'N'){ |
... | ... | |
255 | 367 |
if(nut->frame_code[i].size_mul != tmp_mul ) break; |
256 | 368 |
if(nut->frame_code[i].size_lsb != tmp_size+j) break; |
257 | 369 |
// if(nut->frame_code[i].res != tmp_res ) break; |
370 |
if(nut->frame_code[i].header_idx!= tmp_head_idx) break; |
|
258 | 371 |
} |
259 | 372 |
if(j != tmp_mul - tmp_size) tmp_fields=6; |
260 | 373 |
|
... | ... | |
266 | 379 |
if(tmp_fields>3) put_v(bc, tmp_size); |
267 | 380 |
if(tmp_fields>4) put_v(bc, 0 /*tmp_res*/); |
268 | 381 |
if(tmp_fields>5) put_v(bc, j); |
382 |
if(tmp_fields>6) put_v(bc, tmp_match); |
|
383 |
if(tmp_fields>7) put_v(bc, tmp_head_idx); |
|
384 |
} |
|
385 |
put_v(bc, nut->header_count-1); |
|
386 |
for(i=1; i<nut->header_count; i++){ |
|
387 |
put_v(bc, nut->header_len[i]); |
|
388 |
put_buffer(bc, nut->header[i], nut->header_len[i]); |
|
269 | 389 |
} |
270 | 390 |
} |
271 | 391 |
|
... | ... | |
419 | 539 |
} |
420 | 540 |
|
421 | 541 |
nut->max_distance = MAX_DISTANCE; |
542 |
build_elision_headers(s); |
|
422 | 543 |
build_frame_code(s); |
423 | 544 |
assert(nut->frame_code['N'].flags == FLAG_INVALID); |
424 | 545 |
|
... | ... | |
444 | 565 |
if(pkt->size > 2*nut->max_distance ) flags |= FLAG_CHECKSUM; |
445 | 566 |
if(FFABS(pkt->pts - nus->last_pts) |
446 | 567 |
> nus->max_pts_distance) flags |= FLAG_CHECKSUM; |
568 |
if( pkt->size < nut->header_len[fc->header_idx] |
|
569 |
|| (pkt->size > 4096 && fc->header_idx) |
|
570 |
|| memcmp(pkt->data, nut->header[fc->header_idx], nut->header_len[fc->header_idx])) |
|
571 |
flags |= FLAG_HEADER_IDX; |
|
447 | 572 |
|
448 | 573 |
return flags | (fc->flags & FLAG_CODED); |
449 | 574 |
} |
450 | 575 |
|
576 |
static int find_best_header_idx(NUTContext *nut, AVPacket *pkt){ |
|
577 |
int i; |
|
578 |
int best_i = 0; |
|
579 |
int best_len= 0; |
|
580 |
|
|
581 |
if(pkt->size > 4096) |
|
582 |
return 0; |
|
583 |
|
|
584 |
for(i=1; i<nut->header_count; i++){ |
|
585 |
if( pkt->size >= nut->header_len[i] |
|
586 |
&& nut->header_len[i] > best_len |
|
587 |
&& !memcmp(pkt->data, nut->header[i], nut->header_len[i])){ |
|
588 |
best_i= i; |
|
589 |
best_len= nut->header_len[i]; |
|
590 |
} |
|
591 |
} |
|
592 |
return best_i; |
|
593 |
} |
|
594 |
|
|
451 | 595 |
static int write_packet(AVFormatContext *s, AVPacket *pkt){ |
452 | 596 |
NUTContext *nut = s->priv_data; |
453 | 597 |
StreamContext *nus= &nut->stream[pkt->stream_index]; |
454 | 598 |
ByteIOContext *bc = s->pb, *dyn_bc; |
455 | 599 |
FrameCode *fc; |
456 | 600 |
int64_t coded_pts; |
457 |
int best_length, frame_code, flags, needed_flags, i; |
|
601 |
int best_length, frame_code, flags, needed_flags, i, header_idx, best_header_idx;
|
|
458 | 602 |
int key_frame = !!(pkt->flags & PKT_FLAG_KEY); |
459 | 603 |
int store_sp=0; |
460 | 604 |
int ret; |
... | ... | |
503 | 647 |
if(ff_lsb2full(nus, coded_pts) != pkt->pts) |
504 | 648 |
coded_pts= pkt->pts + (1<<nus->msb_pts_shift); |
505 | 649 |
|
650 |
best_header_idx= find_best_header_idx(nut, pkt); |
|
651 |
|
|
506 | 652 |
best_length=INT_MAX; |
507 | 653 |
frame_code= -1; |
508 | 654 |
for(i=0; i<256; i++){ |
... | ... | |
539 | 685 |
if(flags & FLAG_CODED_PTS) |
540 | 686 |
length += get_length(coded_pts); |
541 | 687 |
|
688 |
if( (flags & FLAG_CODED) |
|
689 |
&& nut->header_len[best_header_idx] > nut->header_len[fc->header_idx]+1){ |
|
690 |
flags |= FLAG_HEADER_IDX; |
|
691 |
} |
|
692 |
|
|
693 |
if(flags & FLAG_HEADER_IDX){ |
|
694 |
length += 1 - nut->header_len[best_header_idx]; |
|
695 |
}else{ |
|
696 |
length -= nut->header_len[fc->header_idx]; |
|
697 |
} |
|
698 |
|
|
542 | 699 |
length*=4; |
543 | 700 |
length+= !(flags & FLAG_CODED_PTS); |
544 | 701 |
length+= !(flags & FLAG_CHECKSUM); |
... | ... | |
552 | 709 |
fc= &nut->frame_code[frame_code]; |
553 | 710 |
flags= fc->flags; |
554 | 711 |
needed_flags= get_needed_flags(nut, nus, fc, pkt); |
712 |
header_idx= fc->header_idx; |
|
555 | 713 |
|
556 | 714 |
init_checksum(bc, ff_crc04C11DB7_update, 0); |
557 | 715 |
put_byte(bc, frame_code); |
... | ... | |
562 | 720 |
if(flags & FLAG_STREAM_ID) put_v(bc, pkt->stream_index); |
563 | 721 |
if(flags & FLAG_CODED_PTS) put_v(bc, coded_pts); |
564 | 722 |
if(flags & FLAG_SIZE_MSB) put_v(bc, pkt->size / fc->size_mul); |
723 |
if(flags & FLAG_HEADER_IDX) put_v(bc, header_idx= best_header_idx); |
|
565 | 724 |
|
566 | 725 |
if(flags & FLAG_CHECKSUM) put_le32(bc, get_checksum(bc)); |
567 | 726 |
else get_checksum(bc); |
568 | 727 |
|
569 |
put_buffer(bc, pkt->data, pkt->size);
|
|
728 |
put_buffer(bc, pkt->data + nut->header_len[header_idx], pkt->size - nut->header_len[header_idx]);
|
|
570 | 729 |
nus->last_flags= flags; |
571 | 730 |
|
572 | 731 |
//FIXME just store one per syncpoint |
Also available in: Unified diff