movenc.c

来自「ffmpeg的完整源代码和作者自己写的文档。不但有在Linux的工程哦」· C语言 代码 · 共 1,708 行 · 第 1/4 页

C
1,708
字号
        put_be16(pb, 0x01);                 /* ? */
        ascii_to_wc (pb, s->title);
        size += len*2+10;

        // Date
//        snprintf(dt,32,"%04d/%02d/%02d %02d:%02d:%02d",t_st->tm_year+1900,t_st->tm_mon+1,t_st->tm_mday,t_st->tm_hour,t_st->tm_min,t_st->tm_sec);
        len = utf8len("2006/04/01 11:11:11")+1;
        if(len<=0)
            goto not_utf8;
        put_be16(pb, len*2+10);    /* size */
        put_be32(pb, 0x03);        /* type */
        put_be16(pb, language_code("und")); /* language */
        put_be16(pb, 0x01);        /* ? */
        ascii_to_wc (pb, "2006/04/01 11:11:11");
        size += len*2+10;

        // size
        curpos = url_ftell(pb);
        url_fseek(pb, pos, SEEK_SET);
        put_be32(pb, size);
        url_fseek(pb, pos+24, SEEK_SET);
        put_be32(pb, size-24);
        url_fseek(pb, curpos, SEEK_SET);
    }

    return size;
not_utf8:
    av_log(s, AV_LOG_ERROR, "not utf8\n");
    return -1;
}

static int mov_write_moov_tag(ByteIOContext *pb, MOVContext *mov,
                              AVFormatContext *s)
{
    int i;
    offset_t pos = url_ftell(pb);
    put_be32(pb, 0); /* size placeholder*/
    put_tag(pb, "moov");
    mov->timescale = globalTimescale;

    for (i=0; i<mov->nb_streams; i++) {
        if(mov->tracks[i].entry <= 0) continue;

        mov->tracks[i].time = mov->time;
        mov->tracks[i].trackID = i+1;
    }

    mov_write_mvhd_tag(pb, mov);
    //mov_write_iods_tag(pb, mov);
    for (i=0; i<mov->nb_streams; i++) {
        if(mov->tracks[i].entry > 0) {
            mov_write_trak_tag(pb, &(mov->tracks[i]));
        }
    }

    if (mov->mode == MODE_PSP)
        mov_write_uuidusmt_tag(pb, s);
    else
        mov_write_udta_tag(pb, mov, s);

    return updateSize(pb, pos);
}

static int mov_write_mdat_tag(ByteIOContext *pb, MOVContext* mov)
{
    put_be32(pb, 8);    // placeholder for extended size field (64 bit)
    put_tag(pb, mov->mode == MODE_MOV ? "wide" : "free");

    mov->mdat_pos = url_ftell(pb);
    put_be32(pb, 0); /* size placeholder*/
    put_tag(pb, "mdat");
    return 0;
}

/* TODO: This needs to be more general */
static void mov_write_ftyp_tag (ByteIOContext *pb, AVFormatContext *s)
{
    MOVContext *mov = s->priv_data;

    put_be32(pb, 0x14 ); /* size */
    put_tag(pb, "ftyp");

    if ( mov->mode == MODE_3GP )
        put_tag(pb, "3gp4");
    else if ( mov->mode == MODE_3G2 )
        put_tag(pb, "3g2a");
    else if ( mov->mode == MODE_PSP )
        put_tag(pb, "MSNV");
    else if ( mov->mode == MODE_MP4 )
        put_tag(pb, "isom");
    else
        put_tag(pb, "qt  ");

    put_be32(pb, 0x200 );

    if ( mov->mode == MODE_3GP )
        put_tag(pb, "3gp4");
    else if ( mov->mode == MODE_3G2 )
        put_tag(pb, "3g2a");
    else if ( mov->mode == MODE_PSP )
        put_tag(pb, "MSNV");
    else if ( mov->mode == MODE_MP4 )
        put_tag(pb, "mp41");
    else
        put_tag(pb, "qt  ");
}

