Files
MeowOS-For-R3P2/patch_v2/ncm_app/app/play/decoder.c
T
MeowOS 70aec677ce ncm: FLAC 解码落地(dr_flac 0.13.4 单头入库) + 缓存键=歌曲ID+音质档位
- 解码分发 decoder.c:magic(fLaC→dr_flac / 其它→minimp3);flac ≤16bit→S16、
  >16bit→S32 满幅送 audiod(AUDIO_FMT_S32),rate/ch/bits 按实际流,零 SRC 零裁剪
- song_url_auto 接受 flac(type 权威/url 扩展名兜底),降级语义不变;flac 下载限幅 1GB
- 缓存键 song_<id>_<level>.<ext>:同歌同档命中不重下,换档新条目旧档留 LRU;
  ext 前导点正名(修双点名),lookup 回退历史双点名+旧无档键温和兼容
- audio_out S32 通路(AUDIOD/ALSA/WAV)+ 流死重开按 bits 还原格式
- 验证(0 真实请求):44.1k/16 与 96k/24 自制样本宿主+qemu 帧数/时长精确、
  逐样本零误差;mp3 回归逐帧一致;player_flow_sim P1-P6(新 P6 档位键/flac ext)、
  cache_evict/cover/pagination/window/audiod_chain sim 全 PASS;mipsel 0 warning
- REPORT §19 / VENDOR_NOTES dr_flac 条目;未部署真机(留主会话)
2026-09-01 14:14:38 +08:00

