国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

FFmpeg源碼分析:avcodec_send_frame()和avcodec_receive_packet()音視頻編碼

這篇具有很好參考價(jià)值的文章主要介紹了FFmpeg源碼分析:avcodec_send_frame()和avcodec_receive_packet()音視頻編碼。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

FFmpeg在libavcodec模塊,舊版本提供avcodec_encode_video2()作為視頻編碼函數(shù),avcodec_encode_audio2()作為音頻編碼函數(shù)。在FFmpeg 3.1版本新增avcodec_send_frame()與avcodec_receive_packet()作為音視頻編碼函數(shù)。后來,在3.4版本把a(bǔ)vcodec_encode_video2()和avcodec_encode_audio2()標(biāo)記為過時(shí)API。

在上一篇文章介紹到音視頻解碼的分析:avcodec_send_packet和avcodec_receive_frame。

目錄

一、avcodec_send_frame發(fā)送AVFrame

1、avcodec_send_frame

二、avcodec_receive_packet接收AVPacket

1、avcodec_receive_packet

2、encode_receive_packet_internal

3、encode_simple_receive_packet

4、encode_simple_internal

5、X264_frame

三、avcodec_encode_video2視頻編碼

四、avcodec_encode_audio2音頻編碼


avcodec_send_frame和avcodec_receive_packet函數(shù)定義位于libavcodec/avcodec.h:

/**
 * Supply a raw video or audio frame to the encoder. Use avcodec_receive_packet()
 * to retrieve buffered output packets.
 *
 * @param avctx     codec context
 * @param[in] frame AVFrame containing the raw audio or video frame to be encoded.
 *
 *                  For audio:
 *                  If AV_CODEC_CAP_VARIABLE_FRAME_SIZE is set, then each frame
 *                  can have any number of samples.
 * @return 0 on success, otherwise negative error code:
 *      AVERROR(EAGAIN):   input is not accepted in the current state, try again.
 *      AVERROR_EOF:       the encoder has been flushed, and no new frames.
 *      AVERROR(EINVAL):   codec not opened, refcounted_frames not set, it is a
 *                         decoder, or requires flush
 *      AVERROR(ENOMEM):   failed to add packet to internal queue, or similar
 *      other errors: legitimate encoding errors
 */
int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame);

/**
 * Read encoded data from the encoder.
 *
 * @param avctx codec context
 * @param avpkt This will be set to a reference-counted packet
 *
 * @return 0 on success, otherwise negative error code:
 *      AVERROR(EAGAIN):   output is not available in the current state, try again.
 *      AVERROR_EOF:       the encoder has been fully flushed, and there will be
 *                         no more output packets
 *      AVERROR(EINVAL):   codec not opened, or it is a decoder
 *      other errors: legitimate encoding errors
 */
int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);

由描述可知,?avcodec_send_frame()負(fù)責(zé)給編碼器提供未壓縮的原始數(shù)據(jù),avcodec_receive_packet()則從編碼器取出編碼后的數(shù)據(jù)。如果返回0,代表成功;返回AGAIN,代表當(dāng)前狀態(tài)沒有可輸出數(shù)據(jù);返回EOF,代表已經(jīng)到達(dá)輸入流結(jié)尾;返回INVAL,代表編碼器沒有打開或者打開的是解碼器。

音視頻編碼的函數(shù)執(zhí)行流程圖如下:

FFmpeg源碼分析:avcodec_send_frame()和avcodec_receive_packet()音視頻編碼

一、avcodec_send_frame發(fā)送AVFrame

1、avcodec_send_frame

avcodec_send_frame()函數(shù)位于libavcodec/encode.c,首先判斷編碼器有沒打開、是否為編碼器,然后調(diào)用internal函數(shù)執(zhí)行具體操作:

int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame)
{
    AVCodecInternal *avci = avctx->internal;
    int ret;
    // 判斷編碼器有沒打開,是否為編碼器
    if (!avcodec_is_open(avctx) || !av_codec_is_encoder(avctx->codec))
        return AVERROR(EINVAL);
    if (avci->draining)
        return AVERROR_EOF;
    if (avci->buffer_frame->data[0])
        return AVERROR(EAGAIN);
    if (!frame) {
        avci->draining = 1;
    } else {
        ret = encode_send_frame_internal(avctx, frame);
        if (ret < 0)
            return ret;
    }

    if (!avci->buffer_pkt->data && !avci->buffer_pkt->side_data) {
        ret = encode_receive_packet_internal(avctx, avci->buffer_pkt);
        if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
            return ret;
    }

    return 0;
}