static void mov_write_uuidprof_tag(ByteIOContext *pb, AVFormatContext *s)
{
    AVCodecContext *VideoCodec = s->streams[0]->codec;
    AVCodecContext *AudioCodec = s->streams[1]->codec;
    int AudioRate = AudioCodec->sample_rate;
    int FrameRate = ((VideoCodec->time_base.den) * (0x10000))/ (VideoCodec->time_base.num);
    int audio_kbitrate= AudioCodec->bit_rate / 1000;
    int video_kbitrate= FFMIN(VideoCodec->bit_rate / 1000, 800 - audio_kbitrate);

    put_be32(pb, 0x94 ); /* size */
    put_tag(pb, "uuid");
    put_tag(pb, "PROF");

    put_be32(pb, 0x21d24fce ); /* 96 bit UUID */
    put_be32(pb, 0xbb88695c );
    put_be32(pb, 0xfac9c740 );

    put_be32(pb, 0x0 );  /* ? */
    put_be32(pb, 0x3 );  /* 3 sections ? */

    put_be32(pb, 0x14 ); /* size */
    put_tag(pb, "FPRF");
    put_be32(pb, 0x0 );  /* ? */
    put_be32(pb, 0x0 );  /* ? */
    put_be32(pb, 0x0 );  /* ? */

    put_be32(pb, 0x2c );  /* size */
    put_tag(pb, "APRF");   /* audio */
    put_be32(pb, 0x0 );
    put_be32(pb, 0x2 );   /* TrackID */
    put_tag(pb, "mp4a");
    put_be32(pb, 0x20f );
    put_be32(pb, 0x0 );
    put_be32(pb, audio_kbitrate);
    put_be32(pb, audio_kbitrate);
    put_be32(pb, AudioRate );
    put_be32(pb, AudioCodec->channels );

    put_be32(pb, 0x34 );  /* size */
    put_tag(pb, "VPRF");   /* video */
    put_be32(pb, 0x0 );
    put_be32(pb, 0x1 );    /* TrackID */
    if (VideoCodec->codec_id == CODEC_ID_H264) {
        put_tag(pb, "avc1");
        put_be16(pb, 0x014D );
        put_be16(pb, 0x0015 );
    } else {
        put_tag(pb, "mp4v");
        put_be16(pb, 0x0000 );
        put_be16(pb, 0x0103 );
    }
    put_be32(pb, 0x0 );
    put_be32(pb, video_kbitrate);
    put_be32(pb, video_kbitrate);
    put_be32(pb, FrameRate);
    put_be32(pb, FrameRate);
    put_be16(pb, VideoCodec->width);
    put_be16(pb, VideoCodec->height);
    put_be32(pb, 0x010001); /* ? */
}

