# VDEC 模块接口文档

## VDEC 概述

`VDEC` 模块是对 K230 平台视频解码功能的封装，提供了完整的视频解码解决方案，支持 H.264、H.265 和 JPEG 格式的视频解码。

**主要特性：**

- 解码格式支持：兼容H.264、H.265及JPEG格式，可高效完成对应编码内容的解码处理。
- 全生命周期管理：提供从解码实例创建、启动解码任务、执行解码操作，到停止任务及销毁实例的完整生命周期管理能力，确保解码过程可控可管。
- 多路并发解码：支持多通道并行解码，最大可同时运行4路解码通道，满足多源视频处理需求。
- 灵活解码模式：
  - 流式解码：适配实时视频流、网络流媒体等场景，可对连续视频流进行不间断解码；
  - 按帧解码：适用于图片序列、抓拍帧等场景，支持单帧或间断帧的精准解码。
- 线程安全保障：解码流处理过程采用线程安全设计，确保多线程环境下数据处理的安全性与稳定性，避免并发冲突。

**API 接口调用顺序：**

1. 创建解码器实例：`VideoDecoder(config)`（构造函数）
2. 初始化解码器：`create()`
3. 启动解码器：`start()`
4. 循环解码过程：
   - 发送编码流：`send_stream()`
   - 获取解码帧：`get_frame()`
   - 处理解码帧后释放：`release_frame()`
5. 停止解码：`stop()`
6. 销毁解码器：`destroy()`（析构函数会自动调用）

## 类型定义

### 解码器类型枚举

```cpp
enum DecoderType {
    VDEC_UNKNOWN, ///< 未知/不支持的编解码类型
    VDEC_H264,   ///< H.264 / AVC (高级视频编码) 标准
    VDEC_H265,   ///< H.265 / HEVC (高效视频编码) 标准
    VDEC_JPEG    ///< JPEG (联合图像专家组) 图像压缩标准
};
```

### 解码器配置结构体

```cpp
struct DecoderConfig {
    DecoderType type;       ///< 视频编解码类型 (H264/H265/JPEG)
    uint32_t max_width;     ///< 输入视频帧的最大宽度（像素）
    uint32_t max_height;    ///< 输入视频帧的最大高度（像素）
    uint32_t input_buf_num; ///< 编码流的输入缓冲区数量
    uint32_t output_buf_num;///< 解码帧的输出缓冲区数量

    /**
     * @brief 解码器配置构造函数
     * @param dec_type 视频编解码类型
     * @param in_buf_num 输入缓冲区数量（默认：4）
     * @param out_buf_num 输出缓冲区数量（默认：6）
     * @param w 最大输入帧宽度（像素，默认：1920）
     * @param h 最大输入帧高度（像素，默认：1088）
     */
    DecoderConfig(DecoderType dec_type, uint32_t in_buf_num = 4,
                  uint32_t out_buf_num = 6, uint32_t w = 1920, uint32_t h = 1088);
};
```

## VideoDecoder 类定义

```cpp
class VideoDecoder
{
public:
    /**
     * @brief 构造函数，使用解码器配置初始化
     * @param config 解码器配置参数
     */
    VideoDecoder(const DecoderConfig& config);

    /**
     * @brief 析构函数，清理解码器资源
     */
    ~VideoDecoder();

    /**
     * @brief 创建并初始化解码器实例
     * 分配必要的资源并设置解码参数
     * @return 成功返回0，失败返回非零错误码
     */
    int create();

    /**
     * @brief 启动解码器操作
     * 将解码器置于活动状态，准备处理输入流
     * @return 成功返回0，失败返回非零错误码
     */
    int start();

    /**
     * @brief 发送编码流数据到解码器进行处理
     * @param data 指向编码流数据的指针
     * @param size 编码流数据的大小（字节）
     * @param timestamp 流数据的显示时间戳（默认：0）
     * @param timeout 等待流提交的最大时间（毫秒，默认：-1，无限等待）
     * @return 成功返回0，失败返回非零错误码
     */
    int send_stream(const uint8_t* data, uint32_t size,
                    uint64_t timestamp = 0, int timeout = -1);

    /**
     * @brief 从解码器获取解码后的帧数据
     * @param frame 输出参数，存储解码后的帧数据
     * @param timeout 等待可用帧数据的最大时间（毫秒，默认：-1，无限等待）
     * @return 成功返回0，失败返回非零错误码
     */
    int get_frame(k_video_frame_info& frame, int timeout = -1);

    /**
     * @brief 释放解码帧缓冲区回解码器
     * 处理完帧数据后必须调用，以避免内存泄漏
     * @param frame 要释放的解码帧
     * @return 成功返回0，失败返回非零错误码
     */
    int release_frame(k_video_frame_info& frame);

    /**
     * @brief 停止解码器操作
     * 暂停解码处理，同时保留分配的资源
     * @return 成功返回0，失败返回非零错误码
     */
    int stop();

    /**
     * @brief 销毁解码器实例
     * 释放所有分配的资源并清理解码器
     * @return 成功返回0，失败返回非零错误码
     */
    int destroy();

    /**
     * @brief 获取解码器的硬件通道ID
     * @return 通道ID（整数）
     */
    int get_channel_id() const { return channel_id_; }

private:
    // 私有成员和方法...
};
```