?encode_send_frame_internal()函數(shù)沒做多少事情,只是解析音頻metadata、檢查frame是否有效,真正的賦值操作不在這里。代碼如下:

static int encode_send_frame_internal(AVCodecContext *avctx, const AVFrame *src)
{
    AVCodecInternal *avci = avctx->internal;
    AVFrame *dst = avci->buffer_frame;
    int ret;

    if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) {
        // 解析音頻metadata
        AVFrameSideData *sd = av_frame_get_side_data(src, AV_FRAME_DATA_AUDIO_SERVICE_TYPE);
        if (sd && sd->size >= sizeof(enum AVAudioServiceType))
            avctx->audio_service_type = *(enum AVAudioServiceType*)sd->data;
        // 檢查frame大小是否有效
        if (avctx->codec->capabilities & AV_CODEC_CAP_SMALL_LAST_FRAME) {
            if (src->nb_samples > avctx->frame_size) {
                return AVERROR(EINVAL);
            }
        } else if (!(avctx->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)) {
            if (avctx->internal->last_audio_frame) {
                return AVERROR(EINVAL);
            }
            if (src->nb_samples < avctx->frame_size) {
                ret = pad_last_frame(avctx, dst, src);
                if (ret < 0)
                    return ret;
                avctx->internal->last_audio_frame = 1;
            } else if (src->nb_samples > avctx->frame_size) {
                return AVERROR(EINVAL);
            }
        }
    }
    if (!dst->data[0]) {
        ret = av_frame_ref(dst, src);
        if (ret < 0)
             return ret;
    }

    return 0;
}

二、avcodec_receive_packet接收AVPacket

1、avcodec_receive_packet

avcodec_receive_packet()函數(shù)也是首先編碼器是否打開、是否為編碼器,然后調(diào)用encode_receive_packet_internal()函數(shù)去執(zhí)行具體操作:

int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
{
    AVCodecInternal *avci = avctx->internal;
    int ret;
    av_packet_unref(avpkt);
    // 判斷編碼器是否打開,是否為編碼器
    if (!avcodec_is_open(avctx) || !av_codec_is_encoder(avctx->codec))
        return AVERROR(EINVAL);

    if (avci->buffer_pkt->data || avci->buffer_pkt->side_data) {
        av_packet_move_ref(avpkt, avci->buffer_pkt);
    } else {
        ret = encode_receive_packet_internal(avctx, avpkt);
        if (ret < 0)
            return ret;
    }

    return 0;
}

2、encode_receive_packet_internal

encode_receive_packet_internal()函數(shù)首先檢測視頻寬高、像素格式,然后判斷使用receive_packet還是encode_simple_receive_packet執(zhí)行編碼操作:

static int encode_receive_packet_internal(AVCodecContext *avctx, AVPacket *avpkt)
{
    AVCodecInternal *avci = avctx->internal;
    int ret;

    if (avci->draining_done)
        return AVERROR_EOF;
    // 檢查視頻寬、高、像素格式
    if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) {
        if (av_image_check_size2(avctx->width, avctx->height, 
			avctx->max_pixels, AV_PIX_FMT_NONE, 0, avctx))
            return AVERROR(EINVAL);
    }
    // 判斷使用receive_packet還是encode_simple_receive_packet編碼
    if (avctx->codec->receive_packet) {
        ret = avctx->codec->receive_packet(avctx, avpkt);
        if (ret < 0)
            av_packet_unref(avpkt);
        else
            av_assert0(!avpkt->data || avpkt->buf);
    } else
        ret = encode_simple_receive_packet(avctx, avpkt);

    if (ret == AVERROR_EOF)
        avci->draining_done = 1;

    return ret;
}

3、encode_simple_receive_packet

encode_simple_receive_packet()函數(shù)比較簡單,主要是調(diào)用encode_simple_internal()函數(shù):

static int encode_simple_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
{
    int ret;

    while (!avpkt->data && !avpkt->side_data) {
        ret = encode_simple_internal(avctx, avpkt);
        if (ret < 0)
            return ret;
    }

    return 0;
}

4、encode_simple_internal

encode_simple_internal()函數(shù)首先判斷frame,如果frame為空則調(diào)用ff_encode_get_frame()取出一幀未壓縮的數(shù)據(jù),然后判斷使用ff_thread_video_encode_frame還是avctx->codec->encode2執(zhí)行真正的編碼操作:

static int encode_simple_internal(AVCodecContext *avctx, AVPacket *avpkt)
{
    AVCodecInternal   *avci = avctx->internal;
    EncodeSimpleContext *es = &avci->es;
    AVFrame          *frame = es->in_frame;
    int got_packet;
    int ret;

    if (avci->draining_done)
        return AVERROR_EOF;
    // 如果frame為空,調(diào)用ff_encode_get_frame取一幀數(shù)據(jù)
    if (!frame->buf[0] && !avci->draining) {
        av_frame_unref(frame);
        ret = ff_encode_get_frame(avctx, frame);
        if (ret < 0 && ret != AVERROR_EOF)
            return ret;
    }
    if (!frame->buf[0]) {
        if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY ||
              (avci->frame_thread_encoder && avctx->active_thread_type 
			  & FF_THREAD_FRAME)))
            return AVERROR_EOF;
        frame = NULL;
    }
    got_packet = 0;
    // 判斷使用ff_thread_video_encode_frame還是avctx->codec->encode2進(jìn)行編碼
    if (CONFIG_FRAME_THREAD_ENCODER &&
        avci->frame_thread_encoder && (avctx->active_thread_type & FF_THREAD_FRAME))
        ret = ff_thread_video_encode_frame(avctx, avpkt, frame, &got_packet);
    else {
        ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet);
        if (avctx->codec->type == AVMEDIA_TYPE_VIDEO && !ret && got_packet &&
            !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY))
            avpkt->pts = avpkt->dts = frame->pts;
    }

    if (!ret && got_packet) {
        if (avpkt->data) {
            ret = av_packet_make_refcounted(avpkt);
            if (ret < 0)
                goto end;
        }
        if (frame && !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY)) {
            if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) {
                if (avpkt->pts == AV_NOPTS_VALUE)
                    avpkt->pts = frame->pts;
                if (!avpkt->duration)
                    avpkt->duration = ff_samples_to_time_base(
				        avctx, frame->nb_samples);
            }
        }
        // 如果是音頻,flags都設(shè)為關(guān)鍵幀
        if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) {
            avpkt->flags |= AV_PKT_FLAG_KEY;
            avpkt->dts = avpkt->pts;
        }
    }

    if (avci->draining && !got_packet)
        avci->draining_done = 1;

end:
    if (ret < 0 || !got_packet)
        av_packet_unref(avpkt);
    if (frame) {
        if (!ret)
            avctx->frame_number++;
        av_frame_unref(frame);
    }

    return ret;
}

?在上面有提到,avcodec_send_frame()函數(shù)沒有進(jìn)行frame的賦值操作,真正執(zhí)行賦值操作的是ff_encode_get_frame()函數(shù),具體如下:

int ff_encode_get_frame(AVCodecContext *avctx, AVFrame *frame)
{
    AVCodecInternal *avci = avctx->internal;

    if (avci->draining)
        return AVERROR_EOF;
    if (!avci->buffer_frame->buf[0])
        return AVERROR(EAGAIN);
    // avci->buffer_frame賦值給frame
    av_frame_move_ref(frame, avci->buffer_frame);

    return 0;
}

5、X264_frame

以libx264編碼器為例,位于libavcodec/libx264.c,對應(yīng)的AVCodec如下:

AVCodec ff_libx264_encoder = {
    .name             = "libx264",
    .long_name        = NULL_IF_CONFIG_SMALL("libx264 H.264/AVC /MPEG-4 part10"),
    .type             = AVMEDIA_TYPE_VIDEO,
    .id               = AV_CODEC_ID_H264,
    .priv_data_size   = sizeof(X264Context),
    .init             = X264_init,
    .encode2          = X264_frame,
    .close            = X264_close,
    .capabilities     = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS |
                        AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
    .caps_internal    = FF_CODEC_CAP_AUTO_THREADS,
    .priv_class       = &x264_class,
    .defaults         = x264_defaults,
    .pix_fmts         = pix_fmts_all,
    .wrapper_name     = "libx264",
};

此時(shí),encode2函數(shù)指針指向X264_frame,代碼如下:

static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame,
                      int *got_packet)
{
    X264Context *x4 = ctx->priv_data;
    x264_nal_t *nal;
    int nnal, i, ret;
    x264_picture_t pic_out = {0};
    int pict_type;
    int bit_depth;
    int64_t wallclock = 0;
    X264Opaque *out_opaque;
    AVFrameSideData *sd;
    // 初始化x264編碼器相關(guān)參數(shù)
    x264_picture_init( &x4->pic );
    x4->pic.img.i_csp = x4->params.i_csp;
#if X264_BUILD >= 153
    bit_depth = x4->params.i_bitdepth;
#else
    bit_depth = x264_bit_depth;
#endif
    if (bit_depth > 8)
        x4->pic.img.i_csp |= X264_CSP_HIGH_DEPTH;
    x4->pic.img.i_plane = avfmt2_num_planes(ctx->pix_fmt);

    if (frame) {
        for (i = 0; i < x4->pic.img.i_plane; i++) {
            x4->pic.img.plane[i]    = frame->data[i];
            x4->pic.img.i_stride[i] = frame->linesize[i];
        }

        x4->pic.i_pts  = frame->pts;
        x4->reordered_opaque[x4->next_reordered_opaque].reordered_opaque = frame->reordered_opaque;
        x4->reordered_opaque[x4->next_reordered_opaque].wallclock = wallclock;
        if (ctx->export_side_data & AV_CODEC_EXPORT_DATA_PRFT)
            x4->reordered_opaque[x4->next_reordered_opaque].wallclock = av_gettime();
        x4->pic.opaque = &x4->reordered_opaque[x4->next_reordered_opaque];
        x4->next_reordered_opaque++;
        x4->next_reordered_opaque %= x4->nb_reordered_opaque;
        // 給x264的pic.i_type賦值,包括IDR、I、P、B幀類型
        switch (frame->pict_type) {
        case AV_PICTURE_TYPE_I:
            x4->pic.i_type = x4->forced_idr > 0 ? X264_TYPE_IDR
                                                : X264_TYPE_KEYFRAME;
            break;
        case AV_PICTURE_TYPE_P:
            x4->pic.i_type = X264_TYPE_P;
            break;
        case AV_PICTURE_TYPE_B:
            x4->pic.i_type = X264_TYPE_B;
            break;
        default:
            x4->pic.i_type = X264_TYPE_AUTO;
            break;
        }
		// 重新配置編碼器
        reconfig_encoder(ctx, frame);

        ......
    }

    do {
		// 調(diào)用x264_encoder_encode()執(zhí)行編碼
        if (x264_encoder_encode(x4->enc, &nal, &nnal, frame? &x4->pic: NULL, &pic_out) < 0)
            return AVERROR_EXTERNAL;
        // 對nal單元進(jìn)行編碼
        ret = encode_nals(ctx, pkt, nal, nnal);
        if (ret < 0)
            return ret;
    } while (!ret && !frame && x264_encoder_delayed_frames(x4->enc));

    if (!ret)
        return 0;

    pkt->pts = pic_out.i_pts;
    pkt->dts = pic_out.i_dts;
    out_opaque = pic_out.opaque;
    if (out_opaque >= x4->reordered_opaque &&
        out_opaque < &x4->reordered_opaque[x4->nb_reordered_opaque]) {
        ctx->reordered_opaque = out_opaque->reordered_opaque;
        wallclock = out_opaque->wallclock;
    } else {
        ctx->reordered_opaque = 0;
    }
    // 給pict_type賦值,包括I、P、B幀類型
    switch (pic_out.i_type) {
    case X264_TYPE_IDR:
    case X264_TYPE_I:
        pict_type = AV_PICTURE_TYPE_I;
        break;
    case X264_TYPE_P:
        pict_type = AV_PICTURE_TYPE_P;
        break;
    case X264_TYPE_B:
    case X264_TYPE_BREF:
        pict_type = AV_PICTURE_TYPE_B;
        break;
    default:
        return AVERROR_EXTERNAL;
    }
    pkt->flags |= AV_PKT_FLAG_KEY*pic_out.b_keyframe;
    if (ret) {
        ff_side_data_set_encoder_stats(pkt, 
			(pic_out.i_qpplus1 - 1) * FF_QP2LAMBDA, NULL, 0, pict_type);
        if (wallclock)
            ff_side_data_set_prft(pkt, wallclock);
    }

    *got_packet = ret;
    return 0;
}

