Mercurial > libavformat.hg
view nut.c @ 1960:c0289552590f libavformat
Change the vhook code to send real timestamps to the filters instead of the
current time of day, which is useless, and which the filters could just as
easily query for themselves.
patch by Bobby Bingham, uhmmmm gmail com
author | diego |
---|---|
date | Thu, 29 Mar 2007 05:24:35 +0000 |
parents | ac2a299df031 |
children | 3804e39efbfd |
line wrap: on
line source
/* * "NUT" Container Format muxer and demuxer (DRAFT-200403??) * Copyright (c) 2003 Alex Beregszaszi * Copyright (c) 2004 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 * * * Visit the official site at http://www.nut.hu/ * */ /* * TODO: * - index writing * - index packet reading support */ //#define DEBUG 1 #include <limits.h> #include "avformat.h" #include "mpegaudio.h" #include "riff.h" #include "adler32.h" #undef NDEBUG #include <assert.h> //#define TRACE //from /dev/random #define MAIN_STARTCODE (0x7A561F5F04ADULL + (((uint64_t)('N'<<8) + 'M')<<48)) #define STREAM_STARTCODE (0x11405BF2F9DBULL + (((uint64_t)('N'<<8) + 'S')<<48)) #define KEYFRAME_STARTCODE (0xE4ADEECA4569ULL + (((uint64_t)('N'<<8) + 'K')<<48)) #define INDEX_STARTCODE (0xDD672F23E64EULL + (((uint64_t)('N'<<8) + 'X')<<48)) #define INFO_STARTCODE (0xAB68B596BA78ULL + (((uint64_t)('N'<<8) + 'I')<<48)) #define ID_STRING "nut/multimedia container\0" #define MAX_DISTANCE (1024*16-1) #define MAX_SHORT_DISTANCE (1024*4-1) #define FLAG_DATA_SIZE 1 #define FLAG_KEY_FRAME 2 #define FLAG_INVALID 4 typedef struct { uint8_t flags; uint8_t stream_id_plus1; uint16_t size_mul; uint16_t size_lsb; int16_t timestamp_delta; uint8_t reserved_count; } FrameCode; typedef struct { int last_key_frame; int msb_timestamp_shift; int rate_num; int rate_den; int64_t last_pts; int64_t last_sync_pos; ///<pos of last 1/2 type frame int decode_delay; } StreamContext; typedef struct { AVFormatContext *avf; int written_packet_size; int64_t packet_start[3]; //0-> startcode less, 1-> short startcode 2-> long startcodes FrameCode frame_code[256]; unsigned int stream_count; uint64_t next_startcode; ///< stores the next startcode if it has alraedy been parsed but the stream isnt seekable StreamContext *stream; int max_distance; int max_short_distance; int rate_num; int rate_den; int short_startcode; } NUTContext; static char *info_table[][2]={ {NULL , NULL }, // end {NULL , NULL }, {NULL , "UTF8"}, {NULL , "v"}, {NULL , "s"}, {"StreamId" , "v"}, {"SegmentId" , "v"}, {"StartTimestamp" , "v"}, {"EndTimestamp" , "v"}, {"Author" , "UTF8"}, {"Title" , "UTF8"}, {"Description" , "UTF8"}, {"Copyright" , "UTF8"}, {"Encoder" , "UTF8"}, {"Keyword" , "UTF8"}, {"Cover" , "JPEG"}, {"Cover" , "PNG"}, }; static void update(NUTContext *nut, int stream_index, int64_t frame_start, int frame_type, int frame_code, int key_frame, int size, int64_t pts){ StreamContext *stream= &nut->stream[stream_index]; stream->last_key_frame= key_frame; nut->packet_start[ frame_type ]= frame_start; stream->last_pts= pts; } static void reset(AVFormatContext *s, int64_t global_ts){ NUTContext *nut = s->priv_data; int i; for(i=0; i<s->nb_streams; i++){ StreamContext *stream= &nut->stream[i]; stream->last_key_frame= 1; stream->last_pts= av_rescale(global_ts, stream->rate_num*(int64_t)nut->rate_den, stream->rate_den*(int64_t)nut->rate_num); } } static void build_frame_code(AVFormatContext *s){ NUTContext *nut = s->priv_data; int key_frame, index, pred, stream_id; int start=0; int end= 255; 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_DATA_SIZE; ft->stream_id_plus1= 0; ft->size_mul=1; ft->timestamp_delta=0; start++; } for(stream_id= 0; stream_id<s->nb_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_FRAME*key_frame; ft->flags|= FLAG_DATA_SIZE; ft->stream_id_plus1= stream_id + 1; ft->size_mul=1; ft->timestamp_delta=0; 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_FRAME*key_frame; ft->stream_id_plus1= stream_id + 1; ft->size_mul=frame_bytes + 2; ft->size_lsb=frame_bytes + pred; ft->timestamp_delta=pts; start2++; } } }else{ FrameCode *ft= &nut->frame_code[start2]; ft->flags= FLAG_KEY_FRAME | FLAG_DATA_SIZE; ft->stream_id_plus1= stream_id + 1; ft->size_mul=1; ft->timestamp_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; pred<pred_count; pred++){ int start3= start2 + (end2-start2)*pred / pred_count; int end3 = start2 + (end2-start2)*(pred+1) / pred_count; for(index=start3; index<end3; index++){ FrameCode *ft= &nut->frame_code[index]; ft->flags= FLAG_KEY_FRAME*key_frame; ft->flags|= FLAG_DATA_SIZE; ft->stream_id_plus1= stream_id + 1; //FIXME use single byte size and pred from last ft->size_mul= end3-start3; ft->size_lsb= index - start3; ft->timestamp_delta= pred_table[pred]; } } } memmove(&nut->frame_code['N'+1], &nut->frame_code['N'], sizeof(FrameCode)*(255-'N')); nut->frame_code['N'].flags= FLAG_INVALID; } static uint64_t get_v(ByteIOContext *bc) { uint64_t val = 0; for(;;) { int tmp = get_byte(bc); if (tmp&0x80) val= (val<<7) + tmp - 0x80; else{ //av_log(NULL, AV_LOG_DEBUG, "get_v()= %"PRId64"\n", (val<<7) + tmp); return (val<<7) + tmp; } } return -1; } static int get_str(ByteIOContext *bc, char *string, unsigned int maxlen){ unsigned int len= get_v(bc); if(len && maxlen) get_buffer(bc, string, FFMIN(len, maxlen)); while(len > maxlen){ get_byte(bc); len--; } if(maxlen) string[FFMIN(len, maxlen-1)]= 0; if(maxlen == len) return -1; else return 0; } static int64_t get_s(ByteIOContext *bc){ int64_t v = get_v(bc) + 1; if (v&1) return -(v>>1); else return (v>>1); } static uint64_t get_vb(ByteIOContext *bc){ uint64_t val=0; unsigned int i= get_v(bc); if(i>8) return UINT64_MAX; while(i--) val = (val<<8) + get_byte(bc); //av_log(NULL, AV_LOG_DEBUG, "get_vb()= %"PRId64"\n", val); return val; } #ifdef TRACE static inline uint64_t get_v_trace(ByteIOContext *bc, char *file, char *func, int line){ uint64_t v= get_v(bc); printf("get_v %5"PRId64" / %"PRIX64" in %s %s:%d\n", v, v, file, func, line); return v; } static inline int64_t get_s_trace(ByteIOContext *bc, char *file, char *func, int line){ int64_t v= get_s(bc); printf("get_s %5"PRId64" / %"PRIX64" in %s %s:%d\n", v, v, file, func, line); return v; } static inline uint64_t get_vb_trace(ByteIOContext *bc, char *file, char *func, int line){ uint64_t v= get_vb(bc); printf("get_vb %5"PRId64" / %"PRIX64" in %s %s:%d\n", v, v, file, func, line); return v; } #define get_v(bc) get_v_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) #define get_s(bc) get_s_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) #define get_vb(bc) get_vb_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) #endif static int get_packetheader(NUTContext *nut, ByteIOContext *bc, int calculate_checksum) { int64_t start, size; start= url_ftell(bc) - 8; size= get_v(bc); init_checksum(bc, calculate_checksum ? av_adler32_update : NULL, 1); nut->packet_start[2] = start; nut->written_packet_size= size; return size; } static int check_checksum(ByteIOContext *bc){ unsigned long checksum= get_checksum(bc); return checksum != get_be32(bc); } /** * */ static int get_length(uint64_t val){ int i; for (i=7; val>>i; i+=7); return i; } static uint64_t find_any_startcode(ByteIOContext *bc, int64_t pos){ uint64_t state=0; if(pos >= 0) url_fseek(bc, pos, SEEK_SET); //note, this may fail if the stream isnt seekable, but that shouldnt matter, as in this case we simply start where we are currently while(!url_feof(bc)){ state= (state<<8) | get_byte(bc); if((state>>56) != 'N') continue; switch(state){ case MAIN_STARTCODE: case STREAM_STARTCODE: case KEYFRAME_STARTCODE: case INFO_STARTCODE: case INDEX_STARTCODE: return state; } } return 0; } /** * find the given startcode. * @param code the startcode * @param pos the start position of the search, or -1 if the current position * @returns the position of the startcode or -1 if not found */ static int64_t find_startcode(ByteIOContext *bc, uint64_t code, int64_t pos){ for(;;){ uint64_t startcode= find_any_startcode(bc, pos); if(startcode == code) return url_ftell(bc) - 8; else if(startcode == 0) return -1; pos=-1; } } static int64_t lsb2full(StreamContext *stream, int64_t lsb){ int64_t mask = (1<<stream->msb_timestamp_shift)-1; int64_t delta= stream->last_pts - mask/2; return ((lsb - delta)&mask) + delta; } #ifdef CONFIG_MUXERS static void put_v(ByteIOContext *bc, uint64_t val) { int i; //av_log(NULL, AV_LOG_DEBUG, "put_v()= %"PRId64"\n", val); val &= 0x7FFFFFFFFFFFFFFFULL; // FIXME can only encode upto 63 bits currently i= get_length(val); for (i-=7; i>0; i-=7){ put_byte(bc, 0x80 | (val>>i)); } put_byte(bc, val&0x7f); } /** * 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){ if (val<=0) put_v(bc, -2*val ); else put_v(bc, 2*val-1); } static void put_vb(ByteIOContext *bc, uint64_t val){ int i; for (i=8; val>>i; i+=8); put_v(bc, i>>3); for(i-=8; i>=0; i-=8) put_byte(bc, (val>>i)&0xFF); } #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); } static inline void put_vb_trace(ByteIOContext *bc, uint64_t v, char *file, char *func, int line){ printf("get_vb %5"PRId64" / %"PRIX64" in %s %s:%d\n", v, v, file, func, line); put_vb(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__) #define put_vb(bc, v) put_vb_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[2]= 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_adler32_update, 1); 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[2]; int64_t cur= url_ftell(bc); int size= cur - start - get_length(nut->written_packet_size)/7 - 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+=7) put_byte(bc, 0x80); 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_be32(bc, get_checksum(bc)); } return 0; } static int nut_write_header(AVFormatContext *s) { NUTContext *nut = s->priv_data; ByteIOContext *bc = &s->pb; AVCodecContext *codec; int i, j, tmp_time, tmp_flags,tmp_stream, tmp_mul, tmp_size, tmp_fields; if (strcmp(s->filename, "./data/b-libav.nut")) { av_log(s, AV_LOG_ERROR, " libavformat NUT is non-compliant and disabled\n"); return -1; } nut->avf= s; nut->stream = av_mallocz(sizeof(StreamContext)*s->nb_streams); put_buffer(bc, ID_STRING, strlen(ID_STRING)); put_byte(bc, 0); nut->packet_start[2]= url_ftell(bc); /* main header */ put_be64(bc, MAIN_STARTCODE); put_packetheader(nut, bc, 120+5*256, 1); put_v(bc, 2); /* version */ put_v(bc, s->nb_streams); put_v(bc, MAX_DISTANCE); put_v(bc, MAX_SHORT_DISTANCE); put_v(bc, nut->rate_num=1); put_v(bc, nut->rate_den=2); put_v(bc, nut->short_startcode=0x4EFE79); build_frame_code(s); assert(nut->frame_code['N'].flags == FLAG_INVALID); tmp_time= tmp_flags= tmp_stream= tmp_mul= tmp_size= /*tmp_res=*/ INT_MAX; for(i=0; i<256;){ tmp_fields=0; tmp_size= 0; if(tmp_time != nut->frame_code[i].timestamp_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_plus1) 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_time = nut->frame_code[i].timestamp_delta; tmp_flags = nut->frame_code[i].flags; tmp_stream= nut->frame_code[i].stream_id_plus1; 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(nut->frame_code[i].timestamp_delta != tmp_time ) break; if(nut->frame_code[i].flags != tmp_flags ) break; if(nut->frame_code[i].stream_id_plus1 != 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_time); 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); /* stream headers */ for (i = 0; i < s->nb_streams; i++) { int nom, denom, ssize; codec = s->streams[i]->codec; put_be64(bc, STREAM_STARTCODE); put_packetheader(nut, bc, 120 + codec->extradata_size, 1); put_v(bc, i /*s->streams[i]->index*/); switch(codec->codec_type){ case CODEC_TYPE_VIDEO: put_v(bc, 0); break; case CODEC_TYPE_AUDIO: put_v(bc, 1); break; // case CODEC_TYPE_TEXT : put_v(bc, 2); break; case CODEC_TYPE_DATA : put_v(bc, 3); break; default: return -1; } if (codec->codec_tag) put_vb(bc, codec->codec_tag); else if (codec->codec_type == CODEC_TYPE_VIDEO) { put_vb(bc, codec_get_bmp_tag(codec->codec_id)); } else if (codec->codec_type == CODEC_TYPE_AUDIO) { put_vb(bc, codec_get_wav_tag(codec->codec_id)); } else put_vb(bc, 0); ff_parse_specific_params(codec, &nom, &ssize, &denom); nut->stream[i].rate_num= nom; nut->stream[i].rate_den= denom; av_set_pts_info(s->streams[i], 60, denom, nom); put_v(bc, codec->bit_rate); put_vb(bc, 0); /* no language code */ put_v(bc, nom); put_v(bc, denom); if(nom / denom < 1000) nut->stream[i].msb_timestamp_shift = 7; else nut->stream[i].msb_timestamp_shift = 14; put_v(bc, nut->stream[i].msb_timestamp_shift); put_v(bc, codec->has_b_frames); put_byte(bc, 0); /* flags: 0x1 - fixed_fps, 0x2 - index_present */ if(codec->extradata_size){ put_v(bc, 1); put_v(bc, codec->extradata_size); put_buffer(bc, codec->extradata, codec->extradata_size); } put_v(bc, 0); /* end of codec specific headers */ switch(codec->codec_type) { case CODEC_TYPE_AUDIO: put_v(bc, codec->sample_rate); put_v(bc, 1); put_v(bc, codec->channels); break; case CODEC_TYPE_VIDEO: put_v(bc, codec->width); put_v(bc, codec->height); put_v(bc, codec->sample_aspect_ratio.num); put_v(bc, codec->sample_aspect_ratio.den); put_v(bc, 0); /* csp type -- unknown */ break; default: break; } update_packetheader(nut, bc, 0, 1); } /* info header */ put_be64(bc, INFO_STARTCODE); put_packetheader(nut, bc, 30+strlen(s->author)+strlen(s->title)+ strlen(s->comment)+strlen(s->copyright)+strlen(LIBAVFORMAT_IDENT), 1); if (s->author[0]) { put_v(bc, 9); /* type */ put_str(bc, s->author); } if (s->title[0]) { put_v(bc, 10); /* type */ put_str(bc, s->title); } if (s->comment[0]) { put_v(bc, 11); /* type */ put_str(bc, s->comment); } if (s->copyright[0]) { put_v(bc, 12); /* type */ put_str(bc, s->copyright); } /* encoder */ if(!(s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT)){ put_v(bc, 13); /* type */ put_str(bc, LIBAVFORMAT_IDENT); } put_v(bc, 0); /* eof info */ update_packetheader(nut, bc, 0, 1); put_flush_packet(bc); return 0; } static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) { NUTContext *nut = s->priv_data; StreamContext *stream= &nut->stream[pkt->stream_index]; ByteIOContext *bc = &s->pb; int key_frame = 0, full_pts=0; AVCodecContext *enc; int64_t coded_pts; int frame_type, best_length, frame_code, flags, i, size_mul, size_lsb, time_delta; const int64_t frame_start= url_ftell(bc); int64_t pts= pkt->pts; int size= pkt->size; int stream_index= pkt->stream_index; enc = s->streams[stream_index]->codec; key_frame = !!(pkt->flags & PKT_FLAG_KEY); frame_type=0; if(frame_start + size + 20 - FFMAX(nut->packet_start[1], nut->packet_start[2]) > MAX_DISTANCE) frame_type=2; if(key_frame && !stream->last_key_frame) frame_type=2; if(frame_type>1){ int64_t global_ts= av_rescale(pts, stream->rate_den*(int64_t)nut->rate_num, stream->rate_num*(int64_t)nut->rate_den); reset(s, global_ts); put_be64(bc, KEYFRAME_STARTCODE); put_v(bc, global_ts); } assert(stream->last_pts != AV_NOPTS_VALUE); coded_pts = pts & ((1<<stream->msb_timestamp_shift)-1); if(lsb2full(stream, coded_pts) != pts) full_pts=1; if(full_pts) coded_pts= pts + (1<<stream->msb_timestamp_shift); best_length=INT_MAX; frame_code= -1; for(i=0; i<256; i++){ int stream_id_plus1= nut->frame_code[i].stream_id_plus1; int fc_key_frame; int length=0; size_mul= nut->frame_code[i].size_mul; size_lsb= nut->frame_code[i].size_lsb; time_delta= nut->frame_code[i].timestamp_delta; flags= nut->frame_code[i].flags; assert(size_mul > size_lsb); if(stream_id_plus1 == 0) length+= get_length(stream_index); else if(stream_id_plus1 - 1 != stream_index) continue; fc_key_frame= !!(flags & FLAG_KEY_FRAME); assert(key_frame==0 || key_frame==1); if(fc_key_frame != key_frame) continue; if(flags & FLAG_DATA_SIZE){ if(size % size_mul != size_lsb) continue; length += get_length(size / size_mul); }else if(size != size_lsb) continue; if(full_pts && time_delta) continue; if(!time_delta){ length += get_length(coded_pts); }else{ if(time_delta != pts - stream->last_pts) continue; } if(length < best_length){ best_length= length; frame_code=i; } // av_log(s, AV_LOG_DEBUG, "%d %d %d %d %d %d %d %d %d %d\n", key_frame, frame_type, full_pts, size, stream_index, flags, size_mul, size_lsb, stream_id_plus1, length); } assert(frame_code != -1); flags= nut->frame_code[frame_code].flags; size_mul= nut->frame_code[frame_code].size_mul; size_lsb= nut->frame_code[frame_code].size_lsb; time_delta= nut->frame_code[frame_code].timestamp_delta; #ifdef TRACE best_length /= 7; best_length ++; //frame_code if(frame_type==2){ best_length += 8; // startcode } av_log(s, AV_LOG_DEBUG, "kf:%d ft:%d pt:%d fc:%2X len:%2d size:%d stream:%d flag:%d mul:%d lsb:%d s+1:%d pts_delta:%d pts:%"PRId64" fs:%"PRId64"\n", key_frame, frame_type, full_pts ? 1 : 0, frame_code, best_length, size, stream_index, flags, size_mul, size_lsb, nut->frame_code[frame_code].stream_id_plus1,(int)(pts - stream->last_pts), pts, frame_start); // av_log(s, AV_LOG_DEBUG, "%d %d %d\n", stream->lru_pts_delta[0], stream->lru_pts_delta[1], stream->lru_pts_delta[2]); #endif assert(frame_type != 1); //short startcode not implemented yet put_byte(bc, frame_code); if(nut->frame_code[frame_code].stream_id_plus1 == 0) put_v(bc, stream_index); if (!time_delta){ put_v(bc, coded_pts); } if(flags & FLAG_DATA_SIZE) put_v(bc, size / size_mul); else assert(size == size_lsb); if(size > MAX_DISTANCE){ assert(frame_type > 1); } put_buffer(bc, pkt->data, size); update(nut, stream_index, frame_start, frame_type, frame_code, key_frame, size, pts); return 0; } static int nut_write_trailer(AVFormatContext *s) { NUTContext *nut = s->priv_data; ByteIOContext *bc = &s->pb; #if 0 int i; /* WRITE INDEX */ for (i = 0; s->nb_streams; i++) { put_be64(bc, INDEX_STARTCODE); put_packetheader(nut, bc, 64, 1); put_v(bc, s->streams[i]->id); put_v(bc, ...); update_packetheader(nut, bc, 0, 1); } #endif put_flush_packet(bc); av_freep(&nut->stream); return 0; } #endif //CONFIG_MUXERS static int nut_probe(AVProbeData *p) { int i; uint64_t code= 0xff; for (i = 0; i < p->buf_size; i++) { code = (code << 8) | p->buf[i]; if (code == MAIN_STARTCODE) return AVPROBE_SCORE_MAX; } return 0; } static int decode_main_header(NUTContext *nut){ AVFormatContext *s= nut->avf; ByteIOContext *bc = &s->pb; uint64_t tmp; int i, j, tmp_stream, tmp_mul, tmp_time, tmp_size, count, tmp_res; get_packetheader(nut, bc, 1); tmp = get_v(bc); if (tmp != 2){ av_log(s, AV_LOG_ERROR, "bad version (%"PRId64")\n", tmp); return -1; } nut->stream_count = get_v(bc); if(nut->stream_count > MAX_STREAMS){ av_log(s, AV_LOG_ERROR, "too many streams\n"); return -1; } nut->max_distance = get_v(bc); nut->max_short_distance = get_v(bc); nut->rate_num= get_v(bc); nut->rate_den= get_v(bc); nut->short_startcode= get_v(bc); if(nut->short_startcode>>16 != 'N'){ av_log(s, AV_LOG_ERROR, "invalid short startcode %X\n", nut->short_startcode); return -1; } for(i=0; i<256;){ int tmp_flags = get_v(bc); int tmp_fields= get_v(bc); if(tmp_fields>0) tmp_time = get_s(bc); if(tmp_fields>1) tmp_mul = get_v(bc); if(tmp_fields>2) tmp_stream= get_v(bc); if(tmp_fields>3) tmp_size = get_v(bc); else tmp_size = 0; if(tmp_fields>4) tmp_res = get_v(bc); else tmp_res = 0; if(tmp_fields>5) count = get_v(bc); else count = tmp_mul - tmp_size; while(tmp_fields-- > 6) get_v(bc); if(count == 0 || i+count > 256){ av_log(s, AV_LOG_ERROR, "illegal count %d at %d\n", count, i); return -1; } if(tmp_stream > nut->stream_count + 1){ av_log(s, AV_LOG_ERROR, "illegal stream number\n"); return -1; } for(j=0; j<count; j++,i++){ nut->frame_code[i].flags = tmp_flags ; nut->frame_code[i].timestamp_delta = tmp_time ; nut->frame_code[i].stream_id_plus1 = tmp_stream; nut->frame_code[i].size_mul = tmp_mul ; nut->frame_code[i].size_lsb = tmp_size+j; nut->frame_code[i].reserved_count = tmp_res ; } } if(nut->frame_code['N'].flags != FLAG_INVALID){ av_log(s, AV_LOG_ERROR, "illegal frame_code table\n"); return -1; } if(check_checksum(bc)){ av_log(s, AV_LOG_ERROR, "Main header checksum mismatch\n"); return -1; } return 0; } static int decode_stream_header(NUTContext *nut){ AVFormatContext *s= nut->avf; ByteIOContext *bc = &s->pb; int class, nom, denom, stream_id; uint64_t tmp; AVStream *st; get_packetheader(nut, bc, 1); stream_id= get_v(bc); if(stream_id >= nut->stream_count || s->streams[stream_id]) return -1; st = av_new_stream(s, stream_id); if (!st) return AVERROR_NOMEM; class = get_v(bc); tmp = get_vb(bc); st->codec->codec_tag= tmp; switch(class) { case 0: st->codec->codec_type = CODEC_TYPE_VIDEO; st->codec->codec_id = codec_get_bmp_id(tmp); if (st->codec->codec_id == CODEC_ID_NONE) av_log(s, AV_LOG_ERROR, "Unknown codec?!\n"); break; case 1: case 32: //compatibility st->codec->codec_type = CODEC_TYPE_AUDIO; st->codec->codec_id = codec_get_wav_id(tmp); if (st->codec->codec_id == CODEC_ID_NONE) av_log(s, AV_LOG_ERROR, "Unknown codec?!\n"); break; case 2: // st->codec->codec_type = CODEC_TYPE_TEXT; // break; case 3: st->codec->codec_type = CODEC_TYPE_DATA; break; default: av_log(s, AV_LOG_ERROR, "Unknown stream class (%d)\n", class); return -1; } s->bit_rate += get_v(bc); get_vb(bc); /* language code */ nom = get_v(bc); denom = get_v(bc); nut->stream[stream_id].msb_timestamp_shift = get_v(bc); st->codec->has_b_frames= nut->stream[stream_id].decode_delay= get_v(bc); get_byte(bc); /* flags */ /* codec specific data headers */ while(get_v(bc) != 0){ st->codec->extradata_size= get_v(bc); if((unsigned)st->codec->extradata_size > (1<<30)) return -1; st->codec->extradata= av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); get_buffer(bc, st->codec->extradata, st->codec->extradata_size); // url_fskip(bc, get_v(bc)); } if (st->codec->codec_type == CODEC_TYPE_VIDEO) /* VIDEO */ { st->codec->width = get_v(bc); st->codec->height = get_v(bc); st->codec->sample_aspect_ratio.num= get_v(bc); st->codec->sample_aspect_ratio.den= get_v(bc); get_v(bc); /* csp type */ } if (st->codec->codec_type == CODEC_TYPE_AUDIO) /* AUDIO */ { st->codec->sample_rate = get_v(bc); get_v(bc); // samplerate_den st->codec->channels = get_v(bc); } if(check_checksum(bc)){ av_log(s, AV_LOG_ERROR, "Stream header %d checksum mismatch\n", stream_id); return -1; } av_set_pts_info(s->streams[stream_id], 60, denom, nom); nut->stream[stream_id].rate_num= nom; nut->stream[stream_id].rate_den= denom; return 0; } static int decode_info_header(NUTContext *nut){ AVFormatContext *s= nut->avf; ByteIOContext *bc = &s->pb; get_packetheader(nut, bc, 1); for(;;){ int id= get_v(bc); char *name, *type, custom_name[256], custom_type[256]; if(!id) break; else if(id >= sizeof(info_table)/sizeof(info_table[0])){ av_log(s, AV_LOG_ERROR, "info id is too large %d %zd\n", id, sizeof(info_table)/sizeof(info_table[0])); return -1; } type= info_table[id][1]; name= info_table[id][0]; //av_log(s, AV_LOG_DEBUG, "%d %s %s\n", id, type, name); if(!type){ get_str(bc, custom_type, sizeof(custom_type)); type= custom_type; } if(!name){ get_str(bc, custom_name, sizeof(custom_name)); name= custom_name; } if(!strcmp(type, "v")){ get_v(bc); }else{ if(!strcmp(name, "Author")) get_str(bc, s->author, sizeof(s->author)); else if(!strcmp(name, "Title")) get_str(bc, s->title, sizeof(s->title)); else if(!strcmp(name, "Copyright")) get_str(bc, s->copyright, sizeof(s->copyright)); else if(!strcmp(name, "Description")) get_str(bc, s->comment, sizeof(s->comment)); else get_str(bc, NULL, 0); } } if(check_checksum(bc)){ av_log(s, AV_LOG_ERROR, "Info header checksum mismatch\n"); return -1; } return 0; } static int nut_read_header(AVFormatContext *s, AVFormatParameters *ap) { NUTContext *nut = s->priv_data; ByteIOContext *bc = &s->pb; int64_t pos; int inited_stream_count; nut->avf= s; /* main header */ pos=0; for(;;){ pos= find_startcode(bc, MAIN_STARTCODE, pos)+1; if (pos<0){ av_log(s, AV_LOG_ERROR, "no main startcode found\n"); return -1; } if(decode_main_header(nut) >= 0) break; } s->bit_rate = 0; nut->stream = av_malloc(sizeof(StreamContext)*nut->stream_count); /* stream headers */ pos=0; for(inited_stream_count=0; inited_stream_count < nut->stream_count;){ pos= find_startcode(bc, STREAM_STARTCODE, pos)+1; if (pos<0+1){ av_log(s, AV_LOG_ERROR, "not all stream headers found\n"); return -1; } if(decode_stream_header(nut) >= 0) inited_stream_count++; } /* info headers */ pos=0; for(;;){ uint64_t startcode= find_any_startcode(bc, pos); pos= url_ftell(bc); if(startcode==0){ av_log(s, AV_LOG_ERROR, "EOF before video frames\n"); return -1; }else if(startcode == KEYFRAME_STARTCODE){ nut->next_startcode= startcode; break; }else if(startcode != INFO_STARTCODE){ continue; } decode_info_header(nut); } return 0; } static int decode_frame_header(NUTContext *nut, int *key_frame_ret, int64_t *pts_ret, int *stream_id_ret, int frame_code, int frame_type, int64_t frame_start){ AVFormatContext *s= nut->avf; StreamContext *stream; ByteIOContext *bc = &s->pb; int size, flags, size_mul, size_lsb, stream_id, time_delta; int64_t pts = 0; if(frame_type < 2 && frame_start - nut->packet_start[2] > nut->max_distance){ av_log(s, AV_LOG_ERROR, "last frame must have been damaged\n"); return -1; } if(frame_type) nut->packet_start[ frame_type ]= frame_start; //otherwise 1 goto 1 may happen flags= nut->frame_code[frame_code].flags; size_mul= nut->frame_code[frame_code].size_mul; size_lsb= nut->frame_code[frame_code].size_lsb; stream_id= nut->frame_code[frame_code].stream_id_plus1 - 1; time_delta= nut->frame_code[frame_code].timestamp_delta; if(stream_id==-1) stream_id= get_v(bc); if(stream_id >= s->nb_streams){ av_log(s, AV_LOG_ERROR, "illegal stream_id\n"); return -1; } stream= &nut->stream[stream_id]; // av_log(s, AV_LOG_DEBUG, "ft:%d ppts:%d %d %d\n", frame_type, stream->lru_pts_delta[0], stream->lru_pts_delta[1], stream->lru_pts_delta[2]); *key_frame_ret= !!(flags & FLAG_KEY_FRAME); if(!time_delta){ int64_t mask = (1<<stream->msb_timestamp_shift)-1; pts= get_v(bc); if(pts > mask){ pts -= mask+1; }else{ if(stream->last_pts == AV_NOPTS_VALUE){ av_log(s, AV_LOG_ERROR, "no reference pts available\n"); return -1; } pts= lsb2full(stream, pts); } }else{ if(stream->last_pts == AV_NOPTS_VALUE){ av_log(s, AV_LOG_ERROR, "no reference pts available\n"); return -1; } pts= stream->last_pts + time_delta; } if(*key_frame_ret){ // av_log(s, AV_LOG_DEBUG, "stream:%d start:%"PRId64" pts:%"PRId64" length:%"PRId64"\n",stream_id, frame_start, av_pts, frame_start - nut->stream[stream_id].last_sync_pos); av_add_index_entry( s->streams[stream_id], frame_start, pts, 0, frame_start - nut->stream[stream_id].last_sync_pos, AVINDEX_KEYFRAME); nut->stream[stream_id].last_sync_pos= frame_start; // assert(nut->packet_start == frame_start); } assert(size_mul > size_lsb); size= size_lsb; if(flags & FLAG_DATA_SIZE) size+= size_mul*get_v(bc); #ifdef TRACE av_log(s, AV_LOG_DEBUG, "fs:%"PRId64" fc:%d ft:%d kf:%d pts:%"PRId64" size:%d mul:%d lsb:%d flags:%d delta:%d\n", frame_start, frame_code, frame_type, *key_frame_ret, pts, size, size_mul, size_lsb, flags, time_delta); #endif if(frame_type==0 && url_ftell(bc) - nut->packet_start[2] + size > nut->max_distance){ av_log(s, AV_LOG_ERROR, "frame size too large\n"); return -1; } *stream_id_ret = stream_id; *pts_ret = pts; update(nut, stream_id, frame_start, frame_type, frame_code, *key_frame_ret, size, pts); return size; } static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code, int frame_type, int64_t frame_start){ AVFormatContext *s= nut->avf; ByteIOContext *bc = &s->pb; int size, stream_id, key_frame, discard; int64_t pts, last_IP_pts; size= decode_frame_header(nut, &key_frame, &pts, &stream_id, frame_code, frame_type, frame_start); if(size < 0) return -1; discard= s->streams[ stream_id ]->discard; last_IP_pts= s->streams[ stream_id ]->last_IP_pts; if( (discard >= AVDISCARD_NONKEY && !key_frame) ||(discard >= AVDISCARD_BIDIR && last_IP_pts != AV_NOPTS_VALUE && last_IP_pts > pts) || discard >= AVDISCARD_ALL){ url_fskip(bc, size); return 1; } av_get_packet(bc, pkt, size); pkt->stream_index = stream_id; if (key_frame) pkt->flags |= PKT_FLAG_KEY; pkt->pts = pts; return 0; } static int nut_read_packet(AVFormatContext *s, AVPacket *pkt) { NUTContext *nut = s->priv_data; ByteIOContext *bc = &s->pb; int i, frame_code=0, ret; for(;;){ int64_t pos= url_ftell(bc); int frame_type= 0; uint64_t tmp= nut->next_startcode; nut->next_startcode=0; if (url_feof(bc)) return -1; if(tmp){ pos-=8; }else{ frame_code = get_byte(bc); if(frame_code == 'N'){ tmp= frame_code; for(i=1; i<8; i++) tmp = (tmp<<8) + get_byte(bc); } } switch(tmp){ case MAIN_STARTCODE: case STREAM_STARTCODE: case INDEX_STARTCODE: get_packetheader(nut, bc, 0); assert(nut->packet_start[2] == pos); url_fseek(bc, nut->written_packet_size, SEEK_CUR); break; case INFO_STARTCODE: if(decode_info_header(nut)<0) goto resync; break; case KEYFRAME_STARTCODE: frame_type = 2; reset(s, get_v(bc)); frame_code = get_byte(bc); case 0: ret= decode_frame(nut, pkt, frame_code, frame_type, pos); if(ret==0) return 0; else if(ret==1) //ok but discard packet break; default: resync: av_log(s, AV_LOG_DEBUG, "syncing from %"PRId64"\n", nut->packet_start[2]+1); tmp= find_any_startcode(bc, nut->packet_start[2]+1); if(tmp==0) return -1; av_log(s, AV_LOG_DEBUG, "sync\n"); nut->next_startcode= tmp; } } } static int64_t nut_read_timestamp(AVFormatContext *s, int stream_index, int64_t *pos_arg, int64_t pos_limit){ NUTContext *nut = s->priv_data; StreamContext *stream; ByteIOContext *bc = &s->pb; int64_t pos, pts; uint64_t code; int frame_code,step, stream_id, i,size, key_frame; av_log(s, AV_LOG_DEBUG, "read_timestamp(X,%d,%"PRId64",%"PRId64")\n", stream_index, *pos_arg, pos_limit); if(*pos_arg < 0) return AV_NOPTS_VALUE; pos= *pos_arg; step= FFMIN(16*1024, pos); do{ pos-= step; code= find_any_startcode(bc, pos); if(code && url_ftell(bc) - 8 <= *pos_arg) break; step= FFMIN(2*step, pos); }while(step); if(!code) //nothing found, not even after pos_arg return AV_NOPTS_VALUE; url_fseek(bc, -8, SEEK_CUR); for(i=0; i<s->nb_streams; i++) nut->stream[i].last_sync_pos= url_ftell(bc); for(;;){ int frame_type=0; int64_t pos= url_ftell(bc); uint64_t tmp=0; if(pos > pos_limit || url_feof(bc)) return AV_NOPTS_VALUE; frame_code = get_byte(bc); if(frame_code == 'N'){ tmp= frame_code; for(i=1; i<8; i++) tmp = (tmp<<8) + get_byte(bc); } //av_log(s, AV_LOG_DEBUG, "before switch %"PRIX64" at=%"PRId64"\n", tmp, pos); switch(tmp){ case MAIN_STARTCODE: case STREAM_STARTCODE: case INDEX_STARTCODE: case INFO_STARTCODE: get_packetheader(nut, bc, 0); assert(nut->packet_start[2]==pos); url_fseek(bc, nut->written_packet_size, SEEK_CUR); break; case KEYFRAME_STARTCODE: frame_type=2; reset(s, get_v(bc)); frame_code = get_byte(bc); case 0: size= decode_frame_header(nut, &key_frame, &pts, &stream_id, frame_code, frame_type, pos); if(size < 0) goto resync; stream= &nut->stream[stream_id]; if(stream_id != stream_index || !key_frame || pos < *pos_arg){ url_fseek(bc, size, SEEK_CUR); break; } *pos_arg= pos; return pts; default: resync: av_log(s, AV_LOG_DEBUG, "syncing from %"PRId64"\n", nut->packet_start[2]+1); if(!find_any_startcode(bc, nut->packet_start[2]+1)) return AV_NOPTS_VALUE; url_fseek(bc, -8, SEEK_CUR); } } return AV_NOPTS_VALUE; } static int nut_read_seek(AVFormatContext *s, int stream_index, int64_t target_ts, int flags){ // NUTContext *nut = s->priv_data; int64_t pos; if(av_seek_frame_binary(s, stream_index, target_ts, flags) < 0) return -1; pos= url_ftell(&s->pb); nut_read_timestamp(s, stream_index, &pos, pos-1); return 0; } static int nut_read_close(AVFormatContext *s) { NUTContext *nut = s->priv_data; av_freep(&nut->stream); return 0; } #ifdef CONFIG_NUT_DEMUXER AVInputFormat nut_demuxer = { "nut", "nut format", sizeof(NUTContext), nut_probe, nut_read_header, nut_read_packet, nut_read_close, nut_read_seek, nut_read_timestamp, .extensions = "nut", }; #endif #ifdef CONFIG_NUT_MUXER 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, nut_write_header, nut_write_packet, nut_write_trailer, .flags = AVFMT_GLOBALHEADER, }; #endif