RK3568笔记十三:Zlmedia推流测试

若该文为原创文章,转载请注明原文出处。

使用正点原子的屏幕竖屏用不习惯,所以想推流用VLC方式显示,而Zlmedia功能很强大,推流拉流都有,拉流在前面有提及。研究了几天,最后还是勇哥帮忙,所以知道了怎么推流。

一、环境

1、平台:rk3568

2、开发板:ATK-RK3568正点原子板子

3、环境:buildroot

二、流程

把主要的函数贴出来。

1、初始化部分

    mk_env_init(&config) ;                             #  初始化

    mk_rtsp_server_start(554, 0);                  # RTSP 服务器

    mk_rtmp_server_start(1935, 0);              # RTMP 服务器

    mk_player player = mk_player_create();    # 创建player

    mk_player_set_on_result(player, on_mk_play_event_func, ctx);

    mk_player_set_on_shutdown(player, on_mk_shutdown_func, ctx);

    mk_player_play(player, url);

2、回调部分

        ctx->push_url = "rtmp://localhost/live/stream";

        ctx->media = mk_media_create("__defaultVhost__", "live", "test", 0, 0, 0);

        for (i = 0; i < track_count; ++i)

        {

            if (mk_track_is_video(tracks[i]))

            {

                log_info("got video track: %s", mk_track_codec_name(tracks[i]));

                // 监听track数据回调

                mk_media_init_track(ctx->media, tracks[i]);

                mk_track_add_delegate(tracks[i], on_track_frame_out, user_data);

            }

        }

        mk_media_init_complete(ctx->media);

        mk_media_set_on_regist(ctx->media, on_mk_media_source_regist_func, ctx);

RTSP推流地址为rtsp:/localhost/live:554/test

三、完整代码参考


void mpp_decoder_frame_callback(void *userdata, int width_stride, int height_stride, int width, int height, int format, int fd, void *data)
{
    rknn_app_context_t *ctx = (rknn_app_context_t *)userdata;

    int ret = 0;
    static int frame_index = 0;
    frame_index++;

    void *mpp_frame = NULL;
    int mpp_frame_fd = 0;
    void *mpp_frame_addr = NULL;
    int enc_data_size;

    rga_buffer_t origin;
    rga_buffer_t src;

    // 编码器准备
    if (ctx->encoder == NULL)
    {
        MppEncoder *mpp_encoder = new MppEncoder();
        MppEncoderParams enc_params;
        memset(&enc_params, 0, sizeof(MppEncoderParams));
        enc_params.width = width;
        enc_params.height = height;
        enc_params.hor_stride = width_stride;
        enc_params.ver_stride = height_stride;
        enc_params.fmt = MPP_FMT_YUV420SP;
        enc_params.type = MPP_VIDEO_CodingAVC;
        mpp_encoder->Init(enc_params, NULL);
        printf("Encoder params:  width=%d, height=%d, hor_strider=%d, ver_strider=%d\n", width, height, width_stride, height_stride);

        ctx->encoder = mpp_encoder;
    }

    int enc_buf_size = ctx->encoder->GetFrameSize();
    char *enc_data = (char *)malloc(enc_buf_size);

    mpp_frame = ctx->encoder->GetInputFrameBuffer();
    mpp_frame_fd = ctx->encoder->GetInputFrameBufferFd(mpp_frame);
    mpp_frame_addr = ctx->encoder->GetInputFrameBufferAddr(mpp_frame);

     // 复制到另一个缓冲区,避免修改mpp解码器缓冲区
    // 使用的是RK RGA的格式转换:YUV420SP -> RGB888
    origin = wrapbuffer_fd(fd, width, height, RK_FORMAT_YCbCr_420_SP, width_stride, height_stride);
    src = wrapbuffer_fd(mpp_frame_fd, width, height, RK_FORMAT_YCbCr_420_SP, width_stride, height_stride);
    cv::Mat origin_mat = cv::Mat::zeros(height, width, CV_8UC3);
    rga_buffer_t rgb_img = wrapbuffer_virtualaddr((void *)origin_mat.data, width, height, RK_FORMAT_RGB_888);
    imcopy(origin, rgb_img);

    static int job_cnt = 0;
    static int result_cnt = 0;
    
    // 提交推理任务给线程池
    yolov5_thread_pool->submitTask(origin_mat, job_cnt++);
    std::vector<Detection> objects;
    // 获取推理结果
    auto ret_code = yolov5_thread_pool->getTargetResultNonBlock(objects, result_cnt);
    if (ret_code == NN_SUCCESS)
    {
        result_cnt++;
    }
    else
    {
        goto RET;
    }

    DrawDetections(origin_mat, objects);
    imcopy(rgb_img, src);

    /* 缩放显示 采用drm方式显示,1280*720  */
    /* 格式RGB888-> ARGB888 在缩放成1280*720 */
    

    // 推流
    static int dts = 0;
    if (frame_index == 1)
    {
        enc_data_size = ctx->encoder->GetHeader(enc_data, enc_buf_size);
    }
    dts += 40;
    memset(enc_data, 0, enc_buf_size);
    enc_data_size = ctx->encoder->Encode(mpp_frame, enc_data, enc_buf_size);
    printf("enc_data_size=%d\n", enc_data_size);
    printf("pushing...");
    ret = mk_media_input_h264(ctx->media, enc_data, enc_data_size, ctx->dts, ctx->pts);
    if (ret != 1)
    {
        printf("mk_media_input_frame failed\n");
    }

RET: // tag
    if (enc_data != nullptr)
    {
        free(enc_data);
    }
}