由此可見,X264_frame()函數(shù)主要有6個(gè)步驟:

  • 初始化x264編碼器相關(guān)參數(shù);
  • 給x264的pic.i_type賦值,包括IDR、I、P、B幀類型;
  • 調(diào)用reconfig_encoder()重新配置編碼器;
  • 調(diào)用x264_encoder_encode()執(zhí)行編碼;
  • 對nal單元進(jìn)行編碼;
  • 給輸出的pict_type賦值,包括I、P、B幀類型;

三、avcodec_encode_video2視頻編碼

由于avcodec_encode_video2()函數(shù)已經(jīng)過時(shí),所以內(nèi)部提供compat_encode()來兼容舊版本,具體代碼如下:

int avcodec_encode_video2(AVCodecContext *avctx,
                                              AVPacket *avpkt,
                                              const AVFrame *frame,
                                              int *got_packet_ptr)
{
    int ret = compat_encode(avctx, avpkt, got_packet_ptr, frame);

    if (ret < 0)
        av_packet_unref(avpkt);

    return ret;
}

我們來看看compat_encode()函數(shù)源碼,其實(shí)內(nèi)部也是調(diào)用avcodec_send_frame()和avcodec_receive_packet()進(jìn)行編碼,具體如下:?

static int compat_encode(AVCodecContext *avctx, AVPacket *avpkt,
                         int *got_packet, const AVFrame *frame)
{
    AVCodecInternal *avci = avctx->internal;
    AVPacket user_pkt;
    int ret;
    *got_packet = 0;
    // 檢測視頻的pixel_format、width、height
    if (frame && avctx->codec->type == AVMEDIA_TYPE_VIDEO) {
        if (frame->format == AV_PIX_FMT_NONE)
            av_log(avctx, AV_LOG_WARNING, "format is not set\n");
        if (frame->width == 0 || frame->height == 0)
            av_log(avctx, AV_LOG_WARNING, "width or height is not set\n");
    }
    // 調(diào)用avcodec_send_frame()發(fā)送frame
    ret = avcodec_send_frame(avctx, frame);
    if (ret == AVERROR_EOF)
        ret = 0;
    else if (ret == AVERROR(EAGAIN)) {
        return AVERROR_BUG;
    } else if (ret < 0)
        return ret;

    av_packet_move_ref(&user_pkt, avpkt);
    while (ret >= 0) {
		// 調(diào)用avcodec_receive_packet()接收packet
        ret = avcodec_receive_packet(avctx, avpkt);
        if (ret < 0) {
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                ret = 0;
            goto finish;
        }

        if (avpkt != avci->compat_encode_packet) {
            if (avpkt->data && user_pkt.data) {
                if (user_pkt.size >= avpkt->size) {
                    memcpy(user_pkt.data, avpkt->data, avpkt->size);
                    av_buffer_unref(&avpkt->buf);
                    avpkt->buf  = user_pkt.buf;
                    avpkt->data = user_pkt.data;
                } else {
                    av_packet_unref(avpkt);
                    ret = AVERROR(EINVAL);
                    goto finish;
                }
            }

            *got_packet = 1;
            avpkt = avci->compat_encode_packet;
        } else {
            if (!avci->compat_decode_warned) {
                avci->compat_decode_warned = 1;
                av_packet_unref(avpkt);
            }
        }
        if (avci->draining)
            break;
    }

finish:
    if (ret < 0)
        av_packet_unref(&user_pkt);

    return ret;
}

四、avcodec_encode_audio2音頻編碼

