📄 nut.c
字号:
/* * "NUT" Container Format muxer and demuxer (DRAFT-200403??) * Copyright (c) 2003 Alex Beregszaszi * Copyright (c) 2004 Michael Niedermayer * * This library 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 of the License, or (at your option) any later version. * * This library 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 General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * NUT DRAFT can be found in MPlayer CVS at DOCS/tech/mpcf.txt * * AND http://people.fsn.hu/~alex/nut/ (TeX, pdf, ps, dvi, ..) * *//* * TODO: * - index writing * - index packet reading support*///#define DEBUG 1#include <limits.h>#include "avformat.h"#include "mpegaudio.h"#include "avi.h"#undef NDEBUG#include <assert.h>//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 MAX_TYPE1_DISTANCE (1024*16-1)#define MAX_TYPE2_DISTANCE (1024*64-1)#define MAX_SIZE_LRU 2#define MAX_PTS_LRU 3#define FLAG_FRAME_TYPE 1#define FLAG_DATA_SIZE 2#define FLAG_PTS 16#define FLAG_FULL_PTS 4#define FLAG_KEY_FRAME 32typedef struct { uint8_t flags; uint8_t stream_id_plus1; uint8_t size_mul; uint8_t size_lsb;} FrameCode;typedef struct { int last_key_frame; int msb_timestamp_shift; int rate_num; int rate_den; int64_t last_pts; int64_t last_full_pts; int lru_pts_delta[MAX_PTS_LRU]; int lru_size[MAX_SIZE_LRU]; int initial_pts_predictor[MAX_PTS_LRU]; int initial_size_predictor[MAX_SIZE_LRU]; int64_t last_sync_pos; ///<pos of last 1/2 type frame} StreamContext;typedef struct { AVFormatContext *avf; int64_t packet_start; int64_t last_packet_start; int written_packet_size; int64_t packet_size_pos; int64_t last_frame_start[3]; FrameCode frame_code[256]; int stream_count; uint64_t next_startcode; ///< stores the next startcode if it has alraedy been parsed but the stream isnt seekable StreamContext *stream;} 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_lru(int *lru, int current, int count){ int i; for(i=0; i<count-1; i++){ if(lru[i] == current) break; } for(; i; i--){ lru[i]= lru[i-1]; } lru[0]= current;}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]; const int flags=nut->frame_code[frame_code].flags; stream->last_key_frame= key_frame; nut->last_frame_start[ frame_type ]= frame_start; if(frame_type == 0) update_lru(stream->lru_pts_delta, pts - stream->last_pts, 3); update_lru(stream->lru_size, size, 2); stream->last_pts= pts; if((flags & FLAG_PTS) && (flags & FLAG_FULL_PTS)) stream->last_full_pts= pts;}static void reset(AVFormatContext *s/*, int frame_type*/){ 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; memcpy(stream->lru_pts_delta, stream->initial_pts_predictor, sizeof(int)*MAX_PTS_LRU); memcpy(stream->lru_size, stream->initial_size_predictor, sizeof(int)*MAX_SIZE_LRU); }}static void build_frame_code(AVFormatContext *s){ NUTContext *nut = s->priv_data; int key_frame, frame_type, full_pts, index, pred, stream_id; int start=0; int end= 255; int keyframe_0_esc= s->nb_streams > 2; if(keyframe_0_esc){ /* keyframe = 0 escapes, 3 codes */ for(frame_type=0; frame_type<2; frame_type++){ for(full_pts=frame_type; full_pts<2; full_pts++){ FrameCode *ft= &nut->frame_code[start]; ft->flags= FLAG_FRAME_TYPE*frame_type + FLAG_FULL_PTS*full_pts; ft->flags|= FLAG_DATA_SIZE | FLAG_PTS; ft->stream_id_plus1= 0; ft->size_mul=1; 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; for(frame_type=0; frame_type<2; frame_type++){ for(full_pts=frame_type; full_pts<2; full_pts++){ FrameCode *ft= &nut->frame_code[start2]; ft->flags= FLAG_FRAME_TYPE*frame_type + FLAG_FULL_PTS*full_pts + FLAG_KEY_FRAME*key_frame; ft->flags|= FLAG_DATA_SIZE | FLAG_PTS; ft->stream_id_plus1= stream_id + 1; 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); for(pred=0; pred<MAX_SIZE_LRU; pred++){ for(frame_type=0; frame_type<1; frame_type++){ FrameCode *ft= &nut->frame_code[start2]; ft->flags= FLAG_KEY_FRAME*key_frame + (FLAG_FULL_PTS+FLAG_PTS+FLAG_FRAME_TYPE)*frame_type; ft->stream_id_plus1= stream_id + 1; ft->size_mul=1; ft->size_lsb=1 + pred; start2++; } nut->stream[stream_id].initial_size_predictor[pred]= frame_bytes + pred; } }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; start2++; }#endif pred_count= 2 + codec->has_b_frames + (codec->codec_id == CODEC_ID_VORBIS); 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 + pred*4; 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; } nut->stream[stream_id].initial_pts_predictor[pred]= pred+1; } } memmove(&nut->frame_code['N'+1], &nut->frame_code['N'], sizeof(FrameCode)*(255-'N')); nut->frame_code['N'].flags= 1;}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 return (val<<7) + tmp; } return -1;}static int get_str(ByteIOContext *bc, char *string, int maxlen){ 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 uint64_t get_vb(ByteIOContext *bc){ uint64_t val=0; int i= get_v(bc); if(i>8) return UINT64_MAX; while(i--) val = (val<<8) + get_byte(bc); return val;}static int get_packetheader(NUTContext *nut, ByteIOContext *bc, int prefix_length, int calculate_checksum){ int64_t start, size, last_size; start= url_ftell(bc) - prefix_length; if(nut->written_packet_size >= 0 && start != nut->packet_start + nut->written_packet_size){ av_log(nut->avf, AV_LOG_ERROR, "get_packetheader called at weird position\n"); if(prefix_length<8) return -1; } init_checksum(bc, calculate_checksum ? update_adler32 : NULL, 0); size= get_v(bc); last_size= get_v(bc); if(nut->written_packet_size >= 0 && nut->written_packet_size != last_size){ av_log(nut->avf, AV_LOG_ERROR, "packet size missmatch %d != %lld at %lld\n", nut->written_packet_size, last_size, start); if(prefix_length<8) return -1; } nut->last_packet_start = nut->packet_start; nut->packet_start = 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; }}#ifdef CONFIG_ENCODERSstatic void put_v(ByteIOContext *bc, uint64_t val){ int i; 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_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);}static int put_packetheader(NUTContext *nut, ByteIOContext *bc, int max_size, int calculate_checksum){ put_flush_packet(bc); nut->last_packet_start= nut->packet_start; nut->packet_start+= nut->written_packet_size; nut->packet_size_pos = url_ftell(bc); nut->written_packet_size = max_size; if(calculate_checksum) init_checksum(bc, update_adler32, 0); /* packet header */ put_v(bc, nut->written_packet_size); /* forward ptr */ put_v(bc, nut->packet_start - nut->last_packet_start); /* backward ptr */ return 0;}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 + additional_size; if(calculate_checksum) size += 4; if(size != nut->written_packet_size){ int i; assert( size <= nut->written_packet_size ); url_fseek(bc, nut->packet_size_pos, 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; nut->avf= s; nut->stream = av_mallocz(sizeof(StreamContext)*s->nb_streams); av_set_pts_info(s, 60, 1, AV_TIME_BASE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -