ffmpeg源码跟踪笔记之av_write_frame 与 av_interleaved_write_frame

来源:互联网 发布:域名注册收费 编辑:程序博客网 时间:2024/05/06 18:37

1、函数介绍

av_interleaved_write_frame函数介绍:

/** * Write a packet to an output media file ensuring correct interleaving. * * This function will buffer the packets internally as needed to make sure the * packets in the output file are properly interleaved in the order of * increasing dts. Callers doing their own interleaving should call * av_write_frame() instead of this function. * * @param s media file handle * @param pkt The packet containing the data to be written. *            <br> *            If the packet is reference-counted, this function will take *            ownership of this reference and unreference it later when it sees *            fit. *            The caller must not access the data through this reference after *            this function returns. If the packet is not reference-counted, *            libavformat will make a copy. *            <br> *            This parameter can be NULL (at any time, not just at the end), to *            flush the interleaving queues. *            <br> *            Packet's @ref AVPacket.stream_index "stream_index" field must be *            set to the index of the corresponding stream in @ref *            AVFormatContext.streams "s->streams". It is very strongly *            recommended that timing information (@ref AVPacket.pts "pts", @ref *            AVPacket.dts "dts", @ref AVPacket.duration "duration") is set to *            correct values. * * @return 0 on success, a negative AVERROR on error. Libavformat will always *         take care of freeing the packet, even if this function fails. * * @see av_write_frame(), AVFormatContext.max_interleave_delta */int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);

av_write_frame函数介绍:

/** * Write a packet to an output media file. * * This function passes the packet directly to the muxer, without any buffering * or reordering. The caller is responsible for correctly interleaving the * packets if the format requires it. Callers that want libavformat to handle * the interleaving should call av_interleaved_write_frame() instead of this * function. * * @param s media file handle * @param pkt The packet containing the data to be written. Note that unlike *            av_interleaved_write_frame(), this function does not take *            ownership of the packet passed to it (though some muxers may make *            an internal reference to the input packet). *            <br> *            This parameter can be NULL (at any time, not just at the end), in *            order to immediately flush data buffered within the muxer, for *            muxers that buffer up data internally before writing it to the *            output. *            <br> *            Packet's @ref AVPacket.stream_index "stream_index" field must be *            set to the index of the corresponding stream in @ref *            AVFormatContext.streams "s->streams". It is very strongly *            recommended that timing information (@ref AVPacket.pts "pts", @ref *            AVPacket.dts "dts", @ref AVPacket.duration "duration") is set to *            correct values. * @return < 0 on error, = 0 if OK, 1 if flushed and there is no more data to flush * * @see av_interleaved_write_frame() */int av_write_frame(AVFormatContext *s, AVPacket *pkt);

2、函数调用图

av_interleaved_write_frame函数调用图:


av_write_frame函数调用图:



3、函数代码

av_interleaved_write_frame函数代码:

int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt){    int ret, flush = 0;    //检查pkt是否合法    ret = check_packet(s, pkt);    if (ret < 0)        goto fail;    if (pkt) {        AVStream *st = s->streams[pkt->stream_index];        av_dlog(s, "av_interleaved_write_frame size:%d dts:%s pts:%s\n",                pkt->size, av_ts2str(pkt->dts), av_ts2str(pkt->pts));        if ((ret = compute_pkt_fields2(s, st, pkt)) < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))//计算和检查pts、dts、duration            goto fail;        if (pkt->dts == AV_NOPTS_VALUE && !(s->oformat->flags & AVFMT_NOTIMESTAMPS)) {            ret = AVERROR(EINVAL);            goto fail;        }    } else {        av_dlog(s, "av_interleaved_write_frame FLUSH\n");        flush = 1;    }    for (;; ) {        AVPacket opkt;        int ret = interleave_packet(s, &opkt, pkt, flush);        if (pkt) {            memset(pkt, 0, sizeof(*pkt));            av_init_packet(pkt);            pkt = NULL;        }        if (ret <= 0) //FIXME cleanup needed for ret<0 ?            return ret;        ret = write_packet(s, &opkt);        if (ret >= 0)            s->streams[opkt.stream_index]->nb_frames++;        av_free_packet(&opkt);        if (ret < 0)            return ret;        if(s->pb && s->pb->error)            return s->pb->error;    }fail:    av_packet_unref(pkt);    return ret;}

av_write_frame函数代码:

int av_write_frame(AVFormatContext *s, AVPacket *pkt){    int ret;    ret = check_packet(s, pkt);    if (ret < 0)        return ret;    if (!pkt) {        if (s->oformat->flags & AVFMT_ALLOW_FLUSH) {            ret = s->oformat->write_packet(s, NULL);            if (s->flush_packets && s->pb && s->pb->error >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS)                avio_flush(s->pb);            if (ret >= 0 && s->pb && s->pb->error < 0)                ret = s->pb->error;            return ret;        }        return 1;    }    ret = compute_pkt_fields2(s, s->streams[pkt->stream_index], pkt);    if (ret < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))        return ret;    ret = write_packet(s, pkt);    if (ret >= 0 && s->pb && s->pb->error < 0)        ret = s->pb->error;    if (ret >= 0)        s->streams[pkt->stream_index]->nb_frames++;    return ret;}