## 接口详细说明

### VideoDecoder 构造函数

```cpp
VideoDecoder(const DecoderConfig& config);
```

**功能：**
使用指定的配置参数创建视频解码器实例。

**参数说明：**

| 参数名   | 类型            | 描述           |
| ----- | ------------- | ------------ |
| config | `DecoderConfig` | 解码器配置参数     |

DecoderConfig 各参数详细说明：

| 参数名           | 类型          | 描述                                                         |
| ---------------- | ------------- | ------------------------------------------------------------ |
| `type`           | `DecoderType` | 视频编解码类型，枚举值包括：<br>- `VDEC_UNKNOWN`：未知或不支持的编码类型<br>- `VDEC_H264`：H.264/AVC标准<br>- `VDEC_H265`：H.265/HEVC标准<br>- `VDEC_JPEG`：JPEG图像压缩标准<br>用于指定解码器需要处理的视频格式。 |
| `max_width`      | `uint32_t`    | 输入视频帧的最大宽度（像素），解码器会根据该值分配适配的缓冲区，默认值为 1920。 |
| `max_height`     | `uint32_t`    | 输入视频帧的最大高度（像素），与 `max_width` 共同决定缓冲区大小，默认值为 1088。 |
| `input_buf_num`  | `uint32_t`    | 编码流的输入缓冲区数量，用于暂存待解码的编码数据，默认值为 4。 |
| `output_buf_num` | `uint32_t`    | 解码帧的输出缓冲区数量，用于存储解码后的视频帧数据，默认值为 6。 |

**注意：**
构造函数仅初始化对象，需要调用 `create()` 方法实际创建解码器。

### VideoDecoder 析构函数

```cpp
~VideoDecoder();
```

**功能：**
自动销毁解码器实例，释放所有分配的资源。

**注意：**
如果解码器仍在运行，析构函数会自动调用 `destroy()` 方法。

### 创建解码器

```cpp
int create();
```

**功能：**
创建并初始化解码器实例，分配必要的硬件资源和缓冲区。

**返回值：**

* `0` - 成功
* `-1` - 失败（无可用解码通道或资源分配失败）

**异常情况：**

* 解码器已创建：输出错误信息并返回 -1
* 无可用解码通道：输出错误信息并返回 -1
* 缓冲区创建失败：输出错误信息并返回 -1

**使用示例：**
```cpp
DecoderConfig config(VDEC_H264);
VideoDecoder decoder(config);
if (decoder.create() != 0) {
    std::cerr << "Failed to create decoder" << std::endl;
    return -1;
}
```

### 启动解码器

```cpp
int start();
```

**功能：**
启动解码器操作，使其进入活动状态并准备处理输入流。

**返回值：**

* `0` - 成功
* `-1` - 失败

**状态要求：**
解码器必须处于 `CREATED` 或 `STOPPED` 状态。

**使用示例：**
```cpp
if (decoder.start() != 0) {
    std::cerr << "Failed to start decoder" << std::endl;
    decoder.destroy();
    return -1;
}
```

### 发送流数据

```cpp
int send_stream(const uint8_t* data, uint32_t size,
                uint64_t timestamp = 0, int timeout = -1);
```

**功能：**
将编码的视频流数据发送到解码器进行处理。

**参数说明：**

| 参数名       | 类型         | 描述                          |
| --------- | ---------- | --------------------------- |
| data      | `const uint8_t*` | 指向编码流数据的指针                 |
| size      | `uint32_t` | 编码流数据的大小（字节）              |
| timestamp | `uint64_t` | 显示时间戳（默认：0）               |
| timeout   | `int`      | 等待流提交的最大时间（毫秒，默认：-1，无限等待） |

**返回值：**

* `0` - 成功
* `-1` - 失败

**状态要求：**
解码器必须处于 `STARTED` 状态。

