Older/MediaServer/libmpeg/source/mpeg-packet.c
amass 08340ad5c8
All checks were successful
Deploy / PullDocker (push) Successful in 11s
Deploy / Build (push) Successful in 1m54s
add codec.
2024-09-30 16:12:57 +00:00

244 lines
8.3 KiB
C

#include "mpeg-pes-internal.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#define MPEG_PACKET_PAYLOAD_MAX_SIZE (10 * 1024 * 1024)
typedef int (*h2645_find_new_access)(const uint8_t* p, size_t bytes, int* vcl);
static int mpeg_packet_append(struct packet_t* pkt, const void* data, size_t size)
{
void* ptr;
// fix: pkt->size + size bits wrap
if (pkt->size + size > MPEG_PACKET_PAYLOAD_MAX_SIZE || pkt->size + size < pkt->size)
return -EINVAL;
if (pkt->capacity < pkt->size + size)
{
ptr = realloc(pkt->data, pkt->size + size + 2048);
if (NULL == ptr) return -ENOMEM;
pkt->data = (uint8_t*)ptr;
pkt->capacity = pkt->size + size + 2048;
}
// append new data
memcpy(pkt->data + pkt->size, data, size);
pkt->size += size;
return 0;
}
static int mpeg_packet_h264_h265_filter(uint16_t program, uint16_t stream, struct packet_t* pkt, const uint8_t* data, size_t size, pes_packet_handler handler, void* param)
{
int i;
size_t off;
size_t leading;
//assert(0 == pes->len || pes->payload.len == pes->len);
// skip AUD
for (off = i = 0; off < size; off += i + 1)
{
i = mpeg_h264_find_nalu(data + off, size - off, &leading);
if (i < 0)
{
assert(0);
return -1;
}
//assert(0 == i - leading);
if (PSI_STREAM_H264 == pkt->codecid ? 9 == (data[off + i] & 0x1f) : 35 == ((data[off + i] >> 1) & 0x3f))
continue;
i -= (int)leading; // rewind to 0x00 00 00 01
break;
}
// TODO: check size > 0 ???
return handler(param, program, stream, pkt->codecid, pkt->flags, pkt->pts, pkt->dts, data + off + i, size - off - i);
}
// @param[out] consume used of new append data
static int mpeg_packet_h26x(struct packet_t* pkt, const struct pes_t* pes, size_t size, size_t* consume, pes_packet_handler handler, void* param)
{
int r, n;
const uint8_t* p, *end, *data;
h2645_find_new_access find;
n = PSI_STREAM_H264 == pes->codecid ? 4 : 5;
data = pkt->data;
end = pkt->data + pkt->size;
p = pkt->size - size < n ? pkt->data : end - size - n; // start from trailing nalu
// TODO: The first frame maybe not a valid frame, filter it
if (0 == pkt->codecid)
{
pkt->pts = pes->pts;
pkt->dts = pes->dts;
pkt->sid = pes->sid;
pkt->codecid = pes->codecid;
pkt->flags = pes->flags;
}
// PES contain multiple packet
find = PSI_STREAM_H264 == pkt->codecid ? mpeg_h264_find_new_access_unit : (PSI_STREAM_H265 == pkt->codecid ? mpeg_h265_find_new_access_unit : mpeg_h266_find_new_access_unit);
n = find(p, end - p, &pkt->vcl);
while (n >= 0)
{
assert(pkt->vcl > 0);
if (MPEG_VCL_CORRUPT == pkt->vcl)
{
// video data contain 00 00 01 BA
// maybe previous packet data lost
r = (p + n - pkt->data) - (pkt->size - *consume);
assert(r >= 0 && r <= *consume); // r == 0: previous packet lost, new start code find
*consume = (r < 0 || r > *consume) ? *consume : r;
pkt->flags |= MPEG_FLAG_PACKET_CORRUPT;
pkt->size = 0; // clear
pkt->vcl = 0;
// todo: handle packet data ???
return 0;
}
p += n;
pkt->flags = (pkt->flags & (~MPEG_FLAG_IDR_FRAME)) | (1 == pkt->vcl ? MPEG_FLAG_IDR_FRAME : 0); // update key frame flags
r = mpeg_packet_h264_h265_filter(pes->pn, pes->pid, pkt, data, p - data, handler, param);
if (0 != r)
return r;
data = p;
pkt->vcl = 0; // next frame
n = find(p, end - p, &pkt->vcl);
}
// save pts/dts
pkt->pts = pes->pts;
pkt->dts = pes->dts;
pkt->sid = pes->sid;
pkt->flags = pes->flags;
// assert(0 == find(p, end - p)); // start with AUD
#if !defined(MPEG_KEDA_H265_FROM_H264)
pkt->codecid = pes->codecid;
#else
// fix: keda h.265 stream psm codec id incorrect, e.g. psm codec id 36 -> 27 -> 27 -> 27
if (pkt->codecid != pes->codecid && 0 == mpeg_h26x_verify(data, end - data, &r))
{
static const uint8_t sc_codecid[] = { PSI_STREAM_RESERVED, PSI_STREAM_H264, PSI_STREAM_H265, PSI_STREAM_H266, PSI_STREAM_MPEG4, };
pkt->codecid = sc_codecid[(r < 0 || r >= sizeof(sc_codecid) / sizeof(sc_codecid[0])) ? PSI_STREAM_RESERVED : r];
}
#endif
// remain data
if (data != pkt->data)
{
memmove(pkt->data, data, end - data);
pkt->size = end - data;
}
return 0;
}
static void pes_packet_codec_verify(struct pes_t* pes, struct packet_t* pkt)
{
int r;
size_t i, n;
#if defined(MPEG_GUESS_STREAM) || defined(MPEG_H26X_VERIFY)
if (pes->codecid == PSI_STREAM_RESERVED && 0 == mpeg_h26x_verify(pkt->data, pkt->size, &r))
{
// modify codecid
static const uint8_t sc_codecid[] = { PSI_STREAM_RESERVED, PSI_STREAM_H264, PSI_STREAM_H265, PSI_STREAM_H266, PSI_STREAM_MPEG4, };
pkt->codecid = pes->codecid = sc_codecid[(r < 0 || r >= sizeof(sc_codecid) / sizeof(sc_codecid[0])) ? PSI_STREAM_RESERVED : r];
}
#endif
#if defined(MPEG_DAHUA_AAC_FROM_G711)
if ((pes->codecid == PSI_STREAM_AUDIO_G711A || pes->codecid == PSI_STREAM_AUDIO_G711U)
&& pkt->size > 7 && 0xFF == pkt->data[0] && 0xF0 == (pkt->data[1] & 0xF0))
{
n = 7;
// calc mpeg4_aac_adts_frame_length
for (i = 0; i + 7 < pkt->size && n >= 7; i += n)
{
// fix n == 0
n = ((size_t)(pkt->data[i + 3] & 0x03) << 11) | ((size_t)pkt->data[i + 4] << 3) | ((size_t)(pkt->data[i + 5] >> 5) & 0x07);
}
pkt->codecid = pes->codecid = i == pkt->size ? PSI_STREAM_AAC : pes->codecid; // fix it
}
#endif
}
int pes_packet(struct packet_t* pkt, struct pes_t* pes, const void* data, size_t size, size_t* consume, int start, pes_packet_handler handler, void* param)
{
int r;
size_t total;
total = size;
*consume = size; // all saved
// use timestamp to split packet
assert(PTS_NO_VALUE != pes->dts);
if (pkt->size > 0 && (pkt->dts != pes->dts || start)
// WARNING: don't use pes->codecid
&& PSI_STREAM_H264 != pkt->codecid && PSI_STREAM_H265 != pkt->codecid && PSI_STREAM_H266 != pkt->codecid)
{
if(0 == pes->codecid)
pes_packet_codec_verify(pes, pkt); // verify on packet complete
if (PSI_STREAM_H264 != pes->codecid && PSI_STREAM_H265 != pes->codecid && PSI_STREAM_H266 != pes->codecid)
{
assert(PTS_NO_VALUE != pkt->dts);
r = handler(param, pes->pn, pes->pid, pkt->codecid, pkt->flags, pkt->pts, pkt->dts, pkt->data, pkt->size);
pkt->size = 0; // new packet start
if (0 != r)
return r;
}
else
{
assert(0 == pkt->codecid);
pkt->codecid = pes->codecid; // update previous packet codec id
total += pkt->size; // find nalu vcl
}
}
// merge buffer
r = mpeg_packet_append(pkt, data, size);
if (0 != r)
return r;
if (PSI_STREAM_H264 == pes->codecid || PSI_STREAM_H265 == pes->codecid || PSI_STREAM_H266 == pes->codecid)
{
return mpeg_packet_h26x(pkt, pes, total, consume, handler, param);
}
else
{
// save pts/dts
pkt->pts = pes->pts;
pkt->dts = pes->dts;
pkt->sid = pes->sid;
pkt->codecid = pes->codecid;
pkt->flags = pes->flags;
// for audio packet only, H.264/H.265 pes->len maybe incorrect
assert(PSI_STREAM_H264 != pes->codecid && PSI_STREAM_H265 != pes->codecid && PSI_STREAM_H266 != pes->codecid);
#if !defined(MPEG_LIVING_VIDEO_FRAME_DEMUX)
if (PES_SID_VIDEO != pes->sid)
#endif
if (pes->len > 0 && pes->pkt.size >= pes->len)
{
pes_packet_codec_verify(pes, pkt); // verify on packet complete
if (PSI_STREAM_H264 == pes->codecid || PSI_STREAM_H265 == pes->codecid || PSI_STREAM_H266 == pes->codecid)
return mpeg_packet_h26x(pkt, pes, size, consume, handler, param);
assert(pes->pkt.size == pes->len || (pkt->flags & MPEG_FLAG_PACKET_CORRUPT)); // packet lost
r = handler(param, pes->pn, pes->pid, pkt->codecid, pkt->flags, pkt->pts, pkt->dts, pes->pkt.data, pes->len);
pkt->size = 0; // new packet start
}
}
return r;
}