JPEG硬件解码
使用了DMA2D做调配
#ifndef JPEG_DECODE_H
#define JPEG_DECODE_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* 色度采样。解码方向不预设格式:Probe 从 SOF0 里读出实际值再决定,
* 4:4:4 / 4:2:2 / 4:2:0 都接受。本机编码器(jpeg_encode.c)输出的是 4:2:0。 */
typedef enum {
JPEG_DECODE_SUBSAMPLING_444 = 0,
JPEG_DECODE_SUBSAMPLING_420 = 1,
JPEG_DECODE_SUBSAMPLING_422 = 2
} JpegDecodeSubsampling_t;
typedef struct {
uint16_t width;
uint16_t height;
JpegDecodeSubsampling_t subsampling;
} JpegDecodeInfo_t;
/* 解析 JPEG 头,校验 MH2457 JPEGD 的限制,并定位第一个 EOI。
*
* jpeg JPEG 数据
* bufferSize 可读字节数
* info 收到 SOF0 里的尺寸与色度采样
* jpegSize 收到"首个 EOI 结尾"的字节数(供 DMA 定长传输用)
*
* 返回 false 表示不是合法 JPEG,或含 JPEGD 不支持的标记(APP1 / SOF2 等)。 */
bool JpegDecode_Probe(const uint8_t *jpeg, size_t bufferSize,
JpegDecodeInfo_t *info, size_t *jpegSize);
/* JPEGD + DMA2 解码成 YCbCr,再用 DMA2D PFC 转 ARGB8888。
*
* jpeg/jpegSize/jpegCapacity 输入缓冲、有效长度、可写容量
* yuv/yuvCapacity 中间 YCbCr 缓冲(JPEGD 输出,16 对齐行距)
* argb/argbPixels 输出 ARGB8888 缓冲,容量以像素计
* info 收到实际解码尺寸(复用 Probe 的结果)
*
* 三个缓冲都必须 4 字节对齐;jpegCapacity 需比 jpegSize 多出最多 3 字节
* (函数内部会补齐到 4 字节边界再做 DMA)。 */
bool JpegDecode_ToArgb8888(uint8_t *jpeg, size_t jpegSize, size_t jpegCapacity,
uint8_t *yuv, size_t yuvCapacity, uint32_t *argb,
size_t argbPixels, JpegDecodeInfo_t *info);
#ifdef __cplusplus
}
#endif
#endif /* JPEG_DECODE_H */
#include "jpeg_decode.h"
#include <string.h>
#ifndef JPEG_DECODE_HOST_TEST
#include "mh2457.h"
#include "mh245x_dma.h"
#include "mh245x_dma2d.h"
#include "mh245x_jpeg.h"
#include "peripheral.h"
#endif
static uint16_t be16(const uint8_t *p) { return (uint16_t)(((uint16_t)p[0] << 8) | p[1]); }
bool JpegDecode_Probe(const uint8_t *jpeg, size_t bufferSize,
JpegDecodeInfo_t *info, size_t *jpegSize)
{
size_t pos = 2U;
bool dqtSeen = false, sofSeen = false;
JpegDecodeInfo_t found = {0U, 0U, JPEG_DECODE_SUBSAMPLING_444};
if (jpegSize != NULL)
*jpegSize = 0U;
if (jpeg == NULL || info == NULL || jpegSize == NULL || bufferSize < 4U ||
jpeg[0] != 0xffU || jpeg[1] != 0xd8U)
return false;
while (pos + 3U < bufferSize)
{
uint8_t marker;
uint16_t length;
while (pos < bufferSize && jpeg[pos] != 0xffU)
++pos;
while (pos < bufferSize && jpeg[pos] == 0xffU)
++pos;
if (pos >= bufferSize)
return false;
marker = jpeg[pos++];
if (marker == 0xd9U)
{
*jpegSize = pos;
*info = found;
return sofSeen;
}
if (marker == 0x00U || (marker >= 0xd0U && marker <= 0xd7U))
continue;
if (pos + 2U > bufferSize)
return false;
length = be16(jpeg + pos);
if (length < 2U || pos + length > bufferSize)
return false;
if (marker == 0xe1U || marker == 0xc2U)
return false; /* JPEGD restrictions */
if (marker == 0xdbU)
{
if (sofSeen)
return false;
dqtSeen = true;
}
else if (marker == 0xc0U)
{
const uint8_t *s = jpeg + pos + 2U;
if (!dqtSeen || length < 17U || s[0] != 8U || s[5] != 3U)
return false;
found.height = be16(s + 1U);
found.width = be16(s + 3U);
if (s[7] == 0x22U)
found.subsampling = JPEG_DECODE_SUBSAMPLING_420;
else if (s[7] == 0x21U || s[7] == 0x12U)
found.subsampling = JPEG_DECODE_SUBSAMPLING_422;
else if (s[7] == 0x11U)
found.subsampling = JPEG_DECODE_SUBSAMPLING_444;
else
return false;
sofSeen = true;
}
else if (marker == 0xdaU)
{
size_t i = pos + length;
if (!sofSeen)
return false;
while (i + 1U < bufferSize)
{
if (jpeg[i] != 0xffU)
{
++i;
continue;
}
if (jpeg[i + 1U] == 0x00U ||
(jpeg[i + 1U] >= 0xd0U && jpeg[i + 1U] <= 0xd7U))
{
i += 2U;
continue;
}
if (jpeg[i + 1U] == 0xd9U)
{
*jpegSize = i + 2U;
*info = found;
return true;
}
return false;
}
return false;
}
pos += length;
}
return false;
}
#define JPEG_DMA_WAIT_GUARD 80000000UL
#define JPEG_DMA2D_WAIT_GUARD 80000000UL
#ifndef JPEG_DECODE_HOST_TEST
bool JpegDecode_ToArgb8888(uint8_t *jpeg, size_t jpegSize,
size_t jpegCapacity, uint8_t *yuv,
size_t yuvCapacity, uint32_t *argb,
size_t argbPixels, JpegDecodeInfo_t *info)
{
DMA_InitTypeDef d;
JPEG_InfoTypeDef hw;
size_t actualSize, paddedSize, worstYuv;
uint32_t guard;
if (jpeg == NULL || yuv == NULL || argb == NULL || info == NULL ||
(((uintptr_t)jpeg | (uintptr_t)yuv | (uintptr_t)argb) & 3U) != 0U)
return false;
if (!JpegDecode_Probe(jpeg, jpegSize, info, &actualSize))
return false;
if (info->width == 0U || info->height == 0U ||
(size_t)info->width * info->height > argbPixels)
return false;
paddedSize = (actualSize + 3U) & ~(size_t)3U;
worstYuv = (size_t)((info->width + 15U) & ~15U) * info->height * 3U;
if (paddedSize > jpegCapacity || worstYuv > yuvCapacity)
return false;
memset(jpeg + actualSize, 0, paddedSize - actualSize);
PeripheralEnable(PeripheralJPEGD, 1);
PeripheralEnable(PeripheralDMA2, 1);
JPEG_Cmd(DISABLE);
PeripheralReset(PeripheralJPEGD);
DMA_Cmd(DMA2_Stream0, DISABLE);
DMA_Cmd(DMA2_Stream5, DISABLE);
guard = JPEG_DMA_WAIT_GUARD;
while ((DMA_GetCmdStatus(DMA2_Stream0) || DMA_GetCmdStatus(DMA2_Stream5)) && --guard)
{
}
if (guard == 0U)
return false;
DMA_ClearFlag(DMA2_Stream0, DMA_FLAG_ALL);
DMA_ClearFlag(DMA2_Stream5, DMA_FLAG_ALL);
DMA_StructInit(&d);
d.DMA_Channel = DMA_Channel_0;
d.DMA_BufferSize = 1U;
d.DMA_DIR = DMA_DIR_MemoryToPeripheral;
d.DMA_FIFOMode = DMA_FIFOMode_Enable;
d.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
d.DMA_Memory0BaseAddr = (uint32_t)jpeg;
d.DMA_MemoryBurst = DMA_MemoryBurst_INC4;
d.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
d.DMA_MemoryInc = DMA_MemoryInc_Enable;
d.DMA_Mode = DMA_Mode_Normal;
d.DMA_PeripheralBaseAddr = (uint32_t)&JPEGD->DIR;
d.DMA_PeripheralBurst = DMA_PeripheralBurst_INC16;
d.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
d.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
d.DMA_Priority = DMA_Priority_Medium;
DMA_Init(DMA2_Stream5, &d);
d.DMA_Channel = DMA_Channel_7;
d.DMA_DIR = DMA_DIR_PeripheralToMemory;
d.DMA_Memory0BaseAddr = (uint32_t)yuv;
d.DMA_PeripheralBaseAddr = (uint32_t)&JPEGD->DOR;
DMA_Init(DMA2_Stream0, &d);
DMA_FlowControllerConfig(DMA2_Stream0, DMA_FlowCtrl_Peripheral);
DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, DISABLE);
DMA_ITConfig(DMA2_Stream5, DMA_IT_TC, DISABLE);
JPEG_SetInFifoThreshold(16U);
JPEG_SetOutFifoThreshold(16U);
DMA2_Stream5->M0AR = (uint32_t)jpeg;
DMA2_Stream5->NDTR = (uint32_t)paddedSize;
DMA2_Stream0->M0AR = (uint32_t)yuv;
DMA2_Stream0->NDTR = 0U;
JPEG_Cmd(ENABLE);
DMA_Cmd(DMA2_Stream0, ENABLE);
DMA_Cmd(DMA2_Stream5, ENABLE);
guard = JPEG_DMA_WAIT_GUARD;
while (DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_TCIF) == RESET && --guard)
{
if (JPEG_GetFlagStatus(JPEG_FLAG_CFGERR) != RESET)
break;
}
if (guard == 0U || JPEG_GetFlagStatus(JPEG_FLAG_CFGERR) != RESET)
{
JPEG_Cmd(DISABLE);
DMA_Cmd(DMA2_Stream0, DISABLE);
DMA_Cmd(DMA2_Stream5, DISABLE);
return false;
}
JPEG_GetInfo(&hw);
JPEG_Cmd(DISABLE);
DMA_Cmd(DMA2_Stream0, DISABLE);
DMA_Cmd(DMA2_Stream5, DISABLE);
if (hw.ImageWidth != info->width || hw.ImageHeight != info->height)
return false;
PeripheralEnable(PeripheralDMA2D, 1);
DMA2D_DisableIT_TC(DMA2D);
guard = JPEG_DMA2D_WAIT_GUARD;
while (DMA2D_IsTransferOngoing(DMA2D) && --guard)
{
}
if (guard == 0U)
return false;
DMA2D_SetMode(DMA2D, DMA2D_MODE_M2M_PFC);
DMA2D_SetLineOffsetMode(DMA2D, DMA2D_LINE_OFFSET_PIXELS);
DMA2D_SetOutputRotationMode(DMA2D, DMA2D_ROTATION_0);
DMA2D_FGND_SetColorMode(DMA2D, DMA2D_INPUT_MODE_YCBCR);
DMA2D_FGND_SetAlphaMode(DMA2D, DMA2D_ALPHA_MODE_REPLACE);
DMA2D_FGND_SetAlpha(DMA2D, 0xffU);
DMA2D_FGND_SetChrSubSampling(DMA2D,
hw.ChromaSubsampling == JPEG_420_SUBSAMPLING ? DMA2D_CSS_420 : (hw.ChromaSubsampling == JPEG_422_SUBSAMPLING ? DMA2D_CSS_422 : DMA2D_CSS_444));
DMA2D_FGND_SetLineOffset(DMA2D, (16U - (hw.ImageWidth & 15U)) & 15U);
DMA2D_FGND_SetMemAddr(DMA2D, (uint32_t)yuv);
DMA2D_SetOutputColorMode(DMA2D, DMA2D_OUTPUT_MODE_ARGB8888);
DMA2D_SetOutputColor(DMA2D, 0xff000000UL);
DMA2D_SetOutputMemAddr(DMA2D, (uint32_t)argb);
DMA2D_SetLineOffset(DMA2D, 0U);
DMA2D_SetNbrOfPixelsPerLines(DMA2D, hw.ImageWidth);
DMA2D_SetNbrOfLines(DMA2D, hw.ImageHeight);
DMA2D_ClearFlag_TC(DMA2D);
DMA2D_ClearFlag_CE(DMA2D);
DMA2D_ClearFlag_TE(DMA2D);
DMA2D_Start(DMA2D);
guard = JPEG_DMA2D_WAIT_GUARD;
while (DMA2D_IsActiveFlag_TC(DMA2D) == 0U && --guard)
{
if (DMA2D_IsActiveFlag_CE(DMA2D) || DMA2D_IsActiveFlag_TE(DMA2D))
break;
}
if (guard == 0U || DMA2D_IsActiveFlag_CE(DMA2D) || DMA2D_IsActiveFlag_TE(DMA2D))
return false;
DMA2D_ClearFlag_TC(DMA2D);
return true;
}
#else
bool JpegDecode_ToArgb8888(uint8_t *jpeg, size_t jpegSize,
size_t jpegCapacity, uint8_t *yuv,
size_t yuvCapacity, uint32_t *argb,
size_t argbPixels, JpegDecodeInfo_t *info)
{
(void)jpeg;
(void)jpegSize;
(void)jpegCapacity;
(void)yuv;
(void)yuvCapacity;
(void)argb;
(void)argbPixels;
(void)info;
return false;
}
#endif