和avcodec_encode_video2()函數(shù)一樣,avcodec_encode_audio2()函數(shù)已經(jīng)過時(shí),內(nèi)部也是提供compat_encode()來兼容舊版本,具體代碼如下:

int avcodec_encode_audio2(AVCodecContext *avctx,
                                              AVPacket *avpkt,
                                              const AVFrame *frame,
                                              int *got_packet_ptr)
{
    int ret = compat_encode(avctx, avpkt, got_packet_ptr, frame);

    if (ret < 0)
        av_packet_unref(avpkt);

    return ret;
}

至此,avcodec_send_frame()和avcodec_receive_packet()組成的編碼函數(shù)已經(jīng)分析完畢。

學(xué)習(xí)FFmpeg與代碼實(shí)踐,可參考:FFmpegAndroid文章來源地址http://www.zghlxwxcb.cn/news/detail-410283.html

到了這里,關(guān)于FFmpeg源碼分析:avcodec_send_frame()和avcodec_receive_packet()音視頻編碼的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • FFmpeg5.0源碼閱讀——av_interleaved_write_frame

    FFmpeg5.0源碼閱讀——av_interleaved_write_frame

    ?? 摘要 :本文主要詳細(xì)描述FFmpeg中封裝時(shí)寫packet到媒體文件的函數(shù) av_interleaved_write_frame 的實(shí)現(xiàn)。 ?? : av_interleaved_write_frame ?? 讀者須知 :讀者需要熟悉ffmpeg的基本使用。 ?? av_interleaved_write_frame 的基本調(diào)用流程圖如下。 ??首先就是根據(jù)輸入數(shù)據(jù)是否為空

    2024年02月14日
    瀏覽(15)
  • ffmpeg.c源碼與函數(shù)關(guān)系分析

    ffmpeg.c源碼與函數(shù)關(guān)系分析

    FFmpeg 是一個(gè)可以處理音視頻的軟件,功能非常強(qiáng)大,主要包括,編解碼轉(zhuǎn)換,封裝格式轉(zhuǎn)換,濾鏡特效。 FFmpeg支持各種網(wǎng)絡(luò)協(xié)議,支持 RTMP ,RTSP,HLS 等高層協(xié)議的推拉流,也支持更底層的TCP/UDP 協(xié)議推拉流。 FFmpeg 可以在 Windows,Linux,Mac,iOS,Android等操作系統(tǒng)上運(yùn)行。 F

    2024年02月14日
    瀏覽(53)
  • (02)Cartographer源碼無死角解析-(78) ROS數(shù)據(jù)發(fā)布→2D點(diǎn)云數(shù)據(jù)、tf、機(jī)器人tracking frame軌跡發(fā)布

    (02)Cartographer源碼無死角解析-(78) ROS數(shù)據(jù)發(fā)布→2D點(diǎn)云數(shù)據(jù)、tf、機(jī)器人tracking frame軌跡發(fā)布

    講解關(guān)于slam一系列文章匯總鏈接:史上最全slam從零開始,針對于本欄目講解(02)Cartographer源碼無死角解析-鏈接如下: (02)Cartographer源碼無死角解析- (00)目錄_最新無死角講解:https://blog.csdn.net/weixin_43013761/article/details/127350885 ? 文末正下方中心提供了本人 聯(lián)系方式, 點(diǎn)擊本人照片

    2024年02月12日
    瀏覽(27)
  • FFMPEG源碼之ffmpeg.c解析

    下面是對每個(gè)步驟的功能的詳細(xì)解釋: 初始化動態(tài)加載。 調(diào)用init_dynload函數(shù),用于初始化動態(tài)加載庫的相關(guān)資源,以便在需要時(shí)加載需要的庫。 注冊退出回調(diào)函數(shù)。 調(diào)用register_exit函數(shù),將ffmpeg_cleanup函數(shù)注冊為在程序退出時(shí)被調(diào)用的回調(diào)函數(shù)。 設(shè)置stderr的緩沖模式。 調(diào)用

    2024年02月15日
    瀏覽(25)
  • FFmpeg5.0源碼閱讀——FFmpeg大體框架

    FFmpeg5.0源碼閱讀——FFmpeg大體框架

    ?? 摘要 :前一段時(shí)間熟悉了下FFmpeg主流程源碼實(shí)現(xiàn),對FFmpeg的整體框架有了個(gè)大概的認(rèn)識,因此在此做一個(gè)筆記,希望以比較容易理解的文字描述FFmpeg本身的結(jié)構(gòu),加深對FFmpeg的框架進(jìn)行梳理加深理解,如果文章中有紕漏或者錯(cuò)誤歡迎指出。本文描述了FFmpeg編解碼框架的

    2024年02月11日
    瀏覽(22)
  • FFmpeg開發(fā)筆記(六)如何訪問Github下載FFmpeg源碼

    FFmpeg開發(fā)筆記(六)如何訪問Github下載FFmpeg源碼

    ? 學(xué)習(xí)FFmpeg的時(shí)候,經(jīng)常要到GitHub下載各種開源代碼,比如FFmpeg的源碼頁面位于https://github.com/FFmpeg/FFmpeg。然而國內(nèi)訪問GitHub很不穩(wěn)定,經(jīng)常打不開該網(wǎng)站,比如在命令行執(zhí)行下面的ping命令。 上面的ping結(jié)果如下所示,可見默認(rèn)解析的DNS地址連接超時(shí)。 現(xiàn)在GitHub的DNS請求超時(shí)

    2024年03月17日
    瀏覽(50)
  • FFmpeg學(xué)習(xí):FFmpeg4數(shù)據(jù)結(jié)構(gòu)分析

    FFmpeg學(xué)習(xí):FFmpeg4數(shù)據(jù)結(jié)構(gòu)分析

    FFMPEG中結(jié)構(gòu)體很多。最關(guān)鍵的結(jié)構(gòu)體可以分成以下幾類: 1、解協(xié)議(http,rtsp,rtmp,mms) AVIOContext,URLProtocol,URLContext主要存儲視音頻使用的協(xié)議的類型以及狀態(tài)。URLProtocol存儲輸入視音頻使用的封裝格式。每種協(xié)議都對應(yīng)一個(gè)URLProtocol結(jié)構(gòu)。(注意:FFMPEG中文件也被當(dāng)做一種協(xié)

    2024年02月05日
    瀏覽(20)
  • [FFmpeg] 源碼編譯

    git clone https://git.ffmpeg.org/ffmpeg.git git checkout -b 5.1 remotes/origin/release/5.1 ./configure --prefix=./OUT --enable-shared --disable-static make make install 默認(rèn)安裝路徑見: /usr/local/bin /usr/local/include /usr/local/lib /usr/local/main/man1 /usr/local/main/man3 有 --prefix 參數(shù)的安裝路徑: [prefix]/bin [prefix]/include [prefix

    2024年02月10日
    瀏覽(18)
  • 源碼編譯FFmpeg4.3

    FreeSWITCH的mod_av模塊目前(1.10.11)暫不支持FFmpeg4.4(或者更高版本),但4.3就沒問題 最近試了試源碼編譯FFmpeg4.3,記錄如下(系統(tǒng)centos7.9): git clone?GitHub - BtbN/FFmpeg-Builds 找到4.4.sh,改成這樣: GIT_BRANCH=\\\"release/4.3\\\" ./build.sh linux64 gpl 4.4 cd ffbuild/ffmpeg ./configure?--enable-shared make

    2024年02月01日
    瀏覽(22)
  • FFmpeg源碼走讀之內(nèi)存管理模型

    FFmpeg源碼走讀之內(nèi)存管理模型

    數(shù)據(jù)包管理過程中當(dāng)數(shù)據(jù)轉(zhuǎn)移到新的數(shù)據(jù)包時(shí)存在兩種操作一種是數(shù)據(jù)包之間相互獨(dú)立,當(dāng)新創(chuàng)建一份數(shù)據(jù)包時(shí),需要將原來的數(shù)據(jù)重新申請一個(gè)數(shù)據(jù)空間并且將數(shù)據(jù)拷貝到新的數(shù)據(jù)包中,具體過程如下圖所示。這種數(shù)據(jù)包的管理優(yōu)勢是在于數(shù)據(jù)之間相互獨(dú)立,不會存在數(shù)據(jù)

    2023年04月24日
    瀏覽(18)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包