**使用示例：**
```cpp
std::vector<uint8_t> stream_data = read_video_data();
if (decoder.send_stream(stream_data.data(), stream_data.size()) != 0) {
    std::cerr << "Failed to send stream data" << std::endl;
}
```

### 获取解码帧

```cpp
int get_frame(k_video_frame_info& frame, int timeout = -1);
```

**功能：**
从解码器获取解码后的视频帧数据。

**参数说明：**

| 参数名     | 类型                 | 描述                          |
| ------- | ------------------ | --------------------------- |
| frame   | `k_video_frame_info&` | 输出参数，存储获取的解码帧信息          |
| timeout | `int`              | 等待可用帧的最大时间（毫秒，默认：-1，无限等待） |

**返回值：**

* `0` - 成功获取帧
* `-1` - 失败或超时

**状态要求：**
解码器必须处于 `STARTED` 状态。

k_video_frame_info 主要成员详细说明：

| 结构体         | 成员名称               | 类型                  | 描述                                                                 |
|----------------|------------------------|-----------------------|----------------------------------------------------------------------|
| `k_video_frame_info` | `v_frame`            | `k_video_frame`       | 视频图像帧，包含当前帧的详细图像信息（如下述 `k_video_frame` 结构体内容）。 |
|                | `pool_id`              | `k_u32`               | 视频缓存池 ID，标识当前帧数据所在的视频缓存池，用于管理缓存资源的分配与释放。         |
|                | `mod_id`               | `k_mod_id`            | 生成视频帧的硬件逻辑模块标识，指示当前帧数据是由哪个硬件逻辑单元输出的。               |
| `k_video_frame` | `width`                | `k_u32`               | 图像宽度（像素），表示视频帧的水平像素数量。                         |
|                | `height`               | `k_u32`               | 图像高度（像素），表示视频帧的垂直像素数量。                         |
|                | `field`                | `k_video_field`       | 帧场模式，指示视频帧的场结构（如逐行、隔行等）。                     |
|                | `pixel_format`         | `k_pixel_format`      | 视频图像像素格式，定义像素的编码方式（如 YUV420、RGB 等）。          |
|                | `video_format`         | `k_video_format`      | 视频图像格式，描述视频的整体格式属性。                               |
|                | `dynamic_range`        | `k_dynamic_range`     | 动态范围，指示视频帧的亮度动态范围（如 SDR、HDR 等）。                |
|                | `compress_mode`        | `k_compress_mode`     | 视频压缩模式，说明图像数据的压缩方式。                               |
|                | `color_gamut`          | `k_color_gamut`       | 色域范围，定义视频帧的色彩空间范围（如 BT.601、BT.709 等）。          |
|                | `header_stride`        | `k_u32`               | 图像压缩头跨距，压缩头数据每行的字节数。                             |
|                | `stride`               | `k_u32`               | 图像数据跨距，图像数据每行实际占用的字节数（可能包含内存对齐部分）。 |
|                | `header_phys_addr`     | `k_u64`               | 压缩头物理地址，图像压缩头数据在物理内存中的地址。                   |
|                | `header_virt_addr`     | `k_u64`               | 压缩头虚拟地址（内核态），内核空间可访问的压缩头数据虚拟地址。       |
|                | `phys_addr`            | `k_u64`               | 图像数据物理地址，图像像素数据在物理内存中的地址。                   |
|                | `virt_addr`            | `k_u64`               | 图像数据虚拟地址（内核态），内核空间可访问的图像像素数据虚拟地址。   |
|                | `offset_top`           | `k_s16`               | 图像顶部剪裁宽度，视频帧顶部需要裁剪的像素数量。                     |
|                | `offset_bottom`        | `k_s16`               | 图像底部剪裁宽度，视频帧底部需要裁剪的像素数量。                     |
|                | `offset_left`          | `k_s16`               | 图像左侧裁剪宽度，视频帧左侧需要裁剪的像素数量。                     |
|                | `offset_right`         | `k_s16`               | 图像右侧裁剪宽度，视频帧右侧需要裁剪的像素数量。                     |
|                | `time_ref`             | `k_u32`               | 图像帧序列号，用于标识视频帧的顺序。                                 |
|                | `pts`                  | `k_u64`               | 图像时间戳，用于音视频同步的 Presentation Time Stamp。               |
|                | `priv_data`            | `k_u64`               | 私有数据，供内部或扩展使用的额外数据。                               |
|                | `supplement`           | `k_video_supplement`  | 图像的补充信息，包含图像的额外辅助数据。                             |

