ffmpeg api-band-test.c 讲解

这篇具有很好参考价值的文章主要介绍了ffmpeg api-band-test.c 讲解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

ffmpeg 4.0.4 源码讲解

draw_horiz_band

av_pix_fmt_desc_get 函数是 FFmpeg 中用于获取像素格式描述信息的函数。它的作用是根据给定的像素格式(AVPixelFormat)返回对应的像素格式描述结构体(AVPixFmtDescriptor),该结构体包含了关于像素格式的详细信息,如分量数、每个分量的位深度、颜色空间等。

参数说明:
pix_fmt:要查询的像素格式,是一个枚举值,表示视频帧的像素格式,如 AV_PIX_FMT_YUV420P、AV_PIX_FMT_YUV422P 等。
返回值:
如果成功找到对应的像素格式描述结构体,则返回该结构体的指针;如果未找到,则返回 NULL。文章来源地址https://www.toymoban.com/news/detail-815556.html


static void draw_horiz_band(AVCodecContext *ctx, const AVFrame *fr, int offset[4],
                            int slice_position, int type, int height)
{
    int i;
    const AVPixFmtDescriptor *pix_fmt_desc;
    int chroma_w, chroma_h;
    int shift_slice_position;
    int shift_height;

    // 标记 draw_horiz_band 被调用
    draw_horiz_band_called = 1;

    // 获取像素格式描述信息
    pix_fmt_desc = av_pix_fmt_desc_get(ctx->pix_fmt);
    // 计算色度分量宽高
    chroma_w = -((-ctx->width) >> pix_fmt_desc->log2_chroma_w);
    chroma_h = -((-height) >> pix_fmt_desc->log2_chroma_h);
    /*
     ctx->width 表示视频帧的宽度,height 表示视频帧的高度,而 pix_fmt_desc->log2_chroma_w 和 pix_fmt_desc->log2_chroma_h 分别表示色度分量宽度和高度的对数值(即以2为底的对数)。
举个例子,假设视频帧的宽度 ctx->width 为 1920,高度 height 为 1080,而色度分量的采样率为 4:2:0(常见的视频编码格式之一),即色度分量的宽度和高度都是亮度分量的一半。
现在假设 pix_fmt_desc->log2_chroma_w 和 pix_fmt_desc->log2_chroma_h 都为 1(因为 2^1 = 2,所以色度分量的宽度和高度都是亮度分量的一半)。
根据这些假设,我们来看一下这两行代码的计算过程:
    首先,将视频帧的宽度 ctx->width 取反,即 -1920。然后右移 pix_fmt_desc->log2_chroma_w 位,
即右移 1 位,得到 -960。再次取反,得到最终的 chroma_w 值为 960。
    将视频帧的高度 height 取反,即 -1080。然后右移 pix_fmt_desc->log2_chroma_h 位,即右移 1 位,
得到 -540。再次取反,得到最终的 chroma_h 值为 540。
    */
    // 计算偏移值
    shift_slice_position = -((-slice_position) >> pix_fmt_desc->log2_chroma_h);
    shift_height = -((-ctx->height) >> pix_fmt_desc->log2_chroma_h);

    // 处理 Y 分量数据
    for (i = 0; i < height; i++) {
        // 拷贝 Y 分量数据到缓冲区
        memcpy(slice_byte_buffer + ctx->width * slice_position + i * ctx->width,
               fr->data[0] + offset[0] + i * fr->linesize[0], ctx->width);
    }
    // 处理 U 分量数据
    for (i = 0; i < chroma_h; i++) {
        // 拷贝 U 分量数据到缓冲区
        memcpy(slice_byte_buffer + ctx->width * ctx->height + chroma_w * shift_slice_position + i * chroma_w,
               fr->data[1] + offset[1] + i * fr->linesize[1], chroma_w);
    }
    // 处理 V 分量数据
    for (i = 0; i < chroma_h; i++) {
        // 拷贝 V 分量数据到缓冲区
        memcpy(slice_byte_buffer + ctx->width * ctx->height + chroma_w * shift_height + chroma_w * shift_slice_position + i * chroma_w,
               fr->data[2] + offset[2] + i * fr->linesize[2], chroma_w);
    }
}

video_decode