static int mov_write_header(AVFormatContext *s)
{
    ByteIOContext *pb = &s->pb;
    MOVContext *mov = s->priv_data;
    int i;

    if (url_is_streamed(&s->pb)) {
        av_log(s, AV_LOG_ERROR, "muxer does not support non seekable output\n");
        return -1;
    }

    /* Default mode == MP4 */
    mov->mode = MODE_MP4;

    if (s->oformat != NULL) {
        if (!strcmp("3gp", s->oformat->name)) mov->mode = MODE_3GP;
        else if (!strcmp("3g2", s->oformat->name)) mov->mode = MODE_3G2;
        else if (!strcmp("mov", s->oformat->name)) mov->mode = MODE_MOV;
        else if (!strcmp("psp", s->oformat->name)) mov->mode = MODE_PSP;

        mov_write_ftyp_tag(pb,s);
        if ( mov->mode == MODE_PSP ) {
            if ( s->nb_streams != 2 ) {
                av_log(s, AV_LOG_ERROR, "PSP mode need one video and one audio stream\n");
                return -1;
            }
            mov_write_uuidprof_tag(pb,s);
        }
    }

    for(i=0; i<s->nb_streams; i++){
        AVStream *st= s->streams[i];
        MOVTrack *track= &mov->tracks[i];

        track->enc = st->codec;
        track->language = ff_mov_iso639_to_lang(st->language, mov->mode != MODE_MOV);
        track->mode = mov->mode;
        if(st->codec->codec_type == CODEC_TYPE_VIDEO){
            track->tag = mov_find_video_codec_tag(s, track);
            track->timescale = st->codec->time_base.den;
            av_set_pts_info(st, 64, 1, st->codec->time_base.den);
            if (track->timescale > 100000)
                av_log(NULL, AV_LOG_WARNING,
                       "WARNING codec timebase is very high. If duration is too long,\n"
                       "file may not be playable by quicktime. Specify a shorter timebase\n"
                       "or choose different container.\n");
        }else if(st->codec->codec_type == CODEC_TYPE_AUDIO){
            track->tag = mov_find_audio_codec_tag(s, track);
            track->timescale = st->codec->sample_rate;
            av_set_pts_info(st, 64, 1, st->codec->sample_rate);
            if(!st->codec->frame_size){
                av_log(s, AV_LOG_ERROR, "track %d: codec frame size is not set\n", i);
                return -1;
            }else if(st->codec->frame_size > 1){ /* assume compressed audio */
                track->audio_vbr = 1;
            }else{
                track->sampleSize = (av_get_bits_per_sample(st->codec->codec_id) >> 3) * st->codec->channels;
            }
        }
    }

    mov_write_mdat_tag(pb, mov);
    mov->time = s->timestamp + 0x7C25B080; //1970 based -> 1904 based
    mov->nb_streams = s->nb_streams;

    put_flush_packet(pb);

    return 0;
}

static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
{
    MOVContext *mov = s->priv_data;
    ByteIOContext *pb = &s->pb;
    MOVTrack *trk = &mov->tracks[pkt->stream_index];
    AVCodecContext *enc = trk->enc;
    unsigned int samplesInChunk = 0;
    int size= pkt->size;

    if (url_is_streamed(&s->pb)) return 0; /* Can't handle that */
    if (!size) return 0; /* Discard 0 sized packets */

    if (enc->codec_id == CODEC_ID_AMR_NB) {
        /* We must find out how many AMR blocks there are in one packet */
        static uint16_t packed_size[16] =
            {13, 14, 16, 18, 20, 21, 27, 32, 6, 0, 0, 0, 0, 0, 0, 0};
        int len = 0;

        while (len < size && samplesInChunk < 100) {
            len += packed_size[(pkt->data[len] >> 3) & 0x0F];
            samplesInChunk++;
        }
        if(samplesInChunk > 1){
            av_log(s, AV_LOG_ERROR, "fatal error, input is not a single packet, implement a AVParser for it\n");
            return -1;
        }
    } else if (trk->sampleSize)
        samplesInChunk = size/trk->sampleSize;
    else
        samplesInChunk = 1;

    /* copy extradata if it exists */
    if (trk->vosLen == 0 && enc->extradata_size > 0) {
        trk->vosLen = enc->extradata_size;
        trk->vosData = av_malloc(trk->vosLen);
        memcpy(trk->vosData, enc->extradata, trk->vosLen);
    }

    if (enc->codec_id == CODEC_ID_H264 && trk->vosLen > 0 && *(uint8_t *)trk->vosData != 1) {
        /* from x264 or from bytestream h264 */
        /* nal reformating needed */
        avc_parse_nal_units(&pkt->data, &pkt->size);
        assert(pkt->size);
        size = pkt->size;
    }

    if (!(trk->entry % MOV_INDEX_CLUSTER_SIZE)) {
        trk->cluster = av_realloc(trk->cluster, (trk->entry + MOV_INDEX_CLUSTER_SIZE) * sizeof(*trk->cluster));
        if (!trk->cluster)
            return -1;
    }

    trk->cluster[trk->entry].pos = url_ftell(pb);
    trk->cluster[trk->entry].samplesInChunk = samplesInChunk;
    trk->cluster[trk->entry].size = size;
    trk->cluster[trk->entry].entries = samplesInChunk;
    trk->cluster[trk->entry].dts = pkt->dts;
    trk->trackDuration = pkt->dts - trk->cluster[0].dts + pkt->duration;

    if(enc->codec_type == CODEC_TYPE_VIDEO) {
        if (pkt->dts != pkt->pts)
            trk->hasBframes = 1;
        trk->cluster[trk->entry].cts = pkt->pts - pkt->dts;
        trk->cluster[trk->entry].key_frame = !!(pkt->flags & PKT_FLAG_KEY);
        if(trk->cluster[trk->entry].key_frame)
            trk->hasKeyframes++;
    }
    trk->entry++;
    trk->sampleCount += samplesInChunk;
    mov->mdat_size += size;

    put_buffer(pb, pkt->data, size);

    put_flush_packet(pb);
    return 0;
}

