diff --git a/libavformat/nutenc.c b/libavformat/nutenc.c new file mode 100644 index 0000000000..6cb57f0ed9 --- /dev/null +++ b/libavformat/nutenc.c @@ -0,0 +1,353 @@ +/* + * nut muxer + * Copyright (c) 2004-2007 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nut.h" + +#define TRACE + +static void build_frame_code(AVFormatContext *s){ + NUTContext *nut = s->priv_data; + int key_frame, index, pred, stream_id; + int start=1; + int end= 254; + int keyframe_0_esc= s->nb_streams > 2; + int pred_table[10]; + + if(keyframe_0_esc){ + /* keyframe = 0 escape */ + FrameCode *ft= &nut->frame_code[start]; + ft->flags= FLAG_STREAM_ID | FLAG_SIZE_MSB | FLAG_CODED_PTS; + ft->size_mul=1; + start++; + } + + for(stream_id= 0; stream_idnb_streams; stream_id++){ + int start2= start + (end-start)*stream_id / s->nb_streams; + int end2 = start + (end-start)*(stream_id+1) / s->nb_streams; + AVCodecContext *codec = s->streams[stream_id]->codec; + int is_audio= codec->codec_type == CODEC_TYPE_AUDIO; + int intra_only= /*codec->intra_only || */is_audio; + int pred_count; + + for(key_frame=0; key_frame<2; key_frame++){ + if(intra_only && keyframe_0_esc && key_frame==0) + continue; + + { + FrameCode *ft= &nut->frame_code[start2]; + ft->flags= FLAG_KEY*key_frame; + ft->flags|= FLAG_SIZE_MSB | FLAG_CODED_PTS; + ft->stream_id= stream_id; + ft->size_mul=1; + start2++; + } + } + + key_frame= intra_only; +#if 1 + if(is_audio){ + int frame_bytes= codec->frame_size*(int64_t)codec->bit_rate / (8*codec->sample_rate); + int pts; + for(pts=0; pts<2; pts++){ + for(pred=0; pred<2; pred++){ + FrameCode *ft= &nut->frame_code[start2]; + ft->flags= FLAG_KEY*key_frame; + ft->stream_id= stream_id; + ft->size_mul=frame_bytes + 2; + ft->size_lsb=frame_bytes + pred; + ft->pts_delta=pts; + start2++; + } + } + }else{ + FrameCode *ft= &nut->frame_code[start2]; + ft->flags= FLAG_KEY | FLAG_SIZE_MSB; + ft->stream_id= stream_id; + ft->size_mul=1; + ft->pts_delta=1; + start2++; + } +#endif + + if(codec->has_b_frames){ + pred_count=5; + pred_table[0]=-2; + pred_table[1]=-1; + pred_table[2]=1; + pred_table[3]=3; + pred_table[4]=4; + }else if(codec->codec_id == CODEC_ID_VORBIS){ + pred_count=3; + pred_table[0]=2; + pred_table[1]=9; + pred_table[2]=16; + }else{ + pred_count=1; + pred_table[0]=1; + } + + for(pred=0; predframe_code[index]; + ft->flags= FLAG_KEY*key_frame; + ft->flags|= FLAG_SIZE_MSB; + ft->stream_id= stream_id; +//FIXME use single byte size and pred from last + ft->size_mul= end3-start3; + ft->size_lsb= index - start3; + ft->pts_delta= pred_table[pred]; + } + } + } + memmove(&nut->frame_code['N'+1], &nut->frame_code['N'], sizeof(FrameCode)*(255-'N')); + nut->frame_code[ 0].flags= + nut->frame_code[255].flags= + nut->frame_code['N'].flags= FLAG_INVALID; +} + +/** + * Gets the length in bytes which is needed to store val as v. + */ +static int get_length(uint64_t val){ + int i=1; + + while(val>>=7) + i++; + + return i; +} + +static void put_v(ByteIOContext *bc, uint64_t val){ + int i= get_length(val); + + while(--i>0) + put_byte(bc, 128 | (val>>(7*i))); + + put_byte(bc, val&127); +} + +/** + * stores a string as vb. + */ +static void put_str(ByteIOContext *bc, const char *string){ + int len= strlen(string); + + put_v(bc, len); + put_buffer(bc, string, len); +} + +static void put_s(ByteIOContext *bc, int64_t val){ + put_v(bc, 2*FFABS(val) - (val>0)); +} + +#ifdef TRACE +static inline void put_v_trace(ByteIOContext *bc, uint64_t v, char *file, char *func, int line){ + printf("get_v %5"PRId64" / %"PRIX64" in %s %s:%d\n", v, v, file, func, line); + + put_v(bc, v); +} + +static inline void put_s_trace(ByteIOContext *bc, int64_t v, char *file, char *func, int line){ + printf("get_s %5"PRId64" / %"PRIX64" in %s %s:%d\n", v, v, file, func, line); + + put_s(bc, v); +} +#define put_v(bc, v) put_v_trace(bc, v, __FILE__, __PRETTY_FUNCTION__, __LINE__) +#define put_s(bc, v) put_s_trace(bc, v, __FILE__, __PRETTY_FUNCTION__, __LINE__) +#endif + +static int put_packetheader(NUTContext *nut, ByteIOContext *bc, int max_size, int calculate_checksum){ + put_flush_packet(bc); + nut->packet_start= url_ftell(bc) - 8; + nut->written_packet_size = max_size; + + /* packet header */ + put_v(bc, nut->written_packet_size); /* forward ptr */ + + if(calculate_checksum) + init_checksum(bc, av_crc04C11DB7_update, 0); + + return 0; +} + +/** + * + * must not be called more then once per packet + */ +static int update_packetheader(NUTContext *nut, ByteIOContext *bc, int additional_size, int calculate_checksum){ + int64_t start= nut->packet_start; + int64_t cur= url_ftell(bc); + int size= cur - start - get_length(nut->written_packet_size) - 8; + + if(calculate_checksum) + size += 4; + + if(size != nut->written_packet_size){ + int i; + + assert( size <= nut->written_packet_size ); + + url_fseek(bc, start + 8, SEEK_SET); + for(i=get_length(size); i < get_length(nut->written_packet_size); i++) + put_byte(bc, 128); + put_v(bc, size); + + url_fseek(bc, cur, SEEK_SET); + nut->written_packet_size= size; //FIXME may fail if multiple updates with differing sizes, as get_length may differ + + if(calculate_checksum) + put_le32(bc, get_checksum(bc)); + } + + return 0; +} + +static int write_header(AVFormatContext *s){ + NUTContext *nut = s->priv_data; + ByteIOContext *bc = &s->pb; + AVCodecContext *codec; + int i, j, tmp_pts, tmp_flags, tmp_stream, tmp_mul, tmp_size, tmp_fields; + + nut->avf= s; + + nut->stream = av_mallocz(sizeof(StreamContext)*s->nb_streams); + nut->time_base= av_mallocz(sizeof(AVRational )*s->nb_streams); + + for(i=0; inb_streams; i++){ + AVStream *st= s->streams[i]; + int num, denom, ssize; + ff_parse_specific_params(st->codec, &num, &ssize, &denom); + + nut->stream[i].time_base= (AVRational){denom, num}; + + av_set_pts_info(st, 64, denom, num); + + for(j=0; jtime_base_count; j++){ + if(!memcmp(&nut->stream[i].time_base, &nut->time_base[j], sizeof(AVRational))){ + break; + } + } + nut->time_base[j]= nut->stream[i].time_base; + if(j==nut->time_base_count) + nut->time_base_count++; + } +//FIXME make nut->stream[i].time_base pointers into nut->time_base + + put_buffer(bc, ID_STRING, strlen(ID_STRING)); + put_byte(bc, 0); + + /* main header */ + put_be64(bc, MAIN_STARTCODE); + put_packetheader(nut, bc, 120+5*256/*FIXME check*/, 1); + + put_v(bc, 2); /* version */ + put_v(bc, s->nb_streams); + put_v(bc, MAX_DISTANCE); + put_v(bc, nut->time_base_count); + + for(i=0; itime_base_count; i++){ + put_v(bc, nut->time_base[i].num); + put_v(bc, nut->time_base[i].den); + } + + build_frame_code(s); + assert(nut->frame_code['N'].flags == FLAG_INVALID); + + tmp_pts=0; + tmp_mul=1; + tmp_stream=0; + for(i=0; i<256;){ + tmp_fields=0; + tmp_size=0; +// tmp_res=0; + if(tmp_pts != nut->frame_code[i].pts_delta) tmp_fields=1; + if(tmp_mul != nut->frame_code[i].size_mul ) tmp_fields=2; + if(tmp_stream != nut->frame_code[i].stream_id) tmp_fields=3; + if(tmp_size != nut->frame_code[i].size_lsb ) tmp_fields=4; +// if(tmp_res != nut->frame_code[i].res ) tmp_fields=5; + + tmp_pts = nut->frame_code[i].pts_delta; + tmp_flags = nut->frame_code[i].flags; + tmp_stream= nut->frame_code[i].stream_id; + tmp_mul = nut->frame_code[i].size_mul; + tmp_size = nut->frame_code[i].size_lsb; +// tmp_res = nut->frame_code[i].res; + + for(j=0; i<256; j++,i++){ + if(i == 'N'){ + j--; + continue; + } + if(nut->frame_code[i].pts_delta != tmp_pts ) break; + if(nut->frame_code[i].flags != tmp_flags ) break; + if(nut->frame_code[i].stream_id != tmp_stream) break; + if(nut->frame_code[i].size_mul != tmp_mul ) break; + if(nut->frame_code[i].size_lsb != tmp_size+j) break; +// if(nut->frame_code[i].res != tmp_res ) break; + } + if(j != tmp_mul - tmp_size) tmp_fields=6; + + put_v(bc, tmp_flags); + put_v(bc, tmp_fields); + if(tmp_fields>0) put_s(bc, tmp_pts); + if(tmp_fields>1) put_v(bc, tmp_mul); + if(tmp_fields>2) put_v(bc, tmp_stream); + if(tmp_fields>3) put_v(bc, tmp_size); + if(tmp_fields>4) put_v(bc, 0 /*tmp_res*/); + if(tmp_fields>5) put_v(bc, j); + } + + update_packetheader(nut, bc, 0, 1); + + put_flush_packet(bc); + + //FIXME stream header, ... + + return 0; +} + +static int write_packet(AVFormatContext *s, AVPacket *pkt){ + //FIXME + return 0; +} + +AVOutputFormat nut_muxer = { + "nut", + "nut format", + "video/x-nut", + "nut", + sizeof(NUTContext), +#ifdef CONFIG_LIBVORBIS + CODEC_ID_VORBIS, +#elif defined(CONFIG_LIBMP3LAME) + CODEC_ID_MP3, +#else + CODEC_ID_MP2, /* AC3 needs liba52 decoder */ +#endif + CODEC_ID_MPEG4, + write_header, + write_packet, +// write_trailer, + .flags = AVFMT_GLOBALHEADER, +};