static int video_decode(const char *input_filename)
{
    AVCodec *codec = NULL; // 视频编解码器
    AVCodecContext *ctx = NULL; // 编解码器上下文
    AVCodecParameters *origin_par = NULL; // 视频流参数
    uint8_t *byte_buffer = NULL; // 存储解码后图像数据的缓冲区
    AVFrame *fr = NULL; // 存储解码后图像帧的结构体
    AVPacket pkt; // 存储视频数据的数据包
    AVFormatContext *fmt_ctx = NULL; // 存储格式相关的上下文信息
    int number_of_written_bytes; // 写入字节数
    int video_stream; // 视频流索引
    int got_frame = 0; // 是否解码到一帧图像
    int byte_buffer_size; // 缓冲区大小
    int result; // 操作结果
    int end_of_stream = 0; // 是否到达视频流末尾

    draw_horiz_band_called = 0; // 标志位,用于检查 draw_horiz_band 函数是否被调用过

    // 打开输入文件并获取格式相关的上下文信息
    result = avformat_open_input(&fmt_ctx, input_filename, NULL, NULL);
    if (result < 0) {
        av_log(NULL, AV_LOG_ERROR, "Can't open file\n");
        return result;
    }

    // 获取流信息
    result = avformat_find_stream_info(fmt_ctx, NULL);
    if (result < 0) {
        av_log(NULL, AV_LOG_ERROR, "Can't get stream info\n");
        return result;
    }

    // 找到视频流
    video_stream = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if (video_stream < 0) {
      av_log(NULL, AV_LOG_ERROR, "Can't find video stream in input file\n");
      return -1;
    }

    // 获取视频流的参数
    origin_par = fmt_ctx->streams[video_stream]->codecpar;

    // 查找视频编解码器
    codec = avcodec_find_decoder(origin_par->codec_id);
    if (!codec) {
        av_log(NULL, AV_LOG_ERROR, "Can't find decoder\n");
        return -1;
    }

    // 分配编解码器上下文
    ctx = avcodec_alloc_context3(codec);
    if (!ctx) {
        av_log(NULL, AV_LOG_ERROR, "Can't allocate decoder context\n");
        return AVERROR(ENOMEM);
    }

    // 将流参数拷贝到编解码器上下文
    result = avcodec_parameters_to_context(ctx, origin_par);
    if (result) {
        av_log(NULL, AV_LOG_ERROR, "Can't copy decoder context\n");
        return result;
    }

    // 设置 draw_horiz_band 函数
    ctx->draw_horiz_band = draw_horiz_band;
    ctx->thread_count = 1; // 设置线程数量为 1

    // 打开编解码器
    result = avcodec_open2(ctx, codec, NULL);
    if (result < 0) {
        av_log(ctx, AV_LOG_ERROR, "Can't open decoder\n");
        return result;
    }

    // 分配图像帧结构体
    fr = av_frame_alloc();
    if (!fr) {
        av_log(NULL, AV_LOG_ERROR, "Can't allocate frame\n");
        return AVERROR(ENOMEM);
    }

    // 分配图像数据缓冲区
    byte_buffer_size = av_image_get_buffer_size(ctx->pix_fmt, ctx->width, ctx->height, 32);
    byte_buffer = av_malloc(byte_buffer_size);
    if (!byte_buffer) {
        av_log(NULL, AV_LOG_ERROR, "Can't allocate buffer\n");
        return AVERROR(ENOMEM);
    }

    // 分配 slice_byte_buffer 存储 slice 数据
    slice_byte_buffer = av_malloc(byte_buffer_size);
    if (!slice_byte_buffer) {
        av_log(NULL, AV_LOG_ERROR, "Can't allocate buffer\n");
        return AVERROR(ENOMEM);
    }
    memset(slice_byte_buffer, 0, byte_buffer_size);
    slice_byte_buffer_size = byte_buffer_size;

    // 初始化数据包
    av_init_packet(&pkt);
    do {
        // 读取数据包
        if (!end_of_stream) {
            if (av_read_frame(fmt_ctx, &pkt) < 0) {
                end_of_stream = 1;
            }
        }
        if (end_of_stream) {
            pkt.data = NULL;
            pkt.size = 0;
        }
        // 解码视频帧
        if (pkt.stream_index == video_stream || end_of_stream) {
            got_frame = 0;
            result = avcodec_decode_video2(ctx, fr, &got_frame, &pkt);
            if (result < 0) {
                av_log(NULL, AV_LOG_ERROR, "Error decoding frame\n");
                return result;
            }
            // 如果解码到一帧图像
            if (got_frame) {
                // 处理解码后的图像数据
                process_frame(fr, byte_buffer, byte_buffer_size);
            }
        }
        // 释放数据包
        av_packet_unref(&pkt);
    } while (!end_of_stream || got_frame);

    // 释放资源
    avcodec_free_context(&ctx);
    avformat_close_input(&fmt_ctx);
    av_frame_free(&fr);
    av_free(byte_buffer);
    av_free(slice_byte_buffer);

    return 0;
}