static int mov_write_trailer(AVFormatContext *s)
{
    MOVContext *mov = s->priv_data;
    ByteIOContext *pb = &s->pb;
    int res = 0;
    int i;

    offset_t moov_pos = url_ftell(pb);

    /* Write size of mdat tag */
    if (mov->mdat_size+8 <= UINT32_MAX) {
        url_fseek(pb, mov->mdat_pos, SEEK_SET);
        put_be32(pb, mov->mdat_size+8);
    } else {
        /* overwrite 'wide' placeholder atom */
        url_fseek(pb, mov->mdat_pos - 8, SEEK_SET);
        put_be32(pb, 1); /* special value: real atom size will be 64 bit value after tag field */
        put_tag(pb, "mdat");
        put_be64(pb, mov->mdat_size+16);
    }
    url_fseek(pb, moov_pos, SEEK_SET);

    mov_write_moov_tag(pb, mov, s);

    for (i=0; i<mov->nb_streams; i++) {
        av_freep(&mov->tracks[i].cluster);

        if( mov->tracks[i].vosLen ) av_free( mov->tracks[i].vosData );

    }

    put_flush_packet(pb);

    return res;
}

#ifdef CONFIG_MOV_MUXER
AVOutputFormat mov_muxer = {
    "mov",
    "mov format",
    NULL,
    "mov",
    sizeof(MOVContext),
    CODEC_ID_AAC,
    CODEC_ID_MPEG4,
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
    .flags = AVFMT_GLOBALHEADER,
};
#endif
#ifdef CONFIG_TGP_MUXER
AVOutputFormat tgp_muxer = {
    "3gp",
    "3gp format",
    NULL,
    "3gp",
    sizeof(MOVContext),
    CODEC_ID_AMR_NB,
    CODEC_ID_H263,
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
    .flags = AVFMT_GLOBALHEADER,
};
#endif
#ifdef CONFIG_MP4_MUXER
AVOutputFormat mp4_muxer = {
    "mp4",
    "mp4 format",
    "application/mp4",
    "mp4,m4a",
    sizeof(MOVContext),
    CODEC_ID_AAC,
    CODEC_ID_MPEG4,
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
    .flags = AVFMT_GLOBALHEADER,
};
#endif
#ifdef CONFIG_PSP_MUXER
AVOutputFormat psp_muxer = {
    "psp",
    "psp mp4 format",
    NULL,
    "mp4,psp",
    sizeof(MOVContext),
    CODEC_ID_AAC,
    CODEC_ID_MPEG4,
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
    .flags = AVFMT_GLOBALHEADER,
};
#endif
#ifdef CONFIG_TG2_MUXER
AVOutputFormat tg2_muxer = {
    "3g2",
    "3gp2 format",
    NULL,
    "3g2",
    sizeof(MOVContext),
    CODEC_ID_AMR_NB,
    CODEC_ID_H263,
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
    .flags = AVFMT_GLOBALHEADER,
};
#endif

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?