Initial commit: TXW82x FPV v2.7.0.7-42229 SDK + project sources
This commit is contained in:
741
sdk/lib/multimedia/mplayer/detector.c
Normal file
741
sdk/lib/multimedia/mplayer/detector.c
Normal file
@@ -0,0 +1,741 @@
|
||||
#include "basic_include.h"
|
||||
#include "lib/multimedia/msi.h"
|
||||
#include "txmplayer.h"
|
||||
|
||||
#include "basic_include.h"
|
||||
#include "lib/multimedia/msi.h"
|
||||
#include "txmplayer.h"
|
||||
|
||||
// 常量定义
|
||||
#define MIN_DETECTION_LENGTH 12
|
||||
#define BMP_HEADER_MIN_SIZE 6
|
||||
#define TIFF_HEADER_SIZE 8
|
||||
#define FLV_HEADER_SIZE 5
|
||||
#define TS_PACKET_SIZE 188
|
||||
#define M2TS_PACKET_SIZE 204
|
||||
|
||||
// 裸视频检测相关常量
|
||||
#define H264_NAL_UNIT_TYPE_SPS 7
|
||||
#define H264_NAL_UNIT_TYPE_PPS 8
|
||||
#define H264_NAL_UNIT_TYPE_IDR 5
|
||||
#define H264_NAL_UNIT_TYPE_NON_IDR 1
|
||||
|
||||
#define H265_NAL_UNIT_TYPE_VPS 32
|
||||
#define H265_NAL_UNIT_TYPE_SPS 33
|
||||
#define H265_NAL_UNIT_TYPE_PPS 34
|
||||
#define H265_NAL_UNIT_TYPE_IDR 19
|
||||
#define H265_NAL_UNIT_TYPE_NON_IDR 1
|
||||
|
||||
#define AV1_OBU_TYPE_SEQUENCE_HEADER 1
|
||||
#define AV1_OBU_TYPE_FRAME_HEADER 3
|
||||
#define AV1_OBU_TYPE_FRAME 6
|
||||
#define AV1_OBU_TYPE_TILE_GROUP 4
|
||||
|
||||
// 辅助宏:检查数据长度是否足够,并比较特征签名
|
||||
#define CHECK_SIGNATURE(data, len, sig, sig_len) \
|
||||
((len) >= (sig_len) && memcmp(data, sig, sig_len) == 0)
|
||||
|
||||
static uint8 check_signature_ignore_whitespace(const uint8_t *data, uint32_t len,
|
||||
const char *sig, uint32_t sig_len)
|
||||
{
|
||||
if (len < sig_len) return 0;
|
||||
|
||||
uint32_t data_pos = 0;
|
||||
while (data_pos < len && (data[data_pos] == ' ' || data[data_pos] == '\t' ||
|
||||
data[data_pos] == '\r' || data[data_pos] == '\n')) {
|
||||
data_pos++;
|
||||
}
|
||||
|
||||
if (len - data_pos < sig_len) return 0;
|
||||
|
||||
for (uint32_t i = 0; i < sig_len; i++) {
|
||||
if (data[data_pos + i] != (uint8_t)sig[i] &&
|
||||
data[data_pos + i] != (uint8_t)(sig[i] ^ 0x20)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const uint8_t *find_pattern(const uint8_t *haystack, uint32_t haystack_len,
|
||||
const uint8_t *needle, uint32_t needle_len)
|
||||
{
|
||||
if (haystack_len < needle_len) return NULL;
|
||||
|
||||
for (uint32_t i = 0; i <= haystack_len - needle_len; i++) {
|
||||
if (memcmp(haystack + i, needle, needle_len) == 0) {
|
||||
return haystack + i;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uint8 check_packet_structure(const uint8_t *data, uint32_t len,
|
||||
uint32_t packet_size, uint32_t min_packets)
|
||||
{
|
||||
if (len < packet_size * min_packets) return 0;
|
||||
|
||||
uint32_t valid_packets = 0;
|
||||
for (uint32_t i = 0; i < min_packets && i * packet_size < len; i++) {
|
||||
const uint8_t *packet = data + i * packet_size;
|
||||
if (packet[0] != 0x47) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 增强TS包检测:检查PID字段,排除空包
|
||||
if (packet_size == TS_PACKET_SIZE) {
|
||||
uint16_t pid = ((packet[1] & 0x1F) << 8) | packet[2];
|
||||
if ((pid & 0x1FFF) == 0x1FFF) { // PID=0x1FFF表示空包
|
||||
continue;
|
||||
}
|
||||
}
|
||||
valid_packets++;
|
||||
}
|
||||
|
||||
return valid_packets >= min_packets;
|
||||
}
|
||||
|
||||
// ==================== MP4格式检测函数 ====================
|
||||
static int32_t detect_mp4(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len < 8) return MEDIA_DTYPE_MAX;
|
||||
|
||||
// 检查MP4特征盒子
|
||||
size_t offset = 0;
|
||||
while (offset + 8 <= len && offset < 1024) { // 只检查前1KB
|
||||
uint32_t box_size = get_unaligned_be32(data + offset);
|
||||
uint32_t box_type = get_unaligned_be32(data + offset + 4);
|
||||
|
||||
// 安全的盒子大小检查
|
||||
if (box_size < 8 || box_size > len - offset) {
|
||||
break;
|
||||
}
|
||||
|
||||
// MP4关键盒子类型
|
||||
switch (box_type) {
|
||||
case 0x66747970: // "ftyp"
|
||||
case 0x6D6F6F76: // "moov"
|
||||
case 0x6D6F6F66: // "moof" - fMP4
|
||||
case 0x6D646174: // "mdat"
|
||||
return MEDIA_DTYPE_VIDEO_MP4;
|
||||
}
|
||||
|
||||
offset += box_size;
|
||||
}
|
||||
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
// ==================== 裸视频编码帧检测函数 ====================
|
||||
static int32_t detect_h264_raw(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len < 16) return MEDIA_DTYPE_MAX;
|
||||
|
||||
uint32_t start_code_count = 0;
|
||||
uint32_t sps_count = 0;
|
||||
uint32_t pps_count = 0;
|
||||
|
||||
// H.264 NAL单元通常以0x000001或0x00000001开头
|
||||
for (uint32_t i = 0; i <= len - 4; i++) {
|
||||
uint8 found_start_code = 0;
|
||||
uint32_t nal_offset = 0;
|
||||
|
||||
// 检查3字节起始码 0x000001
|
||||
if (i <= len - 3 && data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x01) {
|
||||
nal_offset = i + 3;
|
||||
found_start_code = 1;
|
||||
}
|
||||
// 检查4字节起始码 0x00000001
|
||||
else if (i <= len - 4 && data[i] == 0x00 && data[i + 1] == 0x00 &&
|
||||
data[i + 2] == 0x00 && data[i + 3] == 0x01) {
|
||||
nal_offset = i + 4;
|
||||
found_start_code = 1;
|
||||
}
|
||||
|
||||
if (found_start_code && nal_offset < len) {
|
||||
uint8_t nal_unit_type = data[nal_offset] & 0x1F;
|
||||
|
||||
// 检查是否为SPS、PPS或IDR帧等关键NAL单元
|
||||
switch (nal_unit_type) {
|
||||
case H264_NAL_UNIT_TYPE_SPS:
|
||||
sps_count++;
|
||||
break;
|
||||
case H264_NAL_UNIT_TYPE_PPS:
|
||||
pps_count++;
|
||||
break;
|
||||
case H264_NAL_UNIT_TYPE_IDR:
|
||||
case H264_NAL_UNIT_TYPE_NON_IDR:
|
||||
start_code_count++;
|
||||
break;
|
||||
}
|
||||
|
||||
// 更严格的条件:需要SPS+PPS+帧数据
|
||||
if (sps_count >= 1 && pps_count >= 1 && start_code_count >= 2) {
|
||||
return MEDIA_DTYPE_RAW_H264;
|
||||
}
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_h265_raw(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len < 16) return MEDIA_DTYPE_MAX;
|
||||
|
||||
uint32_t start_code_count = 0;
|
||||
uint32_t vps_count = 0;
|
||||
uint32_t sps_count = 0;
|
||||
uint32_t pps_count = 0;
|
||||
|
||||
// H.265 NAL单元也以0x000001或0x00000001开头
|
||||
for (uint32_t i = 0; i <= len - 4; i++) {
|
||||
uint8 found_start_code = 0;
|
||||
uint32_t nal_offset = 0;
|
||||
|
||||
// 检查3字节起始码 0x000001
|
||||
if (i <= len - 3 && data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x01) {
|
||||
nal_offset = i + 3;
|
||||
found_start_code = 1;
|
||||
}
|
||||
// 检查4字节起始码 0x00000001
|
||||
else if (i <= len - 4 && data[i] == 0x00 && data[i + 1] == 0x00 &&
|
||||
data[i + 2] == 0x00 && data[i + 3] == 0x01) {
|
||||
nal_offset = i + 4;
|
||||
found_start_code = 1;
|
||||
}
|
||||
|
||||
if (found_start_code && nal_offset < len) {
|
||||
uint8_t nal_unit_type = (data[nal_offset] >> 1) & 0x3F;
|
||||
|
||||
// 检查是否为VPS、SPS、PPS等关键NAL单元
|
||||
switch (nal_unit_type) {
|
||||
case H265_NAL_UNIT_TYPE_VPS:
|
||||
vps_count++;
|
||||
break;
|
||||
case H265_NAL_UNIT_TYPE_SPS:
|
||||
sps_count++;
|
||||
break;
|
||||
case H265_NAL_UNIT_TYPE_PPS:
|
||||
pps_count++;
|
||||
break;
|
||||
case H265_NAL_UNIT_TYPE_IDR:
|
||||
case H265_NAL_UNIT_TYPE_NON_IDR:
|
||||
start_code_count++;
|
||||
break;
|
||||
}
|
||||
|
||||
// 更严格的条件:需要VPS+SPS+PPS+帧数据
|
||||
if (vps_count >= 1 && sps_count >= 1 && pps_count >= 1 && start_code_count >= 2) {
|
||||
return MEDIA_DTYPE_RAW_H265;
|
||||
}
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_av1_raw(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len < 8) return MEDIA_DTYPE_MAX;
|
||||
|
||||
uint32_t valid_obu_count = 0;
|
||||
|
||||
// AV1帧通常以OBU(Open Bitstream Unit)开始
|
||||
for (uint32_t i = 0; i <= len - 2; i++) {
|
||||
uint8_t obu_header = data[i];
|
||||
|
||||
// 检查OBU头格式:bit4-7必须为0
|
||||
if ((obu_header & 0xF0) != 0x00) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t obu_type = obu_header & 0x0F; // 实际OBU类型在bit0-3
|
||||
//uint8_t has_extension = (obu_header & 0x08) ? 1 : 0; // 实际在bit3
|
||||
//uint8_t has_size = (obu_header & 0x04) ? 1 : 0; // 实际在bit2
|
||||
|
||||
// 验证OBU类型
|
||||
if (obu_type == AV1_OBU_TYPE_SEQUENCE_HEADER ||
|
||||
obu_type == AV1_OBU_TYPE_FRAME_HEADER ||
|
||||
obu_type == AV1_OBU_TYPE_FRAME ||
|
||||
obu_type == AV1_OBU_TYPE_TILE_GROUP) {
|
||||
valid_obu_count++;
|
||||
|
||||
// 需要更多证据来确认
|
||||
if (valid_obu_count >= 3) {
|
||||
return MEDIA_DTYPE_RAW_AV1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_vp9_raw(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len < 8) return MEDIA_DTYPE_MAX;
|
||||
|
||||
uint32_t frame_count = 0;
|
||||
|
||||
// VP9帧头检测
|
||||
for (uint32_t i = 0; i <= len - 3; i++) {
|
||||
// VP9帧头特征:第0字节bit0=1表示帧开始,bit1=错误标记,bit2=帧内,bit3=重置上下文
|
||||
// bit4~6=版本号,bit7=保留
|
||||
if ((data[i] & 0x80) == 0x00) { // 保留位必须为0
|
||||
uint8_t version = (data[i] >> 4) & 0x07;
|
||||
//uint8 show_frame = (data[i] & 0x08) != 0;
|
||||
|
||||
// 有效的VP9版本和合理的帧头
|
||||
if (version <= 4) { // VP9版本0-4
|
||||
frame_count++;
|
||||
// 找到3个有效的帧头才认为是VP9流
|
||||
if (frame_count >= 3) {
|
||||
return MEDIA_DTYPE_RAW_VP9;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_vp8_raw(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len < 8) return MEDIA_DTYPE_MAX;
|
||||
|
||||
uint32_t frame_count = 0;
|
||||
|
||||
// VP8帧头检测
|
||||
for (uint32_t i = 0; i <= len - 6; i++) {
|
||||
// VP8帧头特征:第0字节bit0~2=版本,bit3=显示标记,bit4~7=保留
|
||||
if ((data[i] & 0xF0) == 0x90) { // 检查帧头特征
|
||||
uint8_t version = data[i] & 0x07;
|
||||
|
||||
// 检查关键帧标记(第3字节bit0)
|
||||
//uint8 key_frame = (data[i + 3] & 0x01) == 0;
|
||||
|
||||
if (version <= 3) { // VP8版本0-3
|
||||
frame_count++;
|
||||
// 找到3个有效的帧头才认为是VP8流
|
||||
if (frame_count >= 3) {
|
||||
return MEDIA_DTYPE_RAW_VP8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_mpeg2_raw(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len < 16) return MEDIA_DTYPE_MAX;
|
||||
|
||||
uint32_t start_code_count = 0;
|
||||
uint32_t sequence_header_count = 0;
|
||||
|
||||
// MPEG-2起始码:0x000001 + 起始码值
|
||||
for (uint32_t i = 0; i <= len - 4; i++) {
|
||||
if (data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x01) {
|
||||
uint8_t start_code = data[i + 3];
|
||||
|
||||
// 检查常见的MPEG-2起始码
|
||||
if (start_code == 0x00) { // 图片起始码
|
||||
start_code_count++;
|
||||
} else if (start_code == 0xB3) { // 序列头
|
||||
sequence_header_count++;
|
||||
} else if (start_code == 0xB8) { // GOP头
|
||||
start_code_count++;
|
||||
} else if (start_code >= 0x01 && start_code <= 0xAF) { // 切片起始码
|
||||
start_code_count++;
|
||||
}
|
||||
|
||||
// 需要序列头和足够的起始码
|
||||
if (sequence_header_count >= 1 && start_code_count >= 3) {
|
||||
return MEDIA_DTYPE_RAW_MPEG2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_mpeg4_raw(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len < 16) return MEDIA_DTYPE_MAX;
|
||||
|
||||
uint32_t start_code_count = 0;
|
||||
uint32_t vol_count = 0;
|
||||
|
||||
// MPEG-4 Visual起始码:0x000001Bx
|
||||
for (uint32_t i = 0; i <= len - 4; i++) {
|
||||
if (data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x01) {
|
||||
uint8_t start_code = data[i + 3];
|
||||
|
||||
// MPEG-4 Visual对象起始码范围
|
||||
if (start_code >= 0x20 && start_code <= 0x2F) {
|
||||
if (start_code == 0x20) { // 视频对象序列
|
||||
vol_count++;
|
||||
}
|
||||
start_code_count++;
|
||||
|
||||
// 需要VOL和足够的起始码
|
||||
if (vol_count >= 1 && start_code_count >= 3) {
|
||||
return MEDIA_DTYPE_RAW_MPEG4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
// ==================== 图片格式检测函数 ====================
|
||||
static int32_t detect_png(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
static const uint8_t png_sig[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||
return CHECK_SIGNATURE(data, len, png_sig, sizeof(png_sig)) ?
|
||||
MEDIA_DTYPE_PIC_PNG : MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_jpg(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len >= 4 && data[0] == 0xFF && data[1] == 0xD8) {
|
||||
if (data[2] == 0xFF && (data[3] & 0xF0) == 0xE0) {
|
||||
return MEDIA_DTYPE_PIC_JPG;
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_gif(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len >= 6 && (memcmp(data, "GIF87a", 6) == 0 || memcmp(data, "GIF89a", 6) == 0)) {
|
||||
return MEDIA_DTYPE_PIC_GIF;
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_bmp(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len >= BMP_HEADER_MIN_SIZE && data[0] == 0x42 && data[1] == 0x4D) {
|
||||
uint32_t file_size = get_unaligned_le32(data + 2);
|
||||
// 修复BMP文件大小判断逻辑
|
||||
if (file_size > 54 && file_size <= len) { // 最小BMP文件至少54字节
|
||||
return MEDIA_DTYPE_PIC_BMP;
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_webp(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
// 修复WebP检测逻辑,排除size=0的无效文件
|
||||
if (len >= 16 && memcmp(data, "RIFF", 4) == 0 && memcmp(data + 8, "WEBP", 4) == 0) {
|
||||
uint32_t riff_size = get_unaligned_le32(data + 4);
|
||||
if (riff_size > 8 && riff_size + 8 <= len) {
|
||||
return MEDIA_DTYPE_PIC_WEBP;
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_tiff(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len < TIFF_HEADER_SIZE) return MEDIA_DTYPE_MAX;
|
||||
|
||||
uint8 is_tiff = 0;
|
||||
if (data[0] == 0x49 && data[1] == 0x49 && data[2] == 0x2A && data[3] == 0x00) {
|
||||
uint32_t ifd_offset = get_unaligned_le32(data + 4);
|
||||
is_tiff = (ifd_offset >= 8 && ifd_offset < len);
|
||||
} else if (data[0] == 0x4D && data[1] == 0x4D && data[2] == 0x00 && data[3] == 0x2A) {
|
||||
uint32_t ifd_offset = get_unaligned_be32(data + 4);
|
||||
is_tiff = (ifd_offset >= 8 && ifd_offset < len);
|
||||
}
|
||||
|
||||
return is_tiff ? MEDIA_DTYPE_PIC_TIFF : MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_svg(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len >= 5) {
|
||||
if (check_signature_ignore_whitespace(data, len, "<?xml", 5) ||
|
||||
check_signature_ignore_whitespace(data, len, "<svg", 4)) {
|
||||
return MEDIA_DTYPE_PIC_SVG;
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
// ==================== 音频格式检测函数 ====================
|
||||
static int32_t detect_mp3(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len < 10) return MEDIA_DTYPE_MAX;
|
||||
|
||||
// ID3v2标签
|
||||
if (memcmp(data, "ID3", 3) == 0 && data[3] <= 0x04) {
|
||||
return MEDIA_DTYPE_AUDIO_MP3;
|
||||
}
|
||||
|
||||
// MPEG音频帧
|
||||
if (data[0] == 0xFF && (data[1] & 0xE0) == 0xE0) {
|
||||
uint8_t version = (data[1] >> 3) & 0x03;
|
||||
uint8_t layer = (data[1] >> 1) & 0x03;
|
||||
uint8_t bitrate_index = (data[2] >> 4) & 0x0F;
|
||||
uint8_t freq_index = (data[2] >> 2) & 0x03;
|
||||
|
||||
if (version != 0x01 && layer != 0x00 && bitrate_index != 0x0F && freq_index != 0x03) {
|
||||
return MEDIA_DTYPE_AUDIO_MP3;
|
||||
}
|
||||
}
|
||||
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_wav(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len >= 16 && memcmp(data, "RIFF", 4) == 0 && memcmp(data + 8, "WAVE", 4) == 0) {
|
||||
if (memcmp(data + 12, "fmt ", 4) == 0) {
|
||||
return MEDIA_DTYPE_AUDIO_WAV;
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_flac(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
return CHECK_SIGNATURE(data, len, "fLaC", 4) ?
|
||||
MEDIA_DTYPE_AUDIO_FLAC : MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_aac(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
// 修复AAC检测逻辑,仅校验同步字
|
||||
if (len >= 7 && data[0] == 0xFF && (data[1] & 0xF0) == 0xF0) {
|
||||
uint8_t freq_idx = (data[2] >> 2) & 0x0F;
|
||||
if (freq_idx <= 0x0C) { // 有效的频率索引
|
||||
return MEDIA_DTYPE_AUDIO_AAC;
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_ogg(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len >= 5 && memcmp(data, "OggS", 4) == 0 && data[4] == 0x00) {
|
||||
return MEDIA_DTYPE_AUDIO_OGG;
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_ape(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len >= 8 && memcmp(data, "APE ", 4) == 0) {
|
||||
uint16_t version = get_unaligned_le16(data + 4);
|
||||
if (version == 0x0F8C || version == 0x0F96) {
|
||||
return MEDIA_DTYPE_AUDIO_APE;
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
// ==================== 视频格式检测函数 ====================
|
||||
static int32_t detect_flv(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len >= FLV_HEADER_SIZE && memcmp(data, "FLV", 3) == 0 && data[3] == 0x01) {
|
||||
if ((data[4] & 0x01) || (data[4] & 0x04)) {
|
||||
return MEDIA_DTYPE_VIDEO_FLV;
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_avi(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len >= 20 && memcmp(data, "RIFF", 4) == 0 && memcmp(data + 8, "AVI ", 4) == 0) {
|
||||
if (memcmp(data + 12, "hdrl", 4) == 0) {
|
||||
return MEDIA_DTYPE_VIDEO_AVI;
|
||||
}
|
||||
}
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_ts(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
return check_packet_structure(data, len, TS_PACKET_SIZE, 3) ?
|
||||
MEDIA_DTYPE_VIDEO_TS : MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_m2ts(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
return check_packet_structure(data, len, M2TS_PACKET_SIZE, 3) ?
|
||||
MEDIA_DTYPE_VIDEO_M2TS : MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
static int32_t detect_mkv_webm(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
static const uint8_t ebml_sig[] = {0x1A, 0x45, 0xDF, 0xA3};
|
||||
|
||||
if (!CHECK_SIGNATURE(data, len, ebml_sig, sizeof(ebml_sig))) {
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
// 修复:简化的MKV/WebM检测,避免复杂的EBML解析
|
||||
const uint8_t *pos = data + 4; // 跳过EBML签名
|
||||
uint32_t remaining = len - 4;
|
||||
|
||||
// 在数据中查找"matroska"或"webm"字符串
|
||||
if (find_pattern(pos, remaining, (uint8_t*)"matroska", 8)) {
|
||||
return MEDIA_DTYPE_VIDEO_MKV;
|
||||
}
|
||||
if (find_pattern(pos, remaining, (uint8_t*)"webm", 4)) {
|
||||
return MEDIA_DTYPE_VIDEO_WEBM; // 修复常量名称
|
||||
}
|
||||
|
||||
// 如果找不到明确标记,但EBML签名正确,默认返回MKV
|
||||
return MEDIA_DTYPE_VIDEO_MKV;
|
||||
}
|
||||
|
||||
static int32_t detect_wmv_wma(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
static const uint8_t asf_sig[] = {0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11,
|
||||
0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C
|
||||
};
|
||||
|
||||
if (!CHECK_SIGNATURE(data, len, asf_sig, sizeof(asf_sig)) || len < 50) {
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
// 在ASF数据对象中查找媒体类型
|
||||
static const uint8_t video_marker[] = {0x91, 0x07, 0xDC, 0xB7, 0xB7, 0xA9, 0xCF, 0x11,
|
||||
0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}; // Video Media
|
||||
|
||||
static const uint8_t audio_marker[] = {0x91, 0x07, 0xDC, 0xB7, 0xB7, 0xA9, 0xCF, 0x11,
|
||||
0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x66}; // Audio Media
|
||||
|
||||
if (find_pattern(data + 30, len - 30, video_marker, sizeof(video_marker))) {
|
||||
return MEDIA_DTYPE_VIDEO_WMV;
|
||||
} else if (find_pattern(data + 30, len - 30, audio_marker, sizeof(audio_marker))) {
|
||||
return MEDIA_DTYPE_AUDIO_WMA;
|
||||
}
|
||||
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
// ==================== MOV格式检测函数 ====================
|
||||
static int32_t detect_mov(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len < 12) return MEDIA_DTYPE_MAX;
|
||||
|
||||
// 检查ftyp中的qt品牌
|
||||
if (memcmp(data + 4, "ftyp", 4) == 0) {
|
||||
if (len >= 16 && memcmp(data + 8, "qt ", 4) == 0) {
|
||||
return MEDIA_DTYPE_VIDEO_MOV;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查moov盒子(MOV文件通常moov在开头)
|
||||
if (memcmp(data + 4, "moov", 4) == 0) {
|
||||
return MEDIA_DTYPE_VIDEO_MOV;
|
||||
}
|
||||
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
// ==================== FTYP格式检测函数 ====================
|
||||
static int32_t detect_ftyp_format(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len < 8) return MEDIA_DTYPE_MAX;
|
||||
|
||||
// 修复FTYP大小判断逻辑,允许最小8字节
|
||||
uint32_t ftyp_size = get_unaligned_be32(data);
|
||||
|
||||
// 更严格的FTYP大小验证
|
||||
if (ftyp_size < 8 || ftyp_size > len || ftyp_size > 1024 * 1024) {
|
||||
return MEDIA_DTYPE_MAX; // 防止异常大的ftyp盒子
|
||||
}
|
||||
|
||||
if (memcmp(data + 4, "ftyp", 4) != 0) {
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
// 修复:ftyp大小等于8是有效的(只有大小和类型)
|
||||
if (ftyp_size == 8) {
|
||||
return MEDIA_DTYPE_VIDEO_MP4; // 最基本的MP4格式
|
||||
}
|
||||
|
||||
// HEIF/HEIC品牌
|
||||
static const char *heif_brands[] = {"heic", "heix", "hevc", "hevx", "mif1", "msf1", NULL};
|
||||
for (int i = 0; heif_brands[i]; i++) {
|
||||
if (memcmp(data + 8, heif_brands[i], 4) == 0) {
|
||||
return MEDIA_DTYPE_PIC_HEIF;
|
||||
}
|
||||
}
|
||||
|
||||
// M4A音频品牌
|
||||
static const char *m4a_brands[] = {"M4A ", "mp4a", "aac ", NULL};
|
||||
for (int i = 0; m4a_brands[i]; i++) {
|
||||
if (memcmp(data + 8, m4a_brands[i], 4) == 0) {
|
||||
return MEDIA_DTYPE_AUDIO_M4A;
|
||||
}
|
||||
}
|
||||
|
||||
// MOV品牌
|
||||
if (memcmp(data + 8, "qt ", 4) == 0) {
|
||||
return MEDIA_DTYPE_VIDEO_MOV;
|
||||
}
|
||||
|
||||
// MP4视频品牌
|
||||
static const char *mp4_brands[] = {"isom", "mp41", "mp42", "avc1", "hev1", NULL};
|
||||
for (int i = 0; mp4_brands[i]; i++) {
|
||||
if (memcmp(data + 8, mp4_brands[i], 4) == 0) {
|
||||
return MEDIA_DTYPE_VIDEO_MP4;
|
||||
}
|
||||
}
|
||||
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
int32_t txmplayer_detect_mtype(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
int32_t result;
|
||||
|
||||
if (data == NULL || len < MIN_DETECTION_LENGTH) {
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
// -------------------------- 有明确签名的容器格式检测 --------------------------
|
||||
if ((result = detect_ftyp_format(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_mp4(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_mov(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_flv(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_avi(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_mkv_webm(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_ts(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_m2ts(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_wmv_wma(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
|
||||
// -------------------------- 常见图片格式检测 --------------------------
|
||||
if ((result = detect_png(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_jpg(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_gif(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_bmp(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_webp(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_tiff(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_svg(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
|
||||
// -------------------------- 常见音频格式检测 --------------------------
|
||||
if ((result = detect_mp3(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_wav(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_flac(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_aac(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_ogg(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_ape(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
|
||||
// -------------------------- 裸视频编码帧检测 --------------------------
|
||||
// 注意:裸视频检测放在最后,因为它们可能与其他格式有误判
|
||||
// 同时要求找到多个特征才确认,减少误判
|
||||
if ((result = detect_h264_raw(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_h265_raw(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_av1_raw(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_vp9_raw(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_vp8_raw(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_mpeg2_raw(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
if ((result = detect_mpeg4_raw(data, len)) != MEDIA_DTYPE_MAX) return result;
|
||||
|
||||
return MEDIA_DTYPE_MAX;
|
||||
}
|
||||
|
||||
595
sdk/lib/multimedia/mplayer/txmplayer.c
Normal file
595
sdk/lib/multimedia/mplayer/txmplayer.c
Normal file
@@ -0,0 +1,595 @@
|
||||
#include "basic_include.h"
|
||||
#include "lib/multimedia/msi.h"
|
||||
#include "txmplayer.h"
|
||||
|
||||
#define TXMPLAYER_STREAM_MAX (4)
|
||||
|
||||
#define URLFILE(file) (os_strstr(file, "://") != NULL)
|
||||
|
||||
extern void *fopen(const char *filename, const char *mode);
|
||||
extern void fclose(void *stream);
|
||||
extern size_t fread(void *ptr, size_t size, size_t nmemb, void *stream);
|
||||
extern int fseek(void *stream, long int offset, int whence);
|
||||
extern long ftell(void *stream);
|
||||
extern int feof(void *stream);
|
||||
|
||||
extern void *uf_open(char *url, char *mode, uint32 flags);
|
||||
extern void uf_close(void *file);
|
||||
extern size_t uf_read(void *ptr, size_t size, size_t nmemb, void *file);
|
||||
extern int uf_seek(void *file, long int offset, int whence);
|
||||
extern int uf_eof(void *file);
|
||||
extern ulong uf_filesize(void *fp, uint32 size);
|
||||
extern int32 uf_seektype(void *fp);
|
||||
|
||||
struct txmplayer_codec {
|
||||
uint8 type;
|
||||
struct msi *codec;
|
||||
};
|
||||
|
||||
#define TXMPLAYER_HDRDATA_SIZE (2048)
|
||||
struct txmplayer_stream {
|
||||
ulong total_size;
|
||||
void *file;
|
||||
uint8 pause; //暂停读取数据
|
||||
|
||||
void *container_ctx; //container私有数据
|
||||
const txmplayer_container *container; //关联的container
|
||||
const struct txmplayer_input_ops *ops;
|
||||
|
||||
struct txmplayer_codec codecs[MEDIA_DATA_MAX]; //各路编码流的decoder
|
||||
struct txmplayer_audio_info audio_info;
|
||||
struct txmplayer_video_info video_info;
|
||||
struct txmplayer_picture_info pic_info;
|
||||
uint32 last_input_time; //最后一次输入数据的时间
|
||||
};
|
||||
|
||||
struct txmplayer {
|
||||
struct msi *msi;
|
||||
os_mutex_t lock;
|
||||
uint8 pause: 1, mute: 1, init: 1, rev: 4;
|
||||
uint8 volume;
|
||||
uint8 stream_max;
|
||||
int8 msi_stream;
|
||||
void *task_hdl;
|
||||
uint32 containers_cnt;
|
||||
const txmplayer_container *containers;
|
||||
struct txmplayer_stream streams[TXMPLAYER_STREAM_MAX];
|
||||
|
||||
struct framebuff *fb;
|
||||
uint32 fb_off;
|
||||
} g_txmplayer;
|
||||
|
||||
size_t msi_read(void *ptr, size_t size, size_t nmemb, void *stream)
|
||||
{
|
||||
uint32 len = 0;
|
||||
|
||||
if (g_txmplayer.fb == NULL) {
|
||||
g_txmplayer.fb = msi_get_fb(g_txmplayer.msi, 0);
|
||||
g_txmplayer.fb_off = 0;
|
||||
}
|
||||
|
||||
if (g_txmplayer.fb) {
|
||||
len = size * nmemb;
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = min(len, (g_txmplayer.fb->len - g_txmplayer.fb_off));
|
||||
hw_memcpy(ptr, g_txmplayer.fb->data + g_txmplayer.fb_off, len);
|
||||
g_txmplayer.fb_off += len;
|
||||
if (g_txmplayer.fb_off >= g_txmplayer.fb->len) {
|
||||
fb_put(g_txmplayer.fb);
|
||||
g_txmplayer.fb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
void msi_close(void *stream)
|
||||
{
|
||||
}
|
||||
int msi_seek(void *hdl, long int offset, int whence)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
int msi_eof(void *stream)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
const struct txmplayer_input_ops msi_ops = {
|
||||
.close = msi_close,
|
||||
.read = msi_read,
|
||||
.seek = msi_seek,
|
||||
.eof = msi_eof,
|
||||
};
|
||||
|
||||
const struct txmplayer_input_ops file_ops = {
|
||||
.close = fclose,
|
||||
.read = fread,
|
||||
.seek = fseek,
|
||||
.eof = feof,
|
||||
};
|
||||
|
||||
const struct txmplayer_input_ops uf_ops = {
|
||||
.close = uf_close,
|
||||
.read = uf_read,
|
||||
.seek = uf_seek,
|
||||
.eof = uf_eof,
|
||||
};
|
||||
|
||||
static struct msi *txmplayer_find_decoder(uint16 type)
|
||||
{
|
||||
struct msi *pmsi = NULL;
|
||||
const char *name = NULL;
|
||||
|
||||
switch (type) {
|
||||
case F_H264:
|
||||
name = "h264_dec";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pmsi = msi_find(name, 0);
|
||||
if (pmsi == NULL) {
|
||||
SYSEVT_NEW_MEDIA_EVT(SYSEVT_MEDIA_UNKNOWN_DECODER, type);
|
||||
}
|
||||
return pmsi;
|
||||
}
|
||||
|
||||
static const txmplayer_container *txmplayer_find_container(uint32 type)
|
||||
{
|
||||
uint32 i = 0;
|
||||
for (i = 0; i < g_txmplayer.containers_cnt; i++) {
|
||||
if (g_txmplayer.containers[i].type == type) {
|
||||
return &g_txmplayer.containers[i];
|
||||
}
|
||||
}
|
||||
SYSEVT_NEW_MEDIA_EVT(SYSEVT_MEDIA_UNKNOWN_CONTAINER, type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int32 txmplayer_match_container(struct txmplayer_stream *stream, uint8 *data, uint32 len)
|
||||
{
|
||||
uint32 mtype;
|
||||
|
||||
if (stream->container_ctx == NULL) {
|
||||
mtype = txmplayer_detect_mtype(data, len); //检测媒体数据类型
|
||||
stream->container = txmplayer_find_container(mtype); //根据类型查找container
|
||||
if (stream->container == NULL) {
|
||||
return RET_ERR;
|
||||
}
|
||||
|
||||
stream->container_ctx = stream->container->open(stream->total_size, data, len);
|
||||
if (stream->container_ctx == NULL) {
|
||||
SYSEVT_NEW_MEDIA_EVT(SYSEVT_MEDIA_CONTAINER_OPEN_ERROR, mtype);
|
||||
return RET_ERR;
|
||||
}
|
||||
SYSEVT_NEW_MEDIA_EVT(SYSEVT_MEDIA_CONTAINER_MATCH, mtype);
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static int32 txmplayer_release_stream(uint8 id)
|
||||
{
|
||||
int8 i = 0;
|
||||
struct txmplayer_stream *stream = &g_txmplayer.streams[id];
|
||||
|
||||
for (i = 0; i < MEDIA_DATA_MAX; i++) {
|
||||
msi_put(stream->codecs[i].codec);
|
||||
stream->codecs[i].codec = NULL;
|
||||
stream->codecs[i].type = MEDIA_DATA_MAX;
|
||||
}
|
||||
|
||||
if (stream->container_ctx) {
|
||||
stream->container->close(stream->container_ctx);
|
||||
}
|
||||
|
||||
if (stream->ops) {
|
||||
stream->ops->close(stream->file);
|
||||
}
|
||||
|
||||
if (stream->file == g_txmplayer.msi) {
|
||||
g_txmplayer.msi_stream = -1;
|
||||
}
|
||||
|
||||
stream->pause = 0;
|
||||
stream->last_input_time = 0;
|
||||
stream->container_ctx = NULL;
|
||||
stream->container = NULL;
|
||||
stream->file = NULL;
|
||||
stream->ops = NULL;
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static int32 txmplayer_request_stream(const struct txmplayer_input_ops *ops, void *file)
|
||||
{
|
||||
uint8 i = 0;
|
||||
int8 last = -1;
|
||||
|
||||
for (i = 0; i < g_txmplayer.stream_max; i++) {
|
||||
if (g_txmplayer.streams[i].file == NULL) {
|
||||
g_txmplayer.streams[i].ops = ops;
|
||||
g_txmplayer.streams[i].file = file;
|
||||
return i;
|
||||
}
|
||||
|
||||
if ((last == -1) || (g_txmplayer.streams[i].last_input_time < g_txmplayer.streams[last].last_input_time)) {
|
||||
last = i;
|
||||
}
|
||||
}
|
||||
|
||||
txmplayer_release_stream(last);
|
||||
g_txmplayer.streams[last].ops = ops;
|
||||
g_txmplayer.streams[last].file = file;
|
||||
return last;
|
||||
}
|
||||
|
||||
static int32 txmplayer_check_filesize(uint8 id)
|
||||
{
|
||||
struct txmplayer_stream *stream = &g_txmplayer.streams[id];
|
||||
if (stream->total_size == 0) {
|
||||
if (stream->ops == &file_ops) {
|
||||
stream->ops->seek(stream->file, 0, SEEK_END);
|
||||
stream->total_size = ftell(stream->file);
|
||||
stream->ops->seek(stream->file, 0, SEEK_SET);
|
||||
} else if ((stream->ops == &uf_ops)) {
|
||||
stream->total_size = uf_filesize(stream->file, TXMPLAYER_HDRDATA_SIZE);
|
||||
if (stream->total_size == 0) { //等待网络数据
|
||||
txm_dbg("waitting for connectting ...\r\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
} else {
|
||||
stream->total_size = -1;
|
||||
}
|
||||
SYSEVT_NEW_MEDIA_EVT(SYSEVT_MEDIA_OPEN_SUCCESS, id);
|
||||
}
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static int32 txmplayer_proc_read_data(void)
|
||||
{
|
||||
int8 i = 0;
|
||||
int8 more = 0;
|
||||
uint32 len;
|
||||
struct txmplayer_stream *stream;
|
||||
uint8 *buff = NULL;
|
||||
|
||||
for (i = 0; i < g_txmplayer.stream_max; i++) {
|
||||
stream = &g_txmplayer.streams[i];
|
||||
if (stream->ops && stream->file != g_txmplayer.msi && !stream->pause) {
|
||||
if (txmplayer_check_filesize(i) == -EAGAIN) {
|
||||
return 0; //等待网络连接成功
|
||||
}
|
||||
|
||||
if (stream->container_ctx == NULL) { //匹配container
|
||||
buff = os_malloc(TXMPLAYER_HDRDATA_SIZE); //预读header数据,用于检测媒体类型
|
||||
if (buff) {
|
||||
len = stream->ops->read(buff, 1, TXMPLAYER_HDRDATA_SIZE, stream->file);
|
||||
if (len > 0) {
|
||||
if (txmplayer_match_container(stream, buff, len)) {
|
||||
txmplayer_release_stream(i);
|
||||
txm_err("unknown media format!\r\n");
|
||||
}
|
||||
}
|
||||
os_free(buff);
|
||||
} else {
|
||||
txm_err("no memory!\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
//执行demux,解析一笔数据
|
||||
if (stream->container_ctx && stream->container->demux(stream->container_ctx, stream->ops, stream->file, stream)) {
|
||||
stream->last_input_time = os_seconds();
|
||||
more = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return more;
|
||||
}
|
||||
|
||||
//处理通过MSI组件流程输入的fb
|
||||
static int32 txmplayer_proc_msi_data()
|
||||
{
|
||||
struct framebuff *fb;
|
||||
struct txmplayer_stream *stream;
|
||||
|
||||
if (g_txmplayer.msi_stream != -1 && g_txmplayer.streams[g_txmplayer.msi_stream].container_ctx) {
|
||||
stream = &g_txmplayer.streams[g_txmplayer.msi_stream];
|
||||
if (!stream->pause) {
|
||||
return stream->container->demux(stream->container_ctx, stream->ops, stream->file, stream);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fb = msi_get_fb(g_txmplayer.msi, 0);
|
||||
if (fb) {
|
||||
if (!g_txmplayer.msi->enable) {
|
||||
fb_put(fb);
|
||||
txm_err("TXMplayer stopped!\r\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (g_txmplayer.msi_stream == -1) {
|
||||
g_txmplayer.msi_stream = txmplayer_request_stream(&msi_ops, g_txmplayer.msi);
|
||||
}
|
||||
|
||||
stream = &g_txmplayer.streams[g_txmplayer.msi_stream];
|
||||
stream->last_input_time = os_seconds();
|
||||
stream->total_size = -1;
|
||||
|
||||
MEDIA_DATA_CATEGORY dtype = fb_category(fb);
|
||||
if (dtype == MEDIA_DATA_MAX) { //未知类型,识别container,进行解封装
|
||||
if (txmplayer_match_container(stream, fb->data, fb->len)) {
|
||||
txmplayer_release_stream(g_txmplayer.msi_stream);
|
||||
txm_err("unknown media format!\r\n");
|
||||
}
|
||||
fb_put(fb);
|
||||
} else { //已知类型,直接传递给decoder解码
|
||||
txmplayer_stream_output(stream, fb);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void txmplayer_thread(void *arg)
|
||||
{
|
||||
uint8 more_data = 0;
|
||||
|
||||
while (1) {
|
||||
if (!g_txmplayer.pause) {
|
||||
os_mutex_lock(&g_txmplayer.lock, osWaitForever);
|
||||
more_data |= txmplayer_proc_msi_data();
|
||||
more_data |= txmplayer_proc_read_data();
|
||||
os_mutex_unlock(&g_txmplayer.lock);
|
||||
}
|
||||
|
||||
if (!more_data) {
|
||||
os_sleep_ms(10);
|
||||
}
|
||||
more_data = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//container解封装,构建fb之后,执行此函数输出fb。
|
||||
//该函数根据编码类型:fb->mtype,查找对应的解码模块,输出fb
|
||||
int32 txmplayer_stream_output(void *priv, struct framebuff *fb)
|
||||
{
|
||||
struct msi *decoder = NULL;
|
||||
struct txmplayer_stream *stream = priv;
|
||||
MEDIA_DATA_CATEGORY dtype = fb_category(fb);
|
||||
|
||||
if (fb->msi == NULL) {
|
||||
msi_get(g_txmplayer.msi);
|
||||
fb->msi = g_txmplayer.msi;
|
||||
}
|
||||
|
||||
if (dtype == MEDIA_DATA_MAX) {
|
||||
fb_put(fb);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (fb->mtype != stream->codecs[dtype].type) { //编码类型发生变化,需要重新选择解码模块
|
||||
msi_put(stream->codecs[dtype].codec);
|
||||
stream->codecs[dtype].codec = NULL;
|
||||
stream->codecs[dtype].type = fb->mtype;
|
||||
}
|
||||
|
||||
if (stream->codecs[dtype].codec == NULL) {
|
||||
stream->codecs[dtype].codec = txmplayer_find_decoder(fb->mtype);
|
||||
}
|
||||
|
||||
decoder = stream->codecs[dtype].codec;
|
||||
if (decoder && decoder->enable) {
|
||||
if (msi_do_cmd(decoder, MSI_CMD_TRANS_FB, (uint32)fb, 0) == RET_OK) {
|
||||
if (decoder->fbQ.init) {
|
||||
fbq_enqueue(&decoder->fbQ, fb);
|
||||
}
|
||||
}
|
||||
|
||||
fb_put(fb);
|
||||
return RET_OK;
|
||||
} else {
|
||||
fb_put(fb);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
static int32 txmplayer_msi_action(struct msi *msi, uint32 cmd_id, uint32 param1, uint32 param2)
|
||||
{
|
||||
int32_t ret = RET_OK;
|
||||
|
||||
switch (cmd_id) {
|
||||
case MSI_CMD_TRANS_FB:
|
||||
break;
|
||||
case MSI_CMD_FREE_FB:
|
||||
break;
|
||||
case MSI_CMD_POST_DESTROY:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int32 txmplayer_init(uint8 stream_max, const txmplayer_container *containers, uint32 container_cnt, uint32 stack_size)
|
||||
{
|
||||
ASSERT(containers && container_cnt);
|
||||
if (stream_max == 0) stream_max = 1;
|
||||
if (stack_size == 0) stack_size = 1024;
|
||||
|
||||
os_mutex_init(&g_txmplayer.lock);
|
||||
g_txmplayer.stream_max = min(stream_max, TXMPLAYER_STREAM_MAX);
|
||||
g_txmplayer.containers = containers;
|
||||
g_txmplayer.containers_cnt = container_cnt;
|
||||
g_txmplayer.msi = msi_new("txmplayer", 256, NULL);
|
||||
g_txmplayer.task_hdl = os_task_create("txmplayer", txmplayer_thread, NULL, OS_TASK_PRIORITY_HIGH, 5, NULL, stack_size);
|
||||
ASSERT(g_txmplayer.msi && g_txmplayer.task_hdl);
|
||||
g_txmplayer.msi->action = (msi_action)txmplayer_msi_action;
|
||||
g_txmplayer.msi->enable = 1;
|
||||
g_txmplayer.init = 1;
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
int32 txmplayer_deinit()
|
||||
{
|
||||
int8 i = 0;
|
||||
struct framebuff *fb;
|
||||
|
||||
ASSERT(g_txmplayer.init);
|
||||
g_txmplayer.init = 0;
|
||||
g_txmplayer.msi->enable = 0;
|
||||
|
||||
os_mutex_lock(&g_txmplayer.lock, osWaitForever);
|
||||
os_task_destroy(g_txmplayer.task_hdl);
|
||||
for (i = 0; i < g_txmplayer.stream_max; i++) {
|
||||
txmplayer_release_stream(i);
|
||||
}
|
||||
|
||||
if (g_txmplayer.fb) {
|
||||
fb_put(g_txmplayer.fb);
|
||||
g_txmplayer.fb = NULL;
|
||||
}
|
||||
|
||||
fb = msi_get_fb(g_txmplayer.msi, 0);
|
||||
while (fb) {
|
||||
fb_put(fb);
|
||||
fb = msi_get_fb(g_txmplayer.msi, 0);
|
||||
}
|
||||
os_mutex_unlock(&g_txmplayer.lock);
|
||||
|
||||
msi_destroy(g_txmplayer.msi);
|
||||
|
||||
g_txmplayer.pause = 0;
|
||||
g_txmplayer.mute = 0;
|
||||
g_txmplayer.volume = 0;
|
||||
g_txmplayer.msi_stream = -1;
|
||||
g_txmplayer.task_hdl = NULL;
|
||||
g_txmplayer.msi = NULL;
|
||||
SYSEVT_NEW_MEDIA_EVT(SYSEVT_MEDIA_PLAY_CLOSE, 0);
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
int32 txmplayer_open(char *file)
|
||||
{
|
||||
int32 ret = -EIO;
|
||||
void *file_hdl;
|
||||
const struct txmplayer_input_ops *ops;
|
||||
|
||||
ASSERT(g_txmplayer.init);
|
||||
if (URLFILE(file)) {
|
||||
ops = &uf_ops;
|
||||
file_hdl = uf_open(file, "rav", 0);
|
||||
} else {
|
||||
ops = &file_ops;
|
||||
file_hdl = fopen(file, "r");
|
||||
}
|
||||
|
||||
if (file_hdl == NULL) {
|
||||
txm_err("open %s fail\r\n", file);
|
||||
SYSEVT_NEW_MEDIA_EVT(SYSEVT_MEDIA_OPEN_FAIL, 0);
|
||||
return RET_ERR;
|
||||
}
|
||||
|
||||
os_mutex_lock(&g_txmplayer.lock, osWaitForever);
|
||||
if (g_txmplayer.init) {
|
||||
ret = txmplayer_request_stream(ops, file_hdl);
|
||||
}
|
||||
os_mutex_unlock(&g_txmplayer.lock);
|
||||
|
||||
if (ret < 0) {
|
||||
ops->close(file_hdl);
|
||||
} else {
|
||||
SYSEVT_NEW_MEDIA_EVT(SYSEVT_MEDIA_PLAY_START, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int32 txmplayer_stop(uint8 stream_id)
|
||||
{
|
||||
ASSERT(g_txmplayer.init);
|
||||
if (stream_id < g_txmplayer.stream_max) {
|
||||
os_mutex_lock(&g_txmplayer.lock, osWaitForever);
|
||||
if (g_txmplayer.init) {
|
||||
txmplayer_release_stream(stream_id);
|
||||
SYSEVT_NEW_MEDIA_EVT(SYSEVT_MEDIA_PLAY_STOP, stream_id);
|
||||
}
|
||||
os_mutex_unlock(&g_txmplayer.lock);
|
||||
return RET_OK;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int32 txmplayer_pause(uint8 stream_id, uint8 pause)
|
||||
{
|
||||
ASSERT(g_txmplayer.init);
|
||||
if (stream_id < g_txmplayer.stream_max) {
|
||||
os_mutex_lock(&g_txmplayer.lock, osWaitForever);
|
||||
if (g_txmplayer.init) {
|
||||
g_txmplayer.streams[stream_id].pause = pause;
|
||||
|
||||
//TBD ..........
|
||||
|
||||
if(pause){
|
||||
SYSEVT_NEW_MEDIA_EVT(SYSEVT_MEDIA_PLAY_PAUSE, stream_id);
|
||||
}else{
|
||||
SYSEVT_NEW_MEDIA_EVT(SYSEVT_MEDIA_PLAYING, stream_id);
|
||||
}
|
||||
}
|
||||
os_mutex_unlock(&g_txmplayer.lock);
|
||||
return RET_OK;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int32 txmplayer_seek(uint8 stream_id, uint32 new_time)
|
||||
{
|
||||
struct txmplayer_stream *stream;
|
||||
ASSERT(g_txmplayer.init);
|
||||
|
||||
if (stream_id < g_txmplayer.stream_max) {
|
||||
os_mutex_lock(&g_txmplayer.lock, osWaitForever);
|
||||
if (g_txmplayer.init) {
|
||||
stream = &g_txmplayer.streams[stream_id];
|
||||
if (stream->file) {
|
||||
if (stream->total_size == (ulong)(-1)) {
|
||||
os_mutex_unlock(&g_txmplayer.lock);
|
||||
SYSEVT_NEW_MEDIA_EVT(SYSEVT_MEDIA_PLAY_SEEK_ERR, stream_id);
|
||||
txm_err("Livestream, Can not seek!\r\n");
|
||||
return -ENOTSUP;
|
||||
} else {
|
||||
if (stream->ops == &uf_ops && uf_seektype(stream->file) == 1) { //time seek: 直接执行seek操作
|
||||
stream->ops->seek(stream->file, new_time, SEEK_SET);
|
||||
} else if (stream->container) { //由container执行seek
|
||||
stream->container->seek(stream->container_ctx, new_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
os_mutex_unlock(&g_txmplayer.lock);
|
||||
return RET_OK;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int32 txmplayer_set_speed(uint8 stream_id, MEDIA_PLAY_SPEED speed)
|
||||
{
|
||||
ASSERT(g_txmplayer.init);
|
||||
os_mutex_lock(&g_txmplayer.lock, osWaitForever);
|
||||
//TBD ..........
|
||||
os_mutex_unlock(&g_txmplayer.lock);
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
int32 txmplayer_set_volume(uint8 stream_id, uint8 volume)
|
||||
{
|
||||
ASSERT(g_txmplayer.init);
|
||||
os_mutex_lock(&g_txmplayer.lock, osWaitForever);
|
||||
//TBD ..........
|
||||
os_mutex_unlock(&g_txmplayer.lock);
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
77
sdk/lib/multimedia/mplayer/txmplayer.h
Normal file
77
sdk/lib/multimedia/mplayer/txmplayer.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef __TX_MPLAYER_H__
|
||||
#define __TX_MPLAYER_H__
|
||||
|
||||
#include "basic_include.h"
|
||||
#include "lib/multimedia/framebuff.h"
|
||||
|
||||
#ifndef SEEK_SET
|
||||
#define SEEK_SET 0
|
||||
#endif
|
||||
|
||||
#ifndef SEEK_CUR
|
||||
#define SEEK_CUR 1
|
||||
#endif
|
||||
|
||||
#ifndef SEEK_END
|
||||
#define SEEK_END 2
|
||||
#endif
|
||||
|
||||
|
||||
//编码信息
|
||||
struct txmplayer_audio_info {
|
||||
//TBD...
|
||||
} ;
|
||||
|
||||
struct txmplayer_video_info {
|
||||
//TBD...
|
||||
} ;
|
||||
|
||||
struct txmplayer_picture_info{
|
||||
//TBD...
|
||||
} ;
|
||||
|
||||
struct txmplayer_input_ops {
|
||||
void (*close)(void *hdl);
|
||||
size_t (*read)(void *ptr, size_t size, size_t nmemb, void *hdl);
|
||||
int (*seek)(void *hdl, long int offset, int whence);
|
||||
int (*eof)(void *hdl);
|
||||
};
|
||||
|
||||
//数据源可能是 文件 或 网络流
|
||||
//如果是文件,则可以随意执行seek操作,读取数据进行解析
|
||||
//如果是网络流,执行seek可能失败,container需要应对seek的失败情况 或 不能seek 的情况
|
||||
//open API的参数size=-1 表示不支持seek操作
|
||||
typedef struct {
|
||||
uint32 type;
|
||||
const char *name;
|
||||
|
||||
//初始化cotainer,hdr是 为了识别媒体类型而读取的数据
|
||||
//为了避免seek操作,container需要解析并保存 hdr 数据
|
||||
void *(*open)(ulong size, void *hdr, uint32 len);
|
||||
|
||||
//关闭container,释放资源
|
||||
int32(*close)(void *t);
|
||||
|
||||
//由外部调用,seek到指定的时间位置
|
||||
int32(*seek)(void *t, uint32 time);
|
||||
|
||||
//不能阻塞式执行:执行一次只完成一笔数据的解析
|
||||
int32(*demux)(void *t, const struct txmplayer_input_ops *ops, void *hdl, void *stream);
|
||||
} txmplayer_container;
|
||||
|
||||
#define txm_dbg(fmt, ...) //os_printf("%s:%d::"fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#define txm_err(fmt, ...) os_printf(KERN_ERR"%s:%d::"fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#define txm_warn(fmt, ...) os_printf(KERN_WARNING"%s:%d::"fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
|
||||
int32 txmplayer_init(uint8 stream_max, const txmplayer_container *containers, uint32 container_cnt, uint32 stack_size);
|
||||
int32 txmplayer_open(char *file);
|
||||
int32 txmplayer_stop(uint8 stream_id);
|
||||
int32 txmplayer_pause(uint8 stream_id, uint8 pause);
|
||||
int32 txmplayer_seek(uint8 stream_id, uint32 new_time);
|
||||
int32 txmplayer_set_speed(uint8 stream_id, MEDIA_PLAY_SPEED speed);
|
||||
int32 txmplayer_set_volume(uint8 stream_id, uint8 volume);
|
||||
int32 txmplayer_stream_output(void *stream, struct framebuff *fb);
|
||||
int32 txmplayer_detect_mtype(const uint8_t *data, uint32_t len);
|
||||
|
||||
#endif
|
||||
|
||||
89
sdk/lib/multimedia/msi/fbpool.c
Normal file
89
sdk/lib/multimedia/msi/fbpool.c
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "basic_include.h"
|
||||
#include "lib/multimedia/msi.h"
|
||||
|
||||
|
||||
#ifdef MORE_SRAM
|
||||
#define FBPOOL_MALLOC os_malloc_psram
|
||||
#define FBPOOL_FREE os_free_psram
|
||||
#define FBPOOL_ZALLOC os_zalloc_psram
|
||||
#else
|
||||
#define FBPOOL_MALLOC os_malloc
|
||||
#define FBPOOL_FREE os_free
|
||||
#define FBPOOL_ZALLOC os_zalloc
|
||||
#endif
|
||||
|
||||
int32 fbpool_init(struct fbpool *pool, uint8 size)
|
||||
{
|
||||
uint8 i;
|
||||
if (pool && !pool->inited) {
|
||||
pool->pool = FBPOOL_ZALLOC(sizeof(struct framebuff) * size);
|
||||
ASSERT(pool->pool);
|
||||
pool->inited = 1;
|
||||
pool->size = size;
|
||||
for (i = 0; i < size; i++) {
|
||||
pool->pool[i].index = i;
|
||||
pool->pool[i].used = 0;
|
||||
pool->pool[i].pool = 1;
|
||||
}
|
||||
}
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
struct framebuff *fbpool_get(struct fbpool *pool, uint16 type, struct msi *msi)
|
||||
{
|
||||
uint8 i;
|
||||
uint32 flag;
|
||||
struct framebuff *fb = NULL;
|
||||
|
||||
if (pool && pool->inited) {
|
||||
flag = disable_irq();
|
||||
for (i = 0; i < pool->size; i++) {
|
||||
if (!pool->pool[i].used) {
|
||||
fb = &pool->pool[i];
|
||||
fb->mtype = (type >> 8) & 0xff;
|
||||
fb->stype = type & 0xff;
|
||||
fb->msi = msi;
|
||||
fb->used = 1;
|
||||
fb->next = NULL;
|
||||
msi_get(fb->msi);
|
||||
atomic_inc(&fb->users); //GET 加1
|
||||
break;
|
||||
}
|
||||
}
|
||||
enable_irq(flag);
|
||||
}
|
||||
return fb;
|
||||
}
|
||||
|
||||
int32 fbpool_put(struct fbpool *pool, struct framebuff *fb)
|
||||
{
|
||||
uint32 flag;
|
||||
if (pool && pool->inited && fb && fb->pool &&
|
||||
(fb->index < pool->size) && (&pool->pool[fb->index] == fb)) {
|
||||
flag = disable_irq();
|
||||
pool->pool[fb->index].used = 0;
|
||||
pool->pool[fb->index].msi = NULL;
|
||||
pool->pool[fb->index].next = NULL;
|
||||
atomic_set(&fb->users,0);
|
||||
enable_irq(flag);
|
||||
return RET_OK;
|
||||
} else {
|
||||
os_printf(KERN_ERR"fbpool_put error, pool=%p, fb=%p\r\n", pool, fb);
|
||||
return RET_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
int32 fbpool_destroy(struct fbpool *pool)
|
||||
{
|
||||
int32 i = 0;
|
||||
if (pool && pool->inited) {
|
||||
for (i = 0; i < pool->size; i++) {
|
||||
msi_discard_fb(pool->pool[i].msi, &pool->pool[i]);
|
||||
}
|
||||
FBPOOL_FREE(pool->pool);
|
||||
pool->inited = 0;
|
||||
pool->size = 0;
|
||||
}
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
268
sdk/lib/multimedia/msi/fbqueue.c
Normal file
268
sdk/lib/multimedia/msi/fbqueue.c
Normal file
@@ -0,0 +1,268 @@
|
||||
#include "basic_include.h"
|
||||
#include "lib/multimedia/msi.h"
|
||||
#ifdef MORE_SRAM
|
||||
#define FBQ_MALLOC os_malloc_psram
|
||||
#define FBQ_FREE os_free_psram
|
||||
#define FBQ_ZALLOC os_zalloc_psram
|
||||
#else
|
||||
#define FBQ_MALLOC os_malloc
|
||||
#define FBQ_FREE os_free
|
||||
#define FBQ_ZALLOC os_zalloc
|
||||
#endif
|
||||
|
||||
int32 fbq_init(struct fbqueue *q, uint8 *qbuff, int32 qsize)
|
||||
{
|
||||
if (qbuff == NULL && qsize) {
|
||||
qbuff = (uint8 *)FBQ_MALLOC(sizeof(struct framebuff *) * (qsize + 1));
|
||||
q->alloc = 1;
|
||||
}
|
||||
|
||||
ASSERT(qbuff);
|
||||
if (qbuff && qsize) {
|
||||
RB_INIT_R(&q->rbQ, (qsize + 1), (struct framebuff **)qbuff);
|
||||
os_sema_init(&q->sema, 0);
|
||||
q->reader_max = 1;
|
||||
q->readers = NULL;
|
||||
q->init = 1;
|
||||
return RET_OK;
|
||||
}
|
||||
return RET_ERR;
|
||||
}
|
||||
|
||||
int32 fbq_destory(struct fbqueue *q)
|
||||
{
|
||||
if (q && q->init) {
|
||||
while (RB_COUNT(&q->rbQ)) {
|
||||
fb_put(fbq_dequeue(q, 0));
|
||||
}
|
||||
if (q->alloc) {
|
||||
FBQ_FREE(q->rbQ.rbq);
|
||||
}
|
||||
if (q->readers) {
|
||||
FBQ_FREE(q->readers);
|
||||
}
|
||||
os_sema_del(&q->sema);
|
||||
q->init = 0;
|
||||
}
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
int32 fbq_enqueue(struct fbqueue *q, struct framebuff *fb)
|
||||
{
|
||||
int32 ret;
|
||||
uint32 i;
|
||||
uint32 rpos;
|
||||
uint32 flags;
|
||||
struct framebuff *last = NULL;
|
||||
|
||||
if (!q || !q->init || !fb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fb_get(fb);
|
||||
|
||||
if (q->reader_max > 1) {
|
||||
flags = disable_irq();
|
||||
if (RB_FULL(&q->rbQ)) { // FULL ! move rpos.
|
||||
last = q->rbQ.rbq[q->rbQ.rpos];
|
||||
rpos = RB_NPOS(&q->rbQ, rpos, 1);
|
||||
for (i = 0; i < q->reader_max; i++) {
|
||||
if (q->readers[i] == q->rbQ.rpos) {
|
||||
q->readers[i] = rpos;
|
||||
}
|
||||
}
|
||||
q->rbQ.rpos = rpos;
|
||||
}
|
||||
q->rbQ.rbq[q->rbQ.wpos] = fb;
|
||||
q->rbQ.wpos = RB_NPOS(&q->rbQ, wpos, 1);
|
||||
enable_irq(flags);
|
||||
|
||||
fb_put(last);
|
||||
return 1;
|
||||
} else {
|
||||
ret = RB_INT_SET(&q->rbQ, fb);
|
||||
if (ret) {
|
||||
os_sema_up(&q->sema);
|
||||
return 1;
|
||||
} else {
|
||||
fb_put(fb);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int32 fbq_trace_pos(struct fbqueue *q, struct framebuff *fb, uint32 start, uint32 end, int8 discard)
|
||||
{
|
||||
uint32 pos;
|
||||
for (pos = start; pos < end; pos++) {
|
||||
if (q->rbQ.rbq[pos] == fb) {
|
||||
if (discard) {
|
||||
q->rbQ.rbq[pos] = NULL;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32 fbq_trace(struct fbqueue *q, struct framebuff *fb, int8 discard)
|
||||
{
|
||||
uint32 ret = 0;
|
||||
uint32 flag;
|
||||
|
||||
if (!q || !q->init || !fb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
flag = disable_irq();
|
||||
if (q->rbQ.rpos <= q->rbQ.wpos) {
|
||||
ret = fbq_trace_pos(q, fb, q->rbQ.rpos, q->rbQ.wpos, discard);
|
||||
} else {
|
||||
ret = fbq_trace_pos(q, fb, 0, q->rbQ.wpos, discard);
|
||||
if (!ret) {
|
||||
ret = fbq_trace_pos(q, fb, q->rbQ.rpos, q->rbQ.qsize, discard);
|
||||
}
|
||||
}
|
||||
enable_irq(flag);
|
||||
|
||||
if (ret && discard) {
|
||||
fb_put(fb);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct framebuff *fbq_dequeue(struct fbqueue *q, uint32 tmo_ms)
|
||||
{
|
||||
uint64 jiff;
|
||||
struct framebuff *fb;
|
||||
|
||||
if (!q || !q->init) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
do {
|
||||
if (!RB_EMPTY(&q->rbQ)) {
|
||||
RB_GET(&q->rbQ, fb);
|
||||
return fb;
|
||||
}
|
||||
|
||||
if(tmo_ms == 0){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jiff = os_jiffies();
|
||||
os_sema_down(&q->sema, tmo_ms);
|
||||
jiff = DIFF_JIFFIES(jiff, os_jiffies());
|
||||
jiff = os_jiffies_to_msecs(jiff);
|
||||
if (jiff >= tmo_ms) {
|
||||
break;
|
||||
}
|
||||
|
||||
tmo_ms -= jiff;
|
||||
} while (tmo_ms);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct framebuff *fbq_dequeue_r(struct fbqueue *q, uint8 reader)
|
||||
{
|
||||
uint32 i = 0;
|
||||
uint32 pos;
|
||||
uint32 flags;
|
||||
struct framebuff *fb = NULL;
|
||||
struct framebuff *last = NULL;
|
||||
|
||||
if (!q || !q->init) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ASSERT(reader > 0 && reader <= q->reader_max);
|
||||
ASSERT(q->readers[reader - 1] != 0xffffffff);
|
||||
|
||||
reader -= 1;
|
||||
flags = disable_irq();
|
||||
pos = q->readers[reader];
|
||||
|
||||
if (pos == q->rbQ.wpos) { //no fb for this reader
|
||||
enable_irq(flags);
|
||||
return fb;
|
||||
}
|
||||
|
||||
fb = q->rbQ.rbq[pos];
|
||||
q->readers[reader] = NEXT_RPOS(pos, q->rbQ.qsize, 1);
|
||||
|
||||
//move rpos: find min pos.
|
||||
pos = 0xffffffff;
|
||||
for (i = 0; i < q->reader_max; i++) {
|
||||
if (q->readers[i] < pos) {
|
||||
pos = q->readers[i];
|
||||
}
|
||||
}
|
||||
if (pos != 0xffffffff && q->rbQ.rpos != pos) {
|
||||
last = q->rbQ.rbq[q->rbQ.rpos];
|
||||
q->rbQ.rpos = pos;
|
||||
}
|
||||
|
||||
fb_get(fb);
|
||||
enable_irq(flags);
|
||||
|
||||
fb_put(last);
|
||||
return fb;
|
||||
}
|
||||
|
||||
int32 fbq_conf_readers(struct fbqueue *q, uint16 reader_max)
|
||||
{
|
||||
uint32 i;
|
||||
uint32 *ptr;
|
||||
|
||||
if (!q || !q->init || reader_max <= 1 || q->readers) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ptr = (uint32 *)FBQ_MALLOC(reader_max * sizeof(uint32));
|
||||
if (ptr) {
|
||||
for (i = 0; i < q->reader_max; i++) {
|
||||
q->readers[i] = 0xffffffff;
|
||||
}
|
||||
q->readers = ptr;
|
||||
q->reader_max = reader_max;
|
||||
return RET_OK;
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int32 fbq_open_reader(struct fbqueue *q)
|
||||
{
|
||||
uint32 i;
|
||||
uint32 flags;
|
||||
|
||||
if (!q || !q->init || !q->readers) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
flags = disable_irq();
|
||||
for (i = 0; i < q->reader_max; i++) {
|
||||
if (q->readers[i] == 0xffffffff) {
|
||||
q->readers[i] = q->rbQ.rpos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
enable_irq(flags);
|
||||
return i < q->reader_max ? (i + 1) : 0;
|
||||
}
|
||||
|
||||
int32 fbq_close_reader(struct fbqueue *q, uint8 reader)
|
||||
{
|
||||
uint32 flags;
|
||||
|
||||
if (!q || !q->init || !q->readers) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ASSERT(reader > 0 && reader <= q->reader_max);
|
||||
flags = disable_irq();
|
||||
q->readers[reader - 1] = 0xffffffff;
|
||||
enable_irq(flags);
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
148
sdk/lib/multimedia/msi/framebuff.c
Normal file
148
sdk/lib/multimedia/msi/framebuff.c
Normal file
@@ -0,0 +1,148 @@
|
||||
#include "basic_include.h"
|
||||
#include "lib/multimedia/msi.h"
|
||||
|
||||
static void fb_free(struct framebuff *fb)
|
||||
{
|
||||
int32 ret = 0;
|
||||
struct msi *msi;
|
||||
|
||||
if (fb) {
|
||||
ASSERT(atomic_read(&fb->users) > 0);
|
||||
if (!atomic_dec_and_test(&fb->users)) {
|
||||
return;
|
||||
}
|
||||
|
||||
msi = fb->msi;
|
||||
ret = msi_do_cmd(fb->msi, MSI_CMD_FREE_FB, (uint32)fb, 0);
|
||||
msi_put(msi);
|
||||
if (ret == RET_OK) { //不等于OK表示模块自己管理fb,例如预分配的fb
|
||||
ASSERT(!fb->pool); //预分配的framebuff不能执行到这里
|
||||
FB_FREE(fb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//framebuff 引用计数加1
|
||||
void fb_get(struct framebuff *fb)
|
||||
{
|
||||
if (fb) {
|
||||
fb_get(fb->next);
|
||||
atomic_inc(&fb->users);
|
||||
}
|
||||
}
|
||||
|
||||
struct framebuff *fb_alloc(uint8 *data, int32 size, uint16 type, struct msi *msi)
|
||||
{
|
||||
int32 len = data ? 0 : size;
|
||||
struct framebuff *fb = FB_ALLOC(sizeof(struct framebuff) + len);
|
||||
if (fb) {
|
||||
os_memset(fb, 0, sizeof(struct framebuff));
|
||||
atomic_set(&fb->users, 1);
|
||||
fb->len = size;
|
||||
fb->msi = msi;
|
||||
fb->mtype = (type >> 8) & 0xff;
|
||||
fb->stype = type & 0xff;
|
||||
msi_get(fb->msi);
|
||||
if (data) {
|
||||
fb->data = data;
|
||||
} else if (size) {
|
||||
fb->data = (uint8 *)(fb + 1);
|
||||
}
|
||||
}
|
||||
return fb;
|
||||
}
|
||||
|
||||
struct framebuff *fb_clone(struct framebuff *fb, uint16 type, struct msi *msi)
|
||||
{
|
||||
struct framebuff *fb_n = FB_ALLOC(sizeof(struct framebuff));
|
||||
if (fb_n) {
|
||||
os_memset(fb_n, 0, sizeof(struct framebuff));
|
||||
atomic_set(&fb_n->users, 1);
|
||||
fb_n->clone = 1;
|
||||
fb_n->msi = msi;
|
||||
fb_n->mtype = (type >> 8) & 0xff;
|
||||
fb_n->stype = type & 0xff;
|
||||
msi_get(fb_n->msi);
|
||||
fb_n->data = fb->data;
|
||||
fb_n->len = fb->len;
|
||||
fb_n->time = fb->time;
|
||||
fb_n->priv = fb->priv;
|
||||
fb_n->srcID = fb->srcID;
|
||||
fb_n->datatag = fb->datatag;
|
||||
fb_get(fb);
|
||||
fb_n->next = fb;
|
||||
}
|
||||
return fb_n;
|
||||
}
|
||||
|
||||
//framebuff 引用计数减1,当计数减至0时会释放空间
|
||||
void fb_put(struct framebuff *fb)
|
||||
{
|
||||
if (fb) {
|
||||
fb_put(fb->next);
|
||||
fb_free(fb);
|
||||
}
|
||||
}
|
||||
|
||||
//使用新的framebuff引用关联另1个framebuff,fb_old引用计数加1
|
||||
void fb_ref(struct framebuff *fb_new, struct framebuff *fb_old)
|
||||
{
|
||||
ASSERT(fb_new->next == NULL);
|
||||
fb_get(fb_old);
|
||||
fb_new->next = fb_old;
|
||||
}
|
||||
|
||||
//获取framebuff链表中指定type的数据的第1个节点
|
||||
struct framebuff *fb_find(struct framebuff *fb, uint8 mtype, uint8 stype)
|
||||
{
|
||||
if (mtype == 0 || fb == NULL || fb->next == NULL) {
|
||||
return fb;
|
||||
}
|
||||
|
||||
while (fb) {
|
||||
if (fb->mtype == mtype && (fb->stype == stype || stype == 0)) {
|
||||
return fb;
|
||||
}
|
||||
fb = fb->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//获取framebuff链表中指定type的数据的总长度
|
||||
uint32 fb_len(struct framebuff *fb, uint8 mtype, uint8 stype)
|
||||
{
|
||||
uint32 len = 0;
|
||||
|
||||
if (mtype == 0 || fb == NULL) {
|
||||
return fb ? fb->len : 0;
|
||||
}
|
||||
|
||||
if (fb->mtype != mtype) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (fb) {
|
||||
if (fb->mtype == mtype && (fb->stype == stype || stype == 0)) {
|
||||
len += fb->len;
|
||||
}
|
||||
fb = fb->next;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
//根据fb->mtype 识别fb携带的数据为 视频,音频,图片,字幕
|
||||
MEDIA_DATA_CATEGORY fb_category(struct framebuff *fb)
|
||||
{
|
||||
if (fb->mtype < 32) { //video
|
||||
return MEDIA_DATA_VIDEO;
|
||||
} else if (fb->mtype < 64) { //audio
|
||||
return MEDIA_DATA_AUDIO;
|
||||
} else if (fb->mtype < 96) { //picture
|
||||
return MEDIA_DATA_PICTURE;
|
||||
} else if (fb->mtype < 128) { //text
|
||||
return MEDIA_DATA_TEXT;
|
||||
} else {
|
||||
return MEDIA_DATA_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
463
sdk/lib/multimedia/msi/msi.c
Normal file
463
sdk/lib/multimedia/msi/msi.c
Normal file
@@ -0,0 +1,463 @@
|
||||
#include "basic_include.h"
|
||||
#include "lib/multimedia/msi.h"
|
||||
|
||||
#define MSI_MATCH(msi, name) (os_strcmp((msi)->name, name)==0)
|
||||
|
||||
//媒体流组件列表
|
||||
struct msi_core {
|
||||
struct msi *list;
|
||||
struct os_mutex lock;
|
||||
} g_MSI;
|
||||
|
||||
static void msi_free(struct msi *msi);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//局部函数
|
||||
|
||||
void msi_get(struct msi *msi)
|
||||
{
|
||||
if (msi) {
|
||||
atomic_inc(&msi->users);
|
||||
}
|
||||
}
|
||||
|
||||
void msi_put(struct msi *msi)
|
||||
{
|
||||
uint32 bound;
|
||||
uint32 inited;
|
||||
if (msi) {
|
||||
ASSERT(atomic_read(&msi->users) > 0);
|
||||
bound = atomic_read(&msi->bound);
|
||||
inited = atomic_read(&msi->inited);
|
||||
if (atomic_dec_and_test(&msi->users) && inited == 0) {
|
||||
msi_do_cmd(msi, MSI_CMD_POST_DESTROY, 0, 0);
|
||||
msi->priv = NULL;
|
||||
msi->action = NULL;
|
||||
fbq_destory(&msi->fbQ);
|
||||
if (bound == 0) {
|
||||
msi_free(msi);
|
||||
}else{
|
||||
msi->deleted = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int32 msi_bound(struct msi *msi, struct msi *o_if)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MSI_OUTIF_MAX; i++) {
|
||||
if (msi->output_list[i] == o_if) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32 msi_bind(struct msi *msi, struct msi *o_if)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (msi_bound(msi, o_if)) {
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
for (i = 0; i < MSI_OUTIF_MAX; i++) {
|
||||
if (msi->output_list[i] == NULL) {
|
||||
atomic_inc(&o_if->bound);
|
||||
msi->output_list[i] = o_if;
|
||||
return RET_OK;
|
||||
}
|
||||
}
|
||||
|
||||
os_printf(KERN_ERR"msi %s bind %s fail!\r\n", msi->name, o_if->name);
|
||||
return RET_ERR;
|
||||
}
|
||||
|
||||
static int32 msi_unbind(struct msi *msi, struct msi *o_if)
|
||||
{
|
||||
int i;
|
||||
uint32 flag;
|
||||
uint32 users;
|
||||
struct msi *out;
|
||||
|
||||
for (i = 0; i < MSI_OUTIF_MAX; i++) {
|
||||
flag = disable_irq();
|
||||
out = msi->output_list[i];
|
||||
if (out && (out == o_if || o_if == NULL)) {
|
||||
msi->output_list[i] = NULL;
|
||||
enable_irq(flag);
|
||||
|
||||
users = atomic_read(&out->users);
|
||||
if (atomic_dec_and_test(&out->bound) && users == 0) {
|
||||
msi_free(out); //引用计数 和 绑定计数都等于0,释放msi
|
||||
}
|
||||
|
||||
if (o_if) {
|
||||
return RET_OK;
|
||||
}
|
||||
}
|
||||
enable_irq(flag);
|
||||
}
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
//该函数需要在lock保护下执行
|
||||
static struct msi *msi_find_lock(const char *name)
|
||||
{
|
||||
uint32 flag = disable_irq();
|
||||
struct msi *msi = g_MSI.list;
|
||||
while (msi) {
|
||||
if (MSI_MATCH(msi, name)) {
|
||||
break;
|
||||
}
|
||||
msi = msi->next;
|
||||
}
|
||||
enable_irq(flag);
|
||||
return msi;
|
||||
}
|
||||
|
||||
//该函数需要在lock保护下执行
|
||||
static void msi_list_add(struct msi *msi)
|
||||
{
|
||||
uint32 flag = disable_irq();
|
||||
msi->next = g_MSI.list;
|
||||
g_MSI.list = msi;
|
||||
enable_irq(flag);
|
||||
}
|
||||
|
||||
//该函数需要在lock保护下执行
|
||||
static int32 msi_list_del(struct msi *msi)
|
||||
{
|
||||
struct msi *ptr, *prev;
|
||||
uint32 flag = disable_irq();
|
||||
prev = NULL;
|
||||
ptr = g_MSI.list;
|
||||
while (ptr) {
|
||||
if (ptr == msi) {
|
||||
if (prev) {
|
||||
prev->next = ptr->next;
|
||||
} else {
|
||||
g_MSI.list = ptr->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
prev = ptr;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
enable_irq(flag);
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
//该函数需要在lock保护下执行
|
||||
static void msi_notify_up(struct msi *msi, uint32 cmd, uint32 param1, uint32 param2)
|
||||
{
|
||||
struct msi *ptr = g_MSI.list;
|
||||
while (ptr) {
|
||||
if (ptr != msi && msi_bound(ptr, msi)) {
|
||||
msi_do_cmd(ptr, cmd, param1, param2);
|
||||
msi_notify_up(ptr, cmd, param1, param2);
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
}
|
||||
|
||||
//该函数需要在lock保护下执行
|
||||
static struct msi *msi_new_lock(const char *name)
|
||||
{
|
||||
struct msi *msi = msi_find_lock(name);
|
||||
if (msi == NULL) {
|
||||
msi = os_zalloc(sizeof(struct msi));
|
||||
if (msi) {
|
||||
msi->name = name;
|
||||
msi_list_add(msi);
|
||||
}
|
||||
}
|
||||
return msi;
|
||||
}
|
||||
|
||||
static void msi_free(struct msi *msi)
|
||||
{
|
||||
if (msi) {
|
||||
msi_list_del(msi);
|
||||
msi_unbind(msi, NULL); //取消所有关联的组件
|
||||
os_free(msi);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//全局函数
|
||||
struct msi *msi_new(const char *name, uint32 qsize, uint8 *isnew)
|
||||
{
|
||||
struct msi *msi;
|
||||
|
||||
os_mutex_lock(&g_MSI.lock, osWaitForever);
|
||||
msi = msi_new_lock(name);
|
||||
if(msi && msi->deleted){
|
||||
os_printf(KERN_ERR"msi %s destory running ....\r\n", name);
|
||||
msi = NULL;
|
||||
}
|
||||
|
||||
if (msi) {
|
||||
if(isnew) {
|
||||
*isnew = msi->inited.counter == 0;
|
||||
}
|
||||
atomic_inc(&msi->inited);
|
||||
atomic_inc(&msi->users);
|
||||
if (!msi->fbQ.init && qsize) {
|
||||
fbq_init(&msi->fbQ, NULL, qsize+1);
|
||||
}
|
||||
}
|
||||
os_mutex_unlock(&g_MSI.lock);
|
||||
return msi;
|
||||
}
|
||||
|
||||
void msi_destroy(struct msi *msi)
|
||||
{
|
||||
if (msi) {
|
||||
os_printf(KERN_ERR"msi %s destory, lr:%x\r\n", msi->name, RETURN_ADDR());
|
||||
os_mutex_lock(&g_MSI.lock, osWaitForever);
|
||||
if(atomic_dec_and_test(&msi->inited)){
|
||||
msi->enable = 0;
|
||||
msi->deleted = 1;
|
||||
msi_do_cmd(msi, MSI_CMD_PRE_DESTROY, 0, 0);
|
||||
msi_put(msi);
|
||||
}else{
|
||||
atomic_dec(&msi->users);
|
||||
}
|
||||
os_mutex_unlock(&g_MSI.lock);
|
||||
}
|
||||
}
|
||||
|
||||
//根据ID查找是否存在该组件,引用计数为加1,使用后需要执行 msi_put
|
||||
struct msi *msi_find(const char *name, uint8 inited)
|
||||
{
|
||||
struct msi *msi;
|
||||
|
||||
if(name == NULL){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
os_mutex_lock(&g_MSI.lock, osWaitForever);
|
||||
msi = msi_find_lock(name);
|
||||
if(msi && inited && atomic_read(&msi->inited) == 0){
|
||||
msi = NULL;
|
||||
}else{
|
||||
|
||||
msi_get(msi);
|
||||
}
|
||||
os_mutex_unlock(&g_MSI.lock);
|
||||
return msi;
|
||||
}
|
||||
|
||||
//设置该组件的输出组件
|
||||
int32 msi_add_output(struct msi *msi, const char *lname, const char *oname)
|
||||
{
|
||||
int32 ret = RET_ERR;
|
||||
struct msi *l_msi, *o_msi;
|
||||
|
||||
os_mutex_lock(&g_MSI.lock, osWaitForever);
|
||||
l_msi = msi ? msi : msi_new_lock(lname);
|
||||
o_msi = msi_new_lock(oname);
|
||||
if (l_msi && o_msi) {
|
||||
ret = msi_bind(l_msi, o_msi);
|
||||
}
|
||||
os_mutex_unlock(&g_MSI.lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//删除该组件的输出组件
|
||||
int32 msi_del_output(struct msi *msi, const char *lname, const char *oname)
|
||||
{
|
||||
int32 ret = RET_ERR;
|
||||
struct msi *l_msi, *o_msi;
|
||||
|
||||
os_mutex_lock(&g_MSI.lock, osWaitForever);
|
||||
l_msi = msi ? msi : msi_find(lname, 0);
|
||||
o_msi = msi_find(oname, 0);
|
||||
if (l_msi && o_msi) {
|
||||
ret = msi_unbind(l_msi, o_msi);
|
||||
}
|
||||
|
||||
if(msi == NULL){
|
||||
msi_put(l_msi);
|
||||
}
|
||||
msi_put(o_msi);
|
||||
os_mutex_unlock(&g_MSI.lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//组件输出cmd: 遍历自己的输出组件列表,调用输出组件的 action 接口
|
||||
int32 msi_output_cmd(struct msi *msi, uint32 cmd, uint32 param1, uint32 param2)
|
||||
{
|
||||
int8 i;
|
||||
|
||||
if(!msi){
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
for (i = 0; i < MSI_OUTIF_MAX; i++) {
|
||||
msi_do_cmd(msi->output_list[i], cmd, param1, param2);
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
//组件输出framebuff: 输出framebuff给自己的组件列表
|
||||
//注意:fb参数只能是 framebuff链表的第1个节点,不能是中间节点
|
||||
//如果fb为空,就是检查是否有数据流接收(返回0x80000000)
|
||||
int32 msi_output_fb(struct msi *msi, struct framebuff *fb)
|
||||
{
|
||||
int ret = 0;
|
||||
int8 i;
|
||||
uint32 flag;
|
||||
struct msi *out;
|
||||
|
||||
if(!msi || !msi->enable){
|
||||
fb_put(fb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < MSI_OUTIF_MAX; i++) {
|
||||
flag = disable_irq();
|
||||
out = msi->output_list[i];
|
||||
if(out && out->enable){
|
||||
msi_get(out);
|
||||
}else{
|
||||
out = NULL;
|
||||
}
|
||||
enable_irq(flag);
|
||||
|
||||
if(out && out->enable){
|
||||
if(!fb){
|
||||
ret = -1;
|
||||
msi_put(out);
|
||||
break;
|
||||
} else {
|
||||
if(msi_do_cmd(out, MSI_CMD_TRANS_FB, (uint32)fb, 0) == RET_OK){
|
||||
if(out->fbQ.init && fbq_enqueue(&out->fbQ, fb)){
|
||||
msi_do_cmd(out, MSI_CMD_TRANS_FB_END, (uint32)fb, 0);
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
msi_put(out);
|
||||
}
|
||||
|
||||
fb_put(fb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//组件不输出framebuff,需要删除framebuff
|
||||
//注意:fb参数只能是 framebuff链表的第1个节点,不能是中间节点
|
||||
int32 msi_delete_fb(struct msi *msi, struct framebuff *fb)
|
||||
{
|
||||
fb_put(fb);
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
struct framebuff *msi_get_fb(struct msi *msi, uint32 tmo_ms)
|
||||
{
|
||||
if(msi && msi->fbQ.init){
|
||||
return fbq_dequeue(&msi->fbQ, tmo_ms);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct framebuff *msi_get_fb_r(struct msi *msi, uint32 reader)
|
||||
{
|
||||
if(msi && msi->fbQ.init){
|
||||
return fbq_dequeue_r(&msi->fbQ, reader);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//追踪某个framebuff当前在被哪些模块处理
|
||||
static int32 msi_fb_trace_discard(struct msi *msi, struct framebuff *fb, uint8 discard)
|
||||
{
|
||||
int8 i;
|
||||
uint32 flag;
|
||||
struct msi *out;
|
||||
|
||||
if(msi == NULL){
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
if(fbq_trace(&msi->fbQ, fb, discard)){
|
||||
os_printf(KERN_ERR"MSI:%s framebuff %p is here!\r\n", msi->name, fb);
|
||||
}
|
||||
|
||||
for (i = 0; i < MSI_OUTIF_MAX; i++) {
|
||||
flag = disable_irq();
|
||||
out = msi->output_list[i];
|
||||
msi_get(out);
|
||||
enable_irq(flag);
|
||||
|
||||
if(out && out->fbQ.init && fbq_trace(&msi->fbQ, fb, discard)){
|
||||
os_printf(KERN_ERR"MSI:%s framebuff %p is here!\r\n", msi->name, fb);
|
||||
}
|
||||
|
||||
msi_put(out);
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
//追踪framebuff:查看指定的framebuff当前在被哪些模块处理
|
||||
int32 msi_trace_fb(struct msi *msi, struct framebuff *fb)
|
||||
{
|
||||
return msi_fb_trace_discard(msi, fb, 0);
|
||||
}
|
||||
|
||||
//通知通路中的各个模块,丢弃指定的framebuff
|
||||
int32 msi_discard_fb(struct msi *msi, struct framebuff *fb)
|
||||
{
|
||||
return msi_fb_trace_discard(msi, fb, 1);
|
||||
}
|
||||
|
||||
//组件产生notify操作,可以向上/向下传递
|
||||
void msi_notify(struct msi *msi, uint32 cmd, uint32 param1, uint32 param2)
|
||||
{
|
||||
os_mutex_lock(&g_MSI.lock, osWaitForever);
|
||||
msi_notify_up(msi, cmd, param1, param2); //向上
|
||||
os_mutex_unlock(&g_MSI.lock);
|
||||
msi_output_cmd(msi, cmd, param1, param2); //向下
|
||||
}
|
||||
|
||||
//组件执行自己的action
|
||||
int32 msi_do_cmd(struct msi *msi, uint32 cmd, uint32 param1, uint32 param2)
|
||||
{
|
||||
uint32 flag;
|
||||
int32 ret = RET_OK;
|
||||
msi_action action;
|
||||
|
||||
if(msi){
|
||||
flag = disable_irq();
|
||||
action = msi->action;
|
||||
enable_irq(flag);
|
||||
if(action){
|
||||
ret = action(msi, cmd, param1, param2);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//指定从某个组件开始执行cmd
|
||||
void msi_cmd(const char *name, uint32 cmd, uint32 param1, uint32 param2)
|
||||
{
|
||||
struct msi *msi = msi_find(name, 0);
|
||||
if (msi) {
|
||||
msi_do_cmd(msi, cmd, param1, param2);
|
||||
msi_output_cmd(msi, cmd, param1, param2);
|
||||
msi_put(msi);
|
||||
}
|
||||
}
|
||||
|
||||
int32 msi_core_init()
|
||||
{
|
||||
os_mutex_init(&g_MSI.lock);
|
||||
return RET_OK;
|
||||
}
|
||||
51
sdk/lib/multimedia/msi/utils.c
Normal file
51
sdk/lib/multimedia/msi/utils.c
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "basic_include.h"
|
||||
#include "lib/multimedia/msi.h"
|
||||
|
||||
|
||||
static int32 msi_rbuffer_fb_exist(struct rbuffer *rb, struct framebuff *fb, uint32 start, uint32 end)
|
||||
{
|
||||
uint32 i = 0;
|
||||
struct framebuff **q = (struct framebuff **)rb->rbq;
|
||||
|
||||
for (i = start; i < end; i++) {
|
||||
if (q[i] == fb) {
|
||||
return 1;
|
||||
} else {
|
||||
struct framebuff *next = q[i]->next;
|
||||
while (next) {
|
||||
if (next == fb) {
|
||||
return 1;
|
||||
} else {
|
||||
next = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32 msi_rbuffer_trace_fb(struct msi *mif, struct rbuffer *rb, struct framebuff *fb)
|
||||
{
|
||||
uint32 rpos = rb->rpos;
|
||||
uint32 wpos = rb->wpos;
|
||||
|
||||
if (!RB_EMPTY(rb)) {
|
||||
if (rpos < wpos) {
|
||||
if (msi_rbuffer_fb_exist(rb, fb, rpos, wpos)) {
|
||||
os_printf(KERN_NOTICE"MSI %s: FB %p is here!\r\n", mif->name, fb);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (msi_rbuffer_fb_exist(rb, fb, rpos, rb->qsize)) {
|
||||
os_printf(KERN_NOTICE"MSI %s: FB %p is here!\r\n", mif->name, fb);
|
||||
return 1;
|
||||
}
|
||||
if (msi_rbuffer_fb_exist(rb, fb, 0, wpos)) {
|
||||
os_printf(KERN_NOTICE"MSI %s: FB %p is here!\r\n", mif->name, fb);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
318
sdk/lib/multimedia/msi_file/msi_file.c
Normal file
318
sdk/lib/multimedia/msi_file/msi_file.c
Normal file
@@ -0,0 +1,318 @@
|
||||
#include "basic_include.h"
|
||||
#include "lib/posix/stdio.h"
|
||||
#include "lib/multimedia/msi.h"
|
||||
#include "fatfs/osal_file.h"
|
||||
#include "fatfs/ff.h"
|
||||
|
||||
#define MSI_URLFILE (0)
|
||||
#if MSI_URLFILE
|
||||
#include "lib/net/urlfile/urlfile.h"
|
||||
#endif
|
||||
|
||||
enum MSI_FILE_CMD {
|
||||
MSI_FILE_CMD_OPEN,
|
||||
MSI_FILE_CMD_CLOSE,
|
||||
};
|
||||
|
||||
enum MSI_FILE_STATE {
|
||||
MSI_FILE_STATE_IDLE,
|
||||
MSI_FILE_STATE_OPEN,
|
||||
MSI_FILE_STATE_READ,
|
||||
};
|
||||
|
||||
#define MSI_FILENAME_MAX (512)
|
||||
#define MSI_FILE_CMD_MAX (8)
|
||||
#define MSI_FILE_NUM_TRY (5)
|
||||
|
||||
#define MSIFILE_DBG(fmt, ...) os_printf("%s:%d::"fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#define MSIFILE_ERR(fmt, ...) os_printf(KERN_ERR"%s:%d::"fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
|
||||
#define URLFILE(file) (os_strstr(file, "://") != NULL)
|
||||
|
||||
struct msi_file {
|
||||
struct os_task task;
|
||||
struct msi *msi;
|
||||
const char *name;
|
||||
uint8 running: 1, eof: 1, filelist: 1, rev: 5;
|
||||
uint8 state;
|
||||
uint16 file_idx;
|
||||
void *fp;
|
||||
char file_name[MSI_FILENAME_MAX];
|
||||
char *file_ext; //文件扩展名
|
||||
atomic8_t fb_cnt; //未被释放的framebuff个数
|
||||
uint8 fb_max; //允许使用的framebuff最大个数
|
||||
uint8 speed; //读取速度,用于倍速播放控制
|
||||
RBUFFER_DEF(cmd, uint8, MSI_FILE_CMD_MAX);
|
||||
};
|
||||
|
||||
extern void *fopen(const char *filename, const char *mode);
|
||||
extern size_t fread(void *ptr, size_t size, size_t nmemb, void *stream);
|
||||
extern int fclose(void *stream);
|
||||
|
||||
static void *msi_filestream_fopen(struct msi_file *msifile)
|
||||
{
|
||||
#if MSI_URLFILE
|
||||
if(URLFILE(msifile->file_name))
|
||||
return uf_open(msifile->file_name, 0);
|
||||
#endif
|
||||
return fopen(msifile->file_name, "r");
|
||||
}
|
||||
static int msi_filestream_fclose(struct msi_file *msifile)
|
||||
{
|
||||
if(msifile->fp){
|
||||
#if MSI_URLFILE
|
||||
if(URLFILE(msifile->file_name))
|
||||
return uf_close(msifile->fp);
|
||||
#endif
|
||||
return fclose(msifile->fp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static size_t msi_filestream_fread(void *ptr, size_t size, size_t nmemb, struct msi_file *msifile)
|
||||
{
|
||||
#if MSI_URLFILE
|
||||
if(URLFILE(msifile->file_name))
|
||||
return uf_read(ptr, size, nmemb, msifile->fp);
|
||||
#endif
|
||||
return fread(ptr, size, nmemb, msifile->fp);
|
||||
}
|
||||
static int msi_filestream_fseek(struct msi_file *msifile, off_t offset, int whence)
|
||||
{
|
||||
#if MSI_URLFILE
|
||||
if(URLFILE(msifile->file_name))
|
||||
return uf_seek(msifile->fp, offset, whence);
|
||||
#endif
|
||||
return fseek(msifile->fp, offset, whence);//
|
||||
}
|
||||
static int msi_filestream_feof(struct msi_file *msifile)
|
||||
{
|
||||
#if MSI_URLFILE
|
||||
if(URLFILE(msifile->file_name))
|
||||
return uf_eof(msifile->fp);
|
||||
#endif
|
||||
return feof(msifile->fp);
|
||||
}
|
||||
|
||||
static int32 msi_filestream_action(struct msi *msi, uint32 cmd_id, uint32 param1, uint32 param2)
|
||||
{
|
||||
int32 ret = RET_OK;
|
||||
struct msi_file *msifile = (struct msi_file *)msi->priv;
|
||||
|
||||
switch (cmd_id) {
|
||||
case MSI_CMD_POST_DESTROY:
|
||||
while (msifile->running) {
|
||||
os_sleep_ms(10);
|
||||
}
|
||||
os_free(msifile);
|
||||
break;
|
||||
case MSI_CMD_FREE_FB:
|
||||
break;
|
||||
case MSI_CMD_SET_SPEED: //设置播放倍速,控制数据读取行为
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void msi_filestream_check_filename(struct msi_file *msifile)
|
||||
{
|
||||
msifile->filelist = 0;
|
||||
msifile->file_idx = 0;
|
||||
|
||||
//检查是否存在文件列表,支持自动切换文件,格式要求:xxxx_001.avi
|
||||
if (msifile->file_ext) {
|
||||
char *ptr = msifile->file_ext - 4;
|
||||
if (ptr[0] == '_' && isdigit(ptr[1]) && isdigit(ptr[2]) && isdigit(ptr[3])) {
|
||||
msifile->filelist = 1;
|
||||
msifile->file_idx = os_atoi(ptr + 1);
|
||||
MSIFILE_DBG("start index %d\r\n", msifile->file_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int32 msi_filestream_open_file(struct msi_file *msifile)
|
||||
{
|
||||
char *ptr;
|
||||
int32 i = 0;
|
||||
|
||||
msi_filestream_fclose(msifile);
|
||||
if (msifile->eof) {
|
||||
if (!msifile->filelist) {
|
||||
MSIFILE_ERR("read over!\r\n");
|
||||
msifile->state = MSI_FILE_STATE_IDLE;
|
||||
SYSEVT_NEW_MEDIA_EVT(SYSEVT_MEDIA_READ_EOF, (uint32)msifile);
|
||||
return RET_ERR;
|
||||
}
|
||||
|
||||
ptr = msifile->file_ext - 4;
|
||||
for (i = 1; i <= MSI_FILE_NUM_TRY; i++) {
|
||||
os_sprintf(ptr + 1, "%03d", msifile->file_idx + i);
|
||||
msifile->fp = msi_filestream_fopen(msifile->file_name);
|
||||
if (msifile->fp) {
|
||||
MSIFILE_ERR("open %s success!\r\n", msifile->file_name);
|
||||
msifile->file_idx += i;
|
||||
msifile->state = MSI_FILE_STATE_READ;
|
||||
msifile->eof = 0;
|
||||
return RET_OK;
|
||||
} else {
|
||||
MSIFILE_ERR("open %s fail!\r\n", msifile->file_name);
|
||||
}
|
||||
}
|
||||
|
||||
os_sprintf(ptr + 1, "%03d", msifile->file_idx);
|
||||
MSIFILE_ERR("read over! last file:%s\r\n", msifile->file_name);
|
||||
SYSEVT_NEW_MEDIA_EVT(SYSEVT_MEDIA_READ_EOF, (uint32)msifile);
|
||||
} else {
|
||||
msifile->file_ext = os_strrchr(msifile->file_name, '.'); //文件扩展名
|
||||
msifile->fp = msi_filestream_fopen(msifile->file_name);
|
||||
if (msifile->fp) {
|
||||
msi_filestream_check_filename(msifile);
|
||||
} else {
|
||||
SYSEVT_NEW_MEDIA_EVT(SYSEVT_MEDIA_OPEN_FAIL, (uint32)msifile);
|
||||
MSIFILE_ERR("open %s fail!\r\n", msifile->file_name);
|
||||
}
|
||||
}
|
||||
|
||||
//根据扩展名,加载对应的插件
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
msifile->state = msifile->fp ? MSI_FILE_STATE_READ : MSI_FILE_STATE_IDLE;
|
||||
return msifile->fp ? RET_OK : RET_ERR;
|
||||
}
|
||||
|
||||
static int32 msi_filestream_read_file(struct msi_file *msifile, struct framebuff **fb)
|
||||
{
|
||||
if (atomic_read(&msifile->fb_cnt) >= msifile->fb_max + 1) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
//插件参与控制读取数据的行为: 比如 按帧读取数据,倍速跳帧
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static void msi_filestream_proc_cmd(struct msi_file *msifile)
|
||||
{
|
||||
uint8 cmd;
|
||||
while (!RB_EMPTY(&msifile->cmd)) {
|
||||
RB_GET(&msifile->cmd, cmd);
|
||||
switch (cmd) {
|
||||
case MSI_FILE_CMD_OPEN:
|
||||
msifile->eof = 0;
|
||||
msifile->state = MSI_FILE_STATE_OPEN;
|
||||
break;
|
||||
case MSI_FILE_CMD_CLOSE:
|
||||
msifile->state = MSI_FILE_STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void msi_filestream_task(void *arg)
|
||||
{
|
||||
int32 ret = 0;
|
||||
struct framebuff *fb = NULL;
|
||||
struct msi_file *msifile = (struct msi_file *)arg;
|
||||
|
||||
MSIFILE_DBG("msi_file running ...\r\n");
|
||||
msifile->running = 1;
|
||||
while (msifile->msi->enable) {
|
||||
msi_filestream_proc_cmd(msifile);
|
||||
switch (msifile->state) {
|
||||
case MSI_FILE_STATE_IDLE:
|
||||
os_sleep_ms(10);
|
||||
break;
|
||||
case MSI_FILE_STATE_OPEN:
|
||||
msi_filestream_open_file(msifile);
|
||||
break;
|
||||
case MSI_FILE_STATE_READ:
|
||||
fb = NULL;
|
||||
ret = msi_filestream_read_file(msifile, &fb);
|
||||
if (fb) {
|
||||
atomic_inc(&msifile->fb_cnt);
|
||||
msi_output_fb(msifile->msi, fb);
|
||||
}
|
||||
|
||||
if (msifile->eof) { //当前文件读取结束
|
||||
msifile->state = MSI_FILE_STATE_OPEN;
|
||||
} else if (ret < 0 && msifile->msi->enable) {
|
||||
os_sleep_ms(10);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (msifile->fp) {
|
||||
msi_filestream_fclose(msifile);
|
||||
msifile->fp = NULL;
|
||||
}
|
||||
|
||||
MSIFILE_DBG("msi_file stopped!\r\n");
|
||||
msifile->running = 0;
|
||||
}
|
||||
|
||||
void *msi_filestream_init(const char *name, uint8 fb_max)
|
||||
{
|
||||
struct msi_file *msifile = os_zalloc(sizeof(struct msi_file));
|
||||
if (msifile == NULL) {
|
||||
MSIFILE_ERR("no memory!\r\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RB_INIT(&msifile->cmd, MSI_FILE_CMD_MAX);
|
||||
msifile->fb_max = fb_max;
|
||||
msifile->name = name;
|
||||
msifile->msi = msi_new(name, 0, NULL);
|
||||
if (msifile->msi == NULL) {
|
||||
os_free(msifile);
|
||||
MSIFILE_ERR("no memory!\r\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ASSERT(msifile->msi->priv == NULL);
|
||||
msifile->msi->priv = msifile;
|
||||
msifile->msi->action = msi_filestream_action;
|
||||
msifile->msi->enable = 1;
|
||||
OS_TASK_INIT(name, &msifile->task, msi_filestream_task, msifile, OS_TASK_PRIORITY_NORMAL, NULL,1024);
|
||||
MSIFILE_DBG("msi_file %s init!\r\n", name);
|
||||
return msifile;
|
||||
}
|
||||
|
||||
int32 msi_filestream_destory(void *hdl)
|
||||
{
|
||||
if (hdl) {
|
||||
struct msi_file *msifile = (struct msi_file *)hdl;
|
||||
msi_destroy(msifile->msi); //msi被释放时会通过MSI_CMD_DESTORY通知msifile释放资源
|
||||
return RET_OK;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int32 msi_filestream_close(void *hdl)
|
||||
{
|
||||
if (hdl) {
|
||||
struct msi_file *msifile = (struct msi_file *)hdl;
|
||||
RB_SET(&msifile->cmd, MSI_FILE_CMD_CLOSE);
|
||||
return RET_OK;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int32 msi_filestream_open(void *hdl, char *file)
|
||||
{
|
||||
if (hdl) {
|
||||
struct msi_file *msifile = (struct msi_file *)hdl;
|
||||
if (os_strlen(file) >= MSI_FILENAME_MAX) {
|
||||
return -ENAMETOOLONG;
|
||||
}
|
||||
os_strcpy(msifile->file_name, file);
|
||||
RB_SET(&msifile->cmd, MSI_FILE_CMD_OPEN);
|
||||
return RET_OK;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user