到了这里,关于ffmpeg api-band-test.c 讲解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • RK3588平台开发系列讲解(基础篇)Linux 内核有多少 API 接口

    平台 内核版本 安卓版本 RK3588 Linux 5.10 Android 12 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 Linux 作为比较成熟的操作系统,功能完善,它以众多 API 接口的方式向应用程序提供文件、网络、进程、时间等待服务,并且完美执行了国际 posix 标准。 syscalls_32.h 和 sy

    2024年02月08日
    浏览(60)
  • RK3588平台开发系列讲解(AI 篇)RKNN C API 详细说明

    沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要讲解 RKNN C API 详细说明。

    2024年02月12日
    浏览(41)
  • RK3588平台开发系列讲解(AI 篇)RKNN-Toolkit2 API 介绍

    沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要讲解 RKNN-Toolkit2 API 详细说明。 在使用 RKNN Toolkit2 的所有 API 接口时,都需要先调用 RKNN() 方法初始化 RKNN 对象,当不再使用该对象时,通过调用该对象的 release() 方法进行释放。 初始化 RKNN 对象时,可以设置

    2024年02月09日
    浏览(47)
  • RK3568平台开发系列讲解(驱动基础篇)V4L2 用户空间 API 说明

    🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢设备驱动的主要目的是控制和利用底层硬件,同时向用户展示功能。 这些用户可以是在用户空间或其他内核驱动中运行的应用。 本篇我们将学习如何利用内核公开的 V4L2 设备功能。 我们将从描述和

    2023年04月25日
    浏览(47)
  • org.junit.jupiter.api.Test和org.junit.Test区别

    添加junit4依赖 执行测试时报错 然后换成 执行成功 那么这是为什么呢? 打开junit4的源码 可以得知: 测试注释告诉JUnit,它所附加的公共void方法可以作为测试用例运行。 要运行该方法,JUnit首先构造一个新的类实例,然后调用带注释的方法。 测试引发的任何异常都将被JUni

    2024年02月11日
    浏览(32)
  • API Testing 一个基于 YAML 文件的开源接口测试工具

    目录 前言: 如何使用? 本地模式 服务端模式 文件格式 后续计划 API Testing 是一个基于 YAML 文件的开源接口测试工具,它可以帮助开发者快速地进行接口测试。 在选择工具时,可以从很多方面进行考量、对比,以下几点是该工具的特色或者优点:

    2024年02月16日
    浏览(44)
  • 鸿蒙HarmonyOS应用开发之使用Node-API实现跨语言交互开发流程

    使用Node-API实现跨语言交互,首先需要按照Node-API的机制实现模块的注册和加载等相关动作。 ArkTS/JS侧 :实现C++方法的调用。代码比较简单,import一个对应的so库后,即可调用C++方法。 Native侧 :.cpp文件,实现模块的注册。需要提供注册lib库的名称,并在注册回调方法中定义接

    2024年04月26日
    浏览(45)
  • k8s 中快速启动curl pod 做api test

    k8s上运行的pod需要进行api测试,由于开发使用的镜像都是最小化构建,不能保证现有的pod中一定有curl工具,于是需要启动一个带有curl工具的测试pod专门进行api测试 上述指令实现在指定namespace下创建一个带有curl tool的pod, 可以用于进行api测试.

    2024年02月12日
    浏览(30)
  • Jmeter系列- test plan【测试计划】详细讲解 、 测试计划参数详解 、基础线程组Thread Group

    测试计划描述了 Jmeter 在执行时,一系列的步骤 一个完整的测试计划包含了一个或多个【线程组、逻辑控制器、采样器、监听器、定时器、断言和配置元素】测试计划添加or删除元件 通过右键点击树中的元件,选中要添加的元件 也可以通过合并(merge)或打开(open)从文件中

    2024年02月22日
    浏览(42)
  • FFmpeg入门详解之110:RTSP协议讲解

    测试工具:VLC 数据源:  文件或本地摄像头 测试功能:RTSP直播点播 播放地址:rtsp://127.0.0.1:8554/rtspa001 服务端:推流 客户端:拉流      RTSP(Real Time Streaming Protocol),RFC2326,实时流传输协议 ,是TCP/IP协议体系中的一个应用层协议,由哥伦比亚大学、网景和RealNetworks公司

    2023年04月08日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包