4、解释

可以看出2个函数的区别在于av_interleaved_write_frame调用了interleave_packet,而av_write_frame没有调用,看看interleave_packet的代码:
static int interleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *in, int flush){    if (s->oformat->interleave_packet) {        int ret = s->oformat->interleave_packet(s, out, in, flush);        if (in)            av_free_packet(in);        return ret;    } else        return ff_interleave_packet_per_dts(s, out, in, flush);}
代码非常简单,如果AVOutputFormat有interleave_packet函数指针,则调用,如果没有则调用ff_interleave_packet_per_dts,看看ff_interleave_packet_per_dts的代码:
int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out,                                 AVPacket *pkt, int flush){    AVPacketList *pktl;    int stream_count = 0;    int noninterleaved_count = 0;    int i, ret;    if (pkt) {//把pkt加入到AVPacketList中去        if ((ret = ff_interleave_add_packet(s, pkt, interleave_compare_dts)) < 0)            return ret;    }    for (i = 0; i < s->nb_streams; i++) {        if (s->streams[i]->last_in_packet_buffer) {            ++stream_count;        } else if (s->streams[i]->codec->codec_type != AVMEDIA_TYPE_ATTACHMENT &&                   s->streams[i]->codec->codec_id != AV_CODEC_ID_VP8 &&                   s->streams[i]->codec->codec_id != AV_CODEC_ID_VP9) {            ++noninterleaved_count;        }    }    if (s->internal->nb_interleaved_streams == stream_count)        flush = 1;    if (s->max_interleave_delta > 0 &&        s->internal->packet_buffer &&        !flush &&        s->internal->nb_interleaved_streams == stream_count+noninterleaved_count    ) {        AVPacket *top_pkt = &s->internal->packet_buffer->pkt;        int64_t delta_dts = INT64_MIN;        int64_t top_dts = av_rescale_q(top_pkt->dts,                                       s->streams[top_pkt->stream_index]->time_base,                                       AV_TIME_BASE_Q);        for (i = 0; i < s->nb_streams; i++) {            int64_t last_dts;            const AVPacketList *last = s->streams[i]->last_in_packet_buffer;            if (!last)                continue;            last_dts = av_rescale_q(last->pkt.dts,                                    s->streams[i]->time_base,                                    AV_TIME_BASE_Q);            delta_dts = FFMAX(delta_dts, last_dts - top_dts);        }        if (delta_dts > s->max_interleave_delta) {            av_log(s, AV_LOG_DEBUG,                   "Delay between the first packet and last packet in the "                   "muxing queue is %"PRId64" > %"PRId64": forcing output\n",                   delta_dts, s->max_interleave_delta);            flush = 1;        }    }    if (stream_count && flush) {        AVStream *st;        pktl = s->internal->packet_buffer;        *out = pktl->pkt;        st   = s->streams[out->stream_index];        s->internal->packet_buffer = pktl->next;        if (!s->internal->packet_buffer)            s->internal->packet_buffer_end = NULL;        if (st->last_in_packet_buffer == pktl)            st->last_in_packet_buffer = NULL;        av_freep(&pktl);        return 1;    } else {        av_init_packet(out);        return 0;    }}
从代码中看到,函数ff_interleave_packet_per_dts调用ff_interleave_add_packet把pkt加入缓存,然后再从缓存中取出第一个pkt返回。
看看ff_interleave_add_packet的代码:
int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt,                             int (*compare)(AVFormatContext *, AVPacket *, AVPacket *)){    int ret;    AVPacketList **next_point, *this_pktl;    AVStream *st   = s->streams[pkt->stream_index];    int chunked    = s->max_chunk_size || s->max_chunk_duration;    this_pktl      = av_mallocz(sizeof(AVPacketList));    if (!this_pktl)        return AVERROR(ENOMEM);    this_pktl->pkt = *pkt;#if FF_API_DESTRUCT_PACKETFF_DISABLE_DEPRECATION_WARNINGS    pkt->destruct  = NULL;           // do not free original but only the copyFF_ENABLE_DEPRECATION_WARNINGS#endif    pkt->buf       = NULL;    pkt->side_data = NULL;    pkt->side_data_elems = 0;    if ((pkt->flags & AV_PKT_FLAG_UNCODED_FRAME)) {        av_assert0(pkt->size == UNCODED_FRAME_PACKET_SIZE);        av_assert0(((AVFrame *)pkt->data)->buf);    } else {        // Duplicate the packet if it uses non-allocated memory        if ((ret = av_dup_packet(&this_pktl->pkt)) < 0) {            av_free(this_pktl);            return ret;        }    }    if (s->streams[pkt->stream_index]->last_in_packet_buffer) {        next_point = &(st->last_in_packet_buffer->next);    } else {        next_point = &s->internal->packet_buffer;    }    if (chunked) {        uint64_t max= av_rescale_q_rnd(s->max_chunk_duration, AV_TIME_BASE_Q, st->time_base, AV_ROUND_UP);        st->interleaver_chunk_size     += pkt->size;        st->interleaver_chunk_duration += pkt->duration;        if (   (s->max_chunk_size && st->interleaver_chunk_size > s->max_chunk_size)            || (max && st->interleaver_chunk_duration           > max)) {            st->interleaver_chunk_size      = 0;            this_pktl->pkt.flags |= CHUNK_START;            if (max && st->interleaver_chunk_duration > max) {                int64_t syncoffset = (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)*max/2;                int64_t syncto = av_rescale(pkt->dts + syncoffset, 1, max)*max - syncoffset;                st->interleaver_chunk_duration += (pkt->dts - syncto)/8 - max;            } else                st->interleaver_chunk_duration = 0;        }    }    if (*next_point) {        if (chunked && !(this_pktl->pkt.flags & CHUNK_START))            goto next_non_null;        if (compare(s, &s->internal->packet_buffer_end->pkt, pkt)) {            while (   *next_point                   && ((chunked && !((*next_point)->pkt.flags&CHUNK_START))                       || !compare(s, &(*next_point)->pkt, pkt)))                next_point = &(*next_point)->next;            if (*next_point)                goto next_non_null;        } else {            next_point = &(s->internal->packet_buffer_end->next);        }    }    av_assert1(!*next_point);    s->internal->packet_buffer_end = this_pktl;next_non_null:    this_pktl->next = *next_point;    s->streams[pkt->stream_index]->last_in_packet_buffer =        *next_point                                      = this_pktl;    return 0;}
代码中可以看到,函数ff_interleave_add_packet把pkt经过排序(如果缓存中有pkt的话)加入到缓存中,这个排序是根据ff_interleave_packet_per_dts函数传入的compare(其实就是函数interleave_compare_dts)指针做的,来看看interleave_compare_dts函数的代码:
static int interleave_compare_dts(AVFormatContext *s, AVPacket *next,                                  AVPacket *pkt){    AVStream *st  = s->streams[pkt->stream_index];    AVStream *st2 = s->streams[next->stream_index];    int comp      = av_compare_ts(next->dts, st2->time_base, pkt->dts,                                  st->time_base);    if (s->audio_preload && ((st->codec->codec_type == AVMEDIA_TYPE_AUDIO) != (st2->codec->codec_type == AVMEDIA_TYPE_AUDIO))) {        int64_t ts = av_rescale_q(pkt ->dts, st ->time_base, AV_TIME_BASE_Q) - s->audio_preload*(st ->codec->codec_type == AVMEDIA_TYPE_AUDIO);        int64_t ts2= av_rescale_q(next->dts, st2->time_base, AV_TIME_BASE_Q) - s->audio_preload*(st2->codec->codec_type == AVMEDIA_TYPE_AUDIO);        if (ts == ts2) {            ts= ( pkt ->dts* st->time_base.num*AV_TIME_BASE - s->audio_preload*(int64_t)(st ->codec->codec_type == AVMEDIA_TYPE_AUDIO)* st->time_base.den)*st2->time_base.den               -( next->dts*st2->time_base.num*AV_TIME_BASE - s->audio_preload*(int64_t)(st2->codec->codec_type == AVMEDIA_TYPE_AUDIO)*st2->time_base.den)* st->time_base.den;            ts2=0;        }        comp= (ts>ts2) - (ts<ts2);    }    if (comp == 0)        return pkt->stream_index < next->stream_index;    return comp > 0;}
从代码中可以看到interleave_compare_dts函数其实就是在比较传入的2个pkt的dts值,其实就是比那个的dts更早,如果有音频还有audio_preload(音频预加载时间)的话,还需要把音频包的预加载时间算进去。

好了,现在整体逻辑就清晰了,得出的结论是:在有多个流的情况下要用av_interleaved_write_frame,只有单一流2个函数都可以用。


1 0