void API_CALL on_track_frame_out(void *user_data, mk_frame frame)
{
    rknn_app_context_t *ctx = (rknn_app_context_t *)user_data;
    printf("on_track_frame_out ctx=%p\n", ctx);
    const char *data = mk_frame_get_data(frame);
    ctx->dts = mk_frame_get_dts(frame);
    ctx->pts = mk_frame_get_pts(frame);
    size_t size = mk_frame_get_data_size(frame);
    printf("decoder=%p\n", ctx->decoder);
    ctx->decoder->Decode((uint8_t *)data, size, 0);
    // mk_media_input_frame(ctx->media, frame);
}



void API_CALL on_mk_push_event_func(void *user_data, int err_code, const char *err_msg)
{
    rknn_app_context_t *ctx = (rknn_app_context_t *)user_data;
    if (err_code == 0)
    {
        // push success
        log_info("push %s success!", ctx->push_url);
        printf("push %s success!\n", ctx->push_url);
    }
    else
    {
        log_warn("push %s failed:%d %s", ctx->push_url, err_code, err_msg);
        printf("push %s failed:%d %s\n", ctx->push_url, err_code, err_msg);
        release_pusher(&(ctx->pusher));
    }
}

void API_CALL on_mk_media_source_regist_func(void *user_data, mk_media_source sender, int regist)
{
    printf("mk_media_source:%x\n", sender);
    rknn_app_context_t *ctx = (rknn_app_context_t *)user_data;
    const char *schema = mk_media_source_get_schema(sender);
    if (strncmp(schema, ctx->push_url, strlen(schema)) == 0)
    {
        // 判断是否为推流协议相关的流注册或注销事件
        printf("schema: %s\n", schema);
        release_pusher(&(ctx->pusher));
        if (regist)
        {
            ctx->pusher = mk_pusher_create_src(sender);
            mk_pusher_set_on_result(ctx->pusher, on_mk_push_event_func, ctx);
            mk_pusher_set_on_shutdown(ctx->pusher, on_mk_push_event_func, ctx);
            //            mk_pusher_publish(ctx->pusher, ctx->push_url);
            log_info("push started!");
            printf("push started!\n");
        }
        else
        {
            log_info("push stoped!");
            printf("push stoped!\n");
        }
        printf("push_url:%s\n", ctx->push_url);
    }
    else
    {
        printf("unknown schema:%s\n", schema);
    }
}

void API_CALL on_mk_play_event_func(void *user_data, int err_code, const char *err_msg, mk_track tracks[],
                                    int track_count)
{
    rknn_app_context_t *ctx = (rknn_app_context_t *)user_data;
    if (err_code == 0)
    {
        // success
        printf("play success!");
        int i;
        ctx->push_url = "rtmp://localhost/live/stream";
        ctx->media = mk_media_create("__defaultVhost__", "live", "test", 0, 0, 0);
        for (i = 0; i < track_count; ++i)
        {
            if (mk_track_is_video(tracks[i]))
            {
                log_info("got video track: %s", mk_track_codec_name(tracks[i]));
                // 监听track数据回调
                mk_media_init_track(ctx->media, tracks[i]);
                mk_track_add_delegate(tracks[i], on_track_frame_out, user_data);
            }
        }
        mk_media_init_complete(ctx->media);
        mk_media_set_on_regist(ctx->media, on_mk_media_source_regist_func, ctx);
        //      codec_args v_args = {0};
        //      mk_track v_track = mk_track_create(MKCodecH264, &v_args);
        //      mk_media_init_track(ctx->media, v_track);
        //      mk_media_init_complete(ctx->media);
        //      mk_track_unref(v_track);
    }
    else
    {
        printf("play failed: %d %s", err_code, err_msg);
    }
}

void API_CALL on_mk_shutdown_func(void *user_data, int err_code, const char *err_msg, mk_track tracks[], int track_count)
{
    printf("play interrupted: %d %s", err_code, err_msg);
}


int process_video_rtsp(rknn_app_context_t *ctx, const char *url)
{
    mk_config config;
    memset(&config, 0, sizeof(mk_config));
    
    config.log_mask = LOG_CONSOLE;

    mk_env_init(&config);
    mk_rtsp_server_start(554, 0);
    mk_rtmp_server_start(1935, 0);
    mk_player player = mk_player_create();
    mk_player_set_on_result(player, on_mk_play_event_func, ctx);
    mk_player_set_on_shutdown(player, on_mk_shutdown_func, ctx);
    mk_player_play(player, url);

    printf("enter any key to exit\n");
    getchar();

    if (player)
    {
        mk_player_release(player);
    }
    
    return 0;
}

整个过程是通过拉取RTSP然后解码并通过RKNN推理后显示在屏幕,并编码在Zlmedia推流。

如有侵权,或需要完整代码,请及时联系博主。

  • 20
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

殷忆枫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值