142 lines
5.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* play/decoder.c — 格式分发解码实现(接口见 decoder.h,REPORT §19)。
*
* mp3 细节:minimp3_ex 的 dec.info(hz/channels)在首次 mp3dec_ex_read 后才保证有效,
* 而调用方开 audio_out 前就要 rate/ch —— open 时先预读一批进 stash,read 先吐 stash。
* seek(mp3):关+重开(MP3D_SEEK_TO_BYTE 索引在 open 时重建)再按字节近似 seek,同旧语义。
*/
#include "decoder.h"
#include "minimp3_ex.h"
#include "dr_flac.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEC_STASH_FRAMES 4096 /* open 预读上限(帧),与播放循环单块读同量级 */
enum { DEC_MP3 = 0, DEC_FLAC = 1 };
struct ncm_decoder {
int kind;
char path[512];
int rate, ch, bits;
int64_t total_frames; /* 0=未知 */
/* mp3 */
mp3dec_ex_t mp3;
int16_t *stash; /* 预读样本(交织 s16),stash_n 个样本 */
size_t stash_n, stash_off;
/* flac */
drflac *flac;
};
/* magic 探测:首 4 字节 "fLaC" → flac;其余(ID3 / 0xFF 帧同步 / 未知)→ mp3
*(未知格式留给 minimp3 在 open 时报错,不在本层加第三种分支)。 */
static int sniff_kind(const char *path)
{
unsigned char m[4] = {0};
FILE *f = fopen(path, "rb");
if (f) { size_t n = fread(m, 1, 4, f); fclose(f); if (n < 4) return DEC_MP3; }
if (m[0] == 'f' && m[1] == 'L' && m[2] == 'a' && m[3] == 'C') return DEC_FLAC;
return DEC_MP3;
}
int ncm_dec_open(ncm_decoder_t **out, const char *path,
int *rate, int *ch, int *bits, int64_t *total_frames)
{
if (!out || !path) return -1;
ncm_decoder_t *d = (ncm_decoder_t *)calloc(1, sizeof *d);
if (!d) return -1;
d->kind = sniff_kind(path);
snprintf(d->path, sizeof d->path, "%s", path);
if (d->kind == DEC_FLAC) {
d->flac = drflac_open_file(path, NULL);
if (!d->flac) { free(d); return -1; }
d->rate = (int)d->flac->sampleRate;
d->ch = (int)d->flac->channels;
/* 位宽映射(AUDIOD §4 归一交给 SDK/audiod):≤16 → S16;>16 → S32 满幅 */
d->bits = (d->flac->bitsPerSample <= 16) ? 16 : 32;
d->total_frames = (int64_t)d->flac->totalPCMFrameCount;
} else {
if (mp3dec_ex_open(&d->mp3, path, MP3D_SEEK_TO_BYTE) != 0) { free(d); return -1; }
d->bits = 16;
d->stash = (int16_t *)malloc(DEC_STASH_FRAMES * 2 * sizeof(int16_t));
if (!d->stash) { mp3dec_ex_close(&d->mp3); free(d); return -1; }
/* 预读首块:填 dec.info(hz/channels)+ 预热 stash */
size_t got = mp3dec_ex_read(&d->mp3, d->stash, DEC_STASH_FRAMES * 2);
d->stash_n = got;
d->rate = d->mp3.info.hz;
d->ch = d->mp3.info.channels;
if (d->rate <= 0 || d->ch <= 0) { /* 首块即失败/空流 */
mp3dec_ex_close(&d->mp3); free(d->stash); free(d); return -1;
}
d->total_frames = (d->ch > 0) ? (int64_t)(d->mp3.samples / (uint64_t)d->ch) : 0;
}
if (d->ch < 1 || d->ch > 2) { ncm_dec_close(d); return -1; } /* v1 只接 单/双声道 */
*out = d;
if (rate) *rate = d->rate;
if (ch) *ch = d->ch;
if (bits) *bits = d->bits;
if (total_frames) *total_frames = d->total_frames;
return 0;
}
int64_t ncm_dec_read(ncm_decoder_t *d, void *buf, int64_t cap_frames)
{
if (!d || !buf || cap_frames <= 0) return 0;
if (d->kind == DEC_FLAC) {
if (d->bits == 16)
return (int64_t)drflac_read_pcm_frames_s16(d->flac, (drflac_uint64)cap_frames,
(drflac_int16 *)buf);
return (int64_t)drflac_read_pcm_frames_s32(d->flac, (drflac_uint64)cap_frames,
(drflac_int32 *)buf);
}
/* mp3:先吐 stash,再直读 */
int16_t *out = (int16_t *)buf;
int64_t cap_samples = cap_frames * d->ch;
int64_t done = 0;
if (d->stash_off < d->stash_n) {
size_t left = d->stash_n - d->stash_off;
size_t take = left < (size_t)cap_samples ? left : (size_t)cap_samples;
memcpy(out, d->stash + d->stash_off, take * sizeof(int16_t));
d->stash_off += take;
done += (int64_t)take;
}
if (done < cap_samples) {
size_t got = mp3dec_ex_read(&d->mp3, out + done, (size_t)(cap_samples - done));
done += (int64_t)got;
}
return done / d->ch;
}
int ncm_dec_seek_ms(ncm_decoder_t *d, int64_t ms, int64_t file_bytes, int64_t dur_ms)
{
if (!d || ms < 0) return -1;
if (d->kind == DEC_FLAC) {
drflac_uint64 frame = (drflac_uint64)(ms * d->rate / 1000);
return drflac_seek_to_pcm_frame(d->flac, frame) ? 0 : -1;
}
/* mp3:关+重开(重建字节索引)再按字节近似 */
mp3dec_ex_close(&d->mp3);
memset(&d->mp3, 0, sizeof d->mp3);
if (mp3dec_ex_open(&d->mp3, d->path, MP3D_SEEK_TO_BYTE) != 0) return -1;
d->stash_n = d->stash_off = 0;
int64_t target = (file_bytes > 0 && dur_ms > 0) ? file_bytes * ms / dur_ms : 0;
return mp3dec_ex_seek(&d->mp3, (uint64_t)(target > 0 ? target : 0));
}
void ncm_dec_close(ncm_decoder_t *d)
{
if (!d) return;
if (d->kind == DEC_FLAC) { if (d->flac) drflac_close(d->flac); }
else { mp3dec_ex_close(&d->mp3); free(d->stash); }
free(d);
}
const char *ncm_dec_format(const ncm_decoder_t *d)
{
if (!d) return "?";
return d->kind == DEC_FLAC ? "flac" : "mp3";
}