**使用示例：**
```cpp
k_video_frame_info frame;
if (decoder.get_frame(frame, 100) == 0) {
    std::cout << "Got frame: " << frame.v_frame.width << "x"
              << frame.v_frame.height << std::endl;
    // 处理帧数据...
    decoder.release_frame(frame); // 必须释放帧
}
```

### 释放帧

```cpp
int release_frame(k_video_frame_info& frame);
```

**功能：**
将解码帧缓冲区释放回解码器，供后续使用。

**参数说明：**

| 参数名  | 类型                 | 描述        |
| ---- | ------------------ | --------- |
| frame | `k_video_frame_info&` | 要释放的解码帧 |

**返回值：**

* `0` - 成功
* `-1` - 失败

**重要：**
获取帧后必须调用此方法释放，否则会导致内存泄漏和解码器阻塞。

**使用示例：**
```cpp
k_video_frame_info frame;
if (decoder.get_frame(frame) == 0) {
    // 处理帧数据...
    process_frame(frame);
    // 处理完成后必须释放
    decoder.release_frame(frame);
}
```

### 停止解码器

```cpp
int stop();
```

**功能：**
停止解码器操作，发送流结束信号并等待所有帧处理完成。

**返回值：**

* `0` - 成功
* `-1` - 失败

**状态要求：**
解码器必须处于 `STARTED` 状态。

**内部操作：**
1. 发送流结束（EOS）信号
2. 等待所有待处理帧解码完成
3. 停止解码通道

**使用示例：**
```cpp
if (decoder.stop() != 0) {
    std::cerr << "Failed to stop decoder" << std::endl;
}
```

### 销毁解码器

```cpp
int destroy();
```

**功能：**
完全销毁解码器实例，释放所有硬件资源和内存。

**返回值：**

* `0` - 成功
* `-1` - 失败

**内部操作：**
1. 如果正在运行，先调用 `stop()`
2. 销毁解码通道
3. 释放缓冲区池
4. 释放通道资源

**使用示例：**
```cpp
decoder.destroy(); // 显式销毁，或依赖析构函数自动调用
```

### 获取解码器通道ID

```cpp
int get_channel_id() const { return channel_id_; }
```

**功能：**
获取解码器的通道标识符。这个ID在解码器创建后由系统分配，用于标识唯一的解码通道。

**返回值：**

* 成功：返回解码器的通道ID（非负整数）
* 失败：返回 `K_INVALID_CHANNEL`（-1U），表示无效通道

**使用示例：**
```cpp
DecoderConfig config(VDEC_H264);
VideoDecoder decoder(config);

if (decoder.create() == 0) {
    int channel_id = decoder.get_channel_id();
    std::cout << "Decoder created successfully, channel ID: " << channel_id << std::endl;
}
```

## 完整使用示例

### 文件视频解码示例

