view nut.c @ 536:76c47c58064f libavformat

move packet interleaving function into AVOutputFormat, so it can be overriden easily instead of doing reordering twice if the muxer needs some other interleaving then dts based
author michael
date Wed, 29 Sep 2004 23:25:01 +0000
parents ac85aafd0971
children 0a997108f384
line wrap: on
line source

/*
 * "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>

//#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];
    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()= %lld\n", (val<<7) + tmp);
	    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 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;
    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()= %lld\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 %5lld / %llX 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 %5lld / %llX 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 %5lld / %llX 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;

    init_checksum(bc, calculate_checksum ? update_adler32 : NULL, 0);

    size= get_v(bc);

    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;
    }
}

#ifdef CONFIG_ENCODERS

static void put_v(ByteIOContext *bc, uint64_t val)
{
    int i;

//av_log(NULL, AV_LOG_DEBUG, "put_v()= %lld\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, uint64_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 %5lld / %llX 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 %5lld / %llX 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 %5lld / %llX 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]+= nut->written_packet_size;
    assert(url_ftell(bc) - 8 == nut->packet_start[2]);
    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 */

    return 0;
}

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 + additional_size;
    
    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;

    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, gcd;

	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*/);
	put_v(bc, (codec->codec_type == CODEC_TYPE_AUDIO) ? 32 : 0);
	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);

	if (codec->codec_type == CODEC_TYPE_VIDEO)
	{
	    nom = codec->frame_rate;
	    denom = codec->frame_rate_base;
	}
	else
	{
	    nom = codec->sample_rate;
            if(codec->frame_size>0)
                denom= codec->frame_size;
            else
                denom= 1; //unlucky
	}
        gcd= ff_gcd(nom, denom);
        nom   /= gcd;
        denom /= gcd;
        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 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;
}

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:%lld fs:%lld\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_ENCODERS

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 (%Ld)\n", tmp);
        return -1;
    }
    
    nut->stream_count = get_v(bc);
    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 missmatch\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 32:
            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;
        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);
    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);
        st->codec.extradata= av_mallocz(st->codec.extradata_size);
        get_buffer(bc, st->codec.extradata, st->codec.extradata_size);            
//	    url_fskip(bc, get_v(bc));
    }
    
    if (class == 0) /* 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 */

        st->codec.frame_rate = nom;
        st->codec.frame_rate_base = denom;
    }
    if (class == 32) /* 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 missmatch\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 %d\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")){
            int value= 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 missmatch\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){
            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:%lld pts:%lld length:%lld\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, 
            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:%lld fc:%d ft:%d kf:%d pts:%lld 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;
    int64_t pts;
    
    size= decode_frame_header(nut, &key_frame, &pts, &stream_id, frame_code, frame_type, frame_start);
    if(size < 0)
        return -1;

    av_new_packet(pkt, size);
    get_buffer(bc, pkt->data, 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;

    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 + nut->packet_start[2], SEEK_SET);
            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:
            if(decode_frame(nut, pkt, frame_code, frame_type, pos)>=0)
                return 0;
        default:
resync:
av_log(s, AV_LOG_DEBUG, "syncing from %lld\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,%lld,%lld)\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 %llX at=%lld\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 + pos, SEEK_SET);
            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 %lld\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){
//    NUTContext *nut = s->priv_data;
    int64_t pos;

    if(av_seek_frame_binary(s, stream_index, target_ts) < 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;
    int i;

    for(i=0;i<s->nb_streams;i++) {
        av_freep(&s->streams[i]->codec.extradata);
    }
    av_freep(&nut->stream);

    return 0;
}

static AVInputFormat nut_iformat = {
    "nut",
    "nut format",
    sizeof(NUTContext),
    nut_probe,
    nut_read_header,
    nut_read_packet,
    nut_read_close,
    nut_read_seek,
    nut_read_timestamp,
    .extensions = "nut",
};

#ifdef CONFIG_ENCODERS
static AVOutputFormat nut_oformat = {
    "nut",
    "nut format",
    "video/x-nut",
    "nut",
    sizeof(NUTContext),
#ifdef CONFIG_VORBIS
    CODEC_ID_VORBIS,
#elif defined(CONFIG_MP3LAME)
    CODEC_ID_MP3,
#else
    CODEC_ID_MP2, /* AC3 needs liba52 decoder */
#endif
    CODEC_ID_MPEG4,
    nut_write_header,
    nut_write_packet,
    nut_write_trailer,
};
#endif //CONFIG_ENCODERS

int nut_init(void)
{
    av_register_input_format(&nut_iformat);
#ifdef CONFIG_ENCODERS
    av_register_output_format(&nut_oformat);
#endif //CONFIG_ENCODERS
    return 0;
}