```cpp
#include <fstream>
#include <iostream>
#include <vector>
#include <string>
#include <unistd.h>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <algorithm> // For std::transform
#include <iomanip>

#include <k230_vdec.h>

// Size of the read buffer for each read operation
const size_t READ_BUFFER_SIZE = 40960;

std::atomic<bool> g_decoder_running(true);

// Helper function to get file extension (lowercase)
static std::string get_file_extension(const std::string& file_path) {
    size_t dot_pos = file_path.find_last_of('.');
    if (dot_pos == std::string::npos || dot_pos == file_path.length() - 1) {
        return ""; // No extension
    }
    std::string ext = file_path.substr(dot_pos + 1);
    std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); // Convert to lowercase
    return ext;
}

// Helper function to determine decoder type from file extension
static DecoderType get_decoder_type_from_extension(const std::string& ext) {
    if (ext == "h264" || ext == "264") {
        return VDEC_H264;
    } else if (ext == "h265" || ext == "265" || ext == "hevc") {
        return VDEC_H265; // Assume VDEC_H265 is defined for H.265/HEVC
    } else if (ext == "mjpeg" || ext == "mjpg") {
        return VDEC_JPEG; // Assume VDEC_MPEG is defined for MPEG
    }
    return VDEC_H264;
}

// Decoding frame processing thread function
static void frame_processing_thread(VideoDecoder* decoder)
{
    if (!decoder) {
        std::cerr << "Error: Invalid decoder pointer in processing thread" << std::endl;
        return;
    }

    int frame_count = 0;
    while (g_decoder_running) {

        // Continuously get decoded frames until there are no more frames
        k_video_frame_info frame;
        int ret;
        do {
            ret = decoder->get_frame(frame, 100);
            if (ret == 0) {
                frame_count++;
                std::cout << "[" << frame_count << "]:"
                        << "Obtained decoded frame - Width: " << frame.v_frame.width
                        << ", Height: " << frame.v_frame.height
                        << ", Format: " << frame.v_frame.pixel_format
                        << ", phy[0]: 0x" << std::hex <<  frame.v_frame.phys_addr[0]
                        << ", phy[1]: 0x" << std::hex << frame.v_frame.phys_addr[1]
                        << std::dec << std::endl;

                // Release frame buffer
                if (decoder->release_frame(frame) != 0) {
                    std::cerr << "Warning: Failed to release frame buffer" << std::endl;
                }
            }
        } while (ret == 0);
    }

    std::cout << "Frame processing thread exited" << std::endl;
}

// Modified function: Automatically determine decoder type by file extension
static int file_video_decoder(const char* file_path) {
    int ret = -1;

    if (!file_path) {
        std::cerr << "Error: File path is empty" << std::endl;
        return -1;
    }

    // Get file extension and determine decoder type
    std::string ext = get_file_extension(file_path);
    DecoderType dec_type = get_decoder_type_from_extension(ext);

    if (dec_type == VDEC_UNKNOWN) {
        std::cerr << "Error: Unsupported file format (extension: ." << ext << ")" << std::endl;
        return -1;
    }
    std::cout << "Detected file format: ." << ext << ", using decoder type: " << dec_type << std::endl;

    try {
        // Configure decoder parameters
        DecoderConfig config(dec_type);

        // Create decoder instance
        VideoDecoder decoder(config);

        // Initialize decoder
        if (decoder.create() != 0) {
            std::cerr << "Error: Failed to create decoder" << std::endl;
            return -1;
        }

        // Start decoder
        if (decoder.start() != 0) {
            std::cerr << "Error: Failed to start decoder" << std::endl;
            decoder.destroy();
            return -1;
        }

        // Create and start frame processing thread
        std::thread processing_thread(frame_processing_thread, &decoder);

        // Open file
        std::ifstream file(file_path, std::ios::binary);
        if (!file.is_open()) {
            std::cerr << "Error: Unable to open file: " << file_path << std::endl;
            // Notify thread to exit
            g_decoder_running = false;
            processing_thread.join();
            decoder.stop();
            decoder.destroy();
            return -1;
        }

        // Read file and send data to decoder (main thread)
        std::vector<uint8_t> read_buffer(READ_BUFFER_SIZE);
        size_t total_read = 0;

        while (file) {
            file.read(reinterpret_cast<char*>(read_buffer.data()), READ_BUFFER_SIZE);
            size_t bytes_read = file.gcount();

            if (bytes_read == 0) {
                break;
            }

            total_read += bytes_read;
            std::cout << "Read data: " << bytes_read << " bytes, total: " << total_read << " bytes" << std::endl;

            // Send data to decoder
            ret = decoder.send_stream(read_buffer.data(), bytes_read);
            if (ret != 0) {
                std::cerr << "Error: Failed to send stream data to decoder" << std::endl;
                break;
            }

        }

        std::cout << "File reading completed, total size: " << total_read << " bytes" << std::endl;

        // End of decoding
        g_decoder_running = false;

        // Wait for processing thread to complete
        if (processing_thread.joinable()) {
            processing_thread.join();
        }

        // Stop and destroy decoder
        decoder.stop();
        decoder.destroy();

        // Reset global variable
        g_decoder_running = true;

        return 0; // Success

    } catch (const std::exception& e) {
        std::cerr << "Exception occurred: " << e.what() << std::endl;
        g_decoder_running = false;
        return -1;
    } catch (...) {
        std::cerr << "Unknown exception occurred" << std::endl;
        g_decoder_running = false;
        return -1;
    }
}

void setup()
{
    file_video_decoder("/data/test.264");
}

void loop()
{

}
```

---

## 注意事项

1. **生命周期管理**：必须按照 `create() → start() → send_stream()/get_frame() → stop() → destroy()` 的顺序使用解码器。

2. **帧释放**：每次成功调用 `get_frame()` 后必须调用 `release_frame()`，否则会导致内存泄漏。

3. **线程安全**：`send_stream()` 与 `get_frame()` 支持在不同线程中并发调用，且保证线程安全。需要注意的是，`send_stream()` 返回成功仅代表数据已成功提交给解码器，并不意味着对应流已完成解码；解码后的帧数据需通过 `get_frame()` 另行获取，其获取时机与流数据提交操作相互独立，无法通过 `send_stream()` 的返回状态预判。
