Revert "Merge pull request 'kernel + daemon: H.264 B-frame display reorder fix (closes #6)' (#7) from noether/kernel-daemon-h264-reorder-fix into main"
This reverts commit79256dc7ef, reversing changes made to7ff2d897ea.
This commit is contained in:
+37
-238
@@ -133,288 +133,87 @@ static int send_response(struct chardev_client *cli, uint32_t type,
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register a new (src_pts → cookie) mapping in the pending table.
|
||||
* Reuses an existing slot for src_pts if one exists (defensive — the
|
||||
* kernel should never re-use the same src_pts for two live cookies,
|
||||
* but libva running against a test client without timestamps might
|
||||
* send all-zero src_pts; collapse them onto the latest cookie so the
|
||||
* 1:1-per-stream case keeps working). Returns 0 on success, -ENOSPC
|
||||
* if the table is full.
|
||||
*/
|
||||
static int pending_register(struct chardev_client *cli, uint64_t src_pts,
|
||||
uint32_t cookie,
|
||||
const struct daedalus_req_decode *req)
|
||||
{
|
||||
int free_slot = -1;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DAEDALUS_MAX_PENDING_COOKIES; i++) {
|
||||
if (cli->pending[i].used && cli->pending[i].src_pts == src_pts) {
|
||||
cli->pending[i].cookie = cookie;
|
||||
cli->pending[i].cached_req = *req;
|
||||
return 0;
|
||||
}
|
||||
if (!cli->pending[i].used && free_slot < 0)
|
||||
free_slot = i;
|
||||
}
|
||||
|
||||
if (free_slot < 0) {
|
||||
log_err("pending: table full registering cookie=%u src_pts=%llu",
|
||||
cookie, (unsigned long long) src_pts);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
cli->pending[free_slot].used = 1;
|
||||
cli->pending[free_slot].src_pts = src_pts;
|
||||
cli->pending[free_slot].cookie = cookie;
|
||||
cli->pending[free_slot].cached_req = *req;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up the cookie + cached REQ_DECODE that originally introduced
|
||||
* @src_pts. Returns 0 + populates @cookie_out / @req_out, or -ENOENT
|
||||
* if no match (likely a daemon bug or codec output we can't route).
|
||||
*/
|
||||
static int pending_lookup(const struct chardev_client *cli,
|
||||
uint64_t src_pts,
|
||||
uint32_t *cookie_out,
|
||||
struct daedalus_req_decode *req_out)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DAEDALUS_MAX_PENDING_COOKIES; i++) {
|
||||
if (cli->pending[i].used &&
|
||||
cli->pending[i].src_pts == src_pts) {
|
||||
*cookie_out = cli->pending[i].cookie;
|
||||
*req_out = cli->pending[i].cached_req;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static void pending_release(struct chardev_client *cli, uint64_t src_pts)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DAEDALUS_MAX_PENDING_COOKIES; i++) {
|
||||
if (cli->pending[i].used &&
|
||||
cli->pending[i].src_pts == src_pts) {
|
||||
cli->pending[i].used = 0;
|
||||
cli->pending[i].src_pts = 0;
|
||||
cli->pending[i].cookie = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Pack the daemon's current AVFrame into the CAPTURE buffer owned by
|
||||
* @owner_cookie, then ship RESP_FRAME with the flags caller asked for.
|
||||
* Returns 0 on success; -errno on GET_DMABUF / mmap failure (RESP is
|
||||
* still emitted so the kernel doesn't park the dst buffer forever).
|
||||
*/
|
||||
static int deliver_frame_to_cookie(struct chardev_client *cli,
|
||||
uint32_t owner_cookie,
|
||||
const struct daedalus_req_decode *owner_req,
|
||||
struct daedalus_resp_frame *resp,
|
||||
uint32_t resp_flags)
|
||||
{
|
||||
struct daedalus_capture_planes planes;
|
||||
int orc;
|
||||
|
||||
orc = daedalus_capture_planes_open(cli->fd, owner_cookie, owner_req,
|
||||
&planes);
|
||||
if (orc < 0) {
|
||||
log_warn("drain: GET_DMABUF cookie=%u failed (%d); RESP metadata-only",
|
||||
owner_cookie, orc);
|
||||
} else {
|
||||
(void) daedalus_decoder_pack_current(cli->decoder, &planes,
|
||||
owner_req->capture_pix_fmt);
|
||||
daedalus_capture_planes_close(&planes);
|
||||
}
|
||||
|
||||
resp->flags |= resp_flags;
|
||||
return send_response(cli, DAEDALUS_MSG_RESP_FRAME, owner_cookie,
|
||||
resp, sizeof(*resp));
|
||||
}
|
||||
|
||||
static int handle_req_decode(struct chardev_client *cli,
|
||||
const struct daedalus_msg_hdr *hdr,
|
||||
const uint8_t *payload)
|
||||
{
|
||||
struct daedalus_req_decode req;
|
||||
struct daedalus_resp_frame resp;
|
||||
struct daedalus_capture_planes planes;
|
||||
const struct daedalus_h264_meta *h264_meta = NULL;
|
||||
size_t meta_off, meta_len = 0;
|
||||
int submit_status;
|
||||
int src_consumed_emitted = 0;
|
||||
int rc;
|
||||
int decoded = 0;
|
||||
|
||||
if (hdr->payload_len < sizeof(req)) {
|
||||
struct daedalus_resp_frame err = { 0 };
|
||||
|
||||
log_err("REQ_DECODE cookie=%u: payload too short %u < %zu",
|
||||
hdr->cookie, hdr->payload_len, sizeof(req));
|
||||
err.status = DAEDALUS_DECODE_ERR_RECV;
|
||||
err.flags = DAEDALUS_RESP_FLAG_HAS_PIXELS |
|
||||
DAEDALUS_RESP_FLAG_SRC_CONSUMED;
|
||||
memset(&resp, 0, sizeof(resp));
|
||||
resp.status = DAEDALUS_DECODE_ERR_RECV;
|
||||
return send_response(cli, DAEDALUS_MSG_RESP_FRAME,
|
||||
hdr->cookie, &err, sizeof(err));
|
||||
hdr->cookie, &resp, sizeof(resp));
|
||||
}
|
||||
memcpy(&req, payload, sizeof(req));
|
||||
|
||||
/* Optional H.264 meta block follows req when the flag is set;
|
||||
* bitstream comes after meta. */
|
||||
if (req.flags & DAEDALUS_REQ_FLAG_H264_META)
|
||||
meta_len = sizeof(struct daedalus_h264_meta);
|
||||
meta_off = sizeof(req);
|
||||
|
||||
if ((size_t) req.bitstream_len + sizeof(req) + meta_len !=
|
||||
hdr->payload_len) {
|
||||
struct daedalus_resp_frame err = { 0 };
|
||||
|
||||
log_err("REQ_DECODE cookie=%u: bitstream_len %u + meta %zu inconsistent with payload_len %u",
|
||||
hdr->cookie, req.bitstream_len, meta_len,
|
||||
hdr->payload_len);
|
||||
err.status = DAEDALUS_DECODE_ERR_RECV;
|
||||
err.flags = DAEDALUS_RESP_FLAG_HAS_PIXELS |
|
||||
DAEDALUS_RESP_FLAG_SRC_CONSUMED;
|
||||
memset(&resp, 0, sizeof(resp));
|
||||
resp.status = DAEDALUS_DECODE_ERR_RECV;
|
||||
return send_response(cli, DAEDALUS_MSG_RESP_FRAME,
|
||||
hdr->cookie, &err, sizeof(err));
|
||||
hdr->cookie, &resp, sizeof(resp));
|
||||
}
|
||||
if (meta_len)
|
||||
h264_meta = (const struct daedalus_h264_meta *)
|
||||
(payload + meta_off);
|
||||
|
||||
log_info("REQ_DECODE cookie=%u codec=%u bitstream=%u bytes meta=%s capture=%ux%u %u planes src_pts=%llu",
|
||||
log_info("REQ_DECODE cookie=%u codec=%u bitstream=%u bytes meta=%s capture=%ux%u %u planes",
|
||||
hdr->cookie, req.codec_id, req.bitstream_len,
|
||||
h264_meta ? "h264" : "none",
|
||||
req.capture_width, req.capture_height,
|
||||
req.capture_num_planes,
|
||||
(unsigned long long) req.src_pts);
|
||||
req.capture_num_planes);
|
||||
|
||||
/*
|
||||
* Register (src_pts → cookie) mapping BEFORE submit, so any drained
|
||||
* frame whose pts matches this REQ's src_pts (the steady-state
|
||||
* 1:1 path) can find its owner via pending_lookup below. Out of
|
||||
* space here is fatal — we'd lose the routing identity for this
|
||||
* cookie's eventual frame. Send an error RESP that releases both
|
||||
* src and dst so the V4L2 client moves on.
|
||||
* Open dmabuf-fds for every CAPTURE plane and mmap them.
|
||||
* If this fails we still attempt the decode (so the kernel
|
||||
* gets a structured error response) — but we pass NULL
|
||||
* planes so pixels aren't written anywhere.
|
||||
*/
|
||||
rc = pending_register(cli, req.src_pts, hdr->cookie, &req);
|
||||
rc = daedalus_capture_planes_open(cli->fd, hdr->cookie, &req,
|
||||
&planes);
|
||||
if (rc < 0) {
|
||||
struct daedalus_resp_frame err = { 0 };
|
||||
|
||||
err.status = DAEDALUS_DECODE_ERR_SEND;
|
||||
err.flags = DAEDALUS_RESP_FLAG_HAS_PIXELS |
|
||||
DAEDALUS_RESP_FLAG_SRC_CONSUMED;
|
||||
return send_response(cli, DAEDALUS_MSG_RESP_FRAME,
|
||||
hdr->cookie, &err, sizeof(err));
|
||||
log_warn("REQ_DECODE cookie=%u: GET_DMABUF/mmap failed (%d); decode metadata-only",
|
||||
hdr->cookie, rc);
|
||||
/* planes is already zeroed by capture_planes_open */
|
||||
}
|
||||
|
||||
submit_status = daedalus_decoder_submit(cli->decoder, &req,
|
||||
payload + meta_off + meta_len,
|
||||
h264_meta);
|
||||
if (submit_status != 0) {
|
||||
/*
|
||||
* avcodec_send_packet failed before any frame could have
|
||||
* been queued for this src_pts. Drop the pending entry
|
||||
* (no future drain will find a matching pts), and emit a
|
||||
* combined HAS_PIXELS|SRC_CONSUMED error RESP for this
|
||||
* cookie so the V4L2 client unblocks.
|
||||
*/
|
||||
struct daedalus_resp_frame err = { 0 };
|
||||
rc = daedalus_decoder_run_request(cli->decoder, &req,
|
||||
payload + meta_off + meta_len,
|
||||
h264_meta,
|
||||
&resp,
|
||||
planes.nr ? &planes : NULL);
|
||||
decoded = (rc >= 0);
|
||||
|
||||
pending_release(cli, req.src_pts);
|
||||
err.status = (uint32_t) submit_status;
|
||||
err.codec_id = req.codec_id;
|
||||
err.flags = DAEDALUS_RESP_FLAG_HAS_PIXELS |
|
||||
DAEDALUS_RESP_FLAG_SRC_CONSUMED;
|
||||
err.output_src_pts = req.src_pts;
|
||||
return send_response(cli, DAEDALUS_MSG_RESP_FRAME,
|
||||
hdr->cookie, &err, sizeof(err));
|
||||
}
|
||||
daedalus_capture_planes_close(&planes);
|
||||
|
||||
if (!decoded)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Drain libavcodec for as many display-ordered frames as it can
|
||||
* emit right now. Each frame's pts identifies which cookie's
|
||||
* CAPTURE buffer the pixels go in (see [[daedalus-v4l2#6]]). In
|
||||
* steady state for VP9/AV1 (no reorder) the loop runs exactly
|
||||
* once, draining the just-submitted packet's own frame. For
|
||||
* H.264 with B-frames the first drained frame may belong to an
|
||||
* EARLIER cookie's bitstream — that's the entire point.
|
||||
* RESP_FRAME is metadata-only in Phase 8.6 — pixels already
|
||||
* live in the V4L2 client's CAPTURE buffer via the dmabuf
|
||||
* the daemon wrote to in pack_nv12_to_planes.
|
||||
*/
|
||||
for (;;) {
|
||||
struct daedalus_resp_frame resp;
|
||||
uint32_t owner_cookie = 0;
|
||||
struct daedalus_req_decode owner_req;
|
||||
uint32_t flags;
|
||||
|
||||
rc = daedalus_decoder_drain_one(cli->decoder, req.codec_id,
|
||||
&resp);
|
||||
if (rc == -EAGAIN)
|
||||
break;
|
||||
if (rc != 0) {
|
||||
/*
|
||||
* Hard codec error during drain. resp->status is set.
|
||||
* Pin it to THIS REQ's cookie (we can't know whose
|
||||
* pts the failed frame would have had); set both
|
||||
* flags so the V4L2 client moves on.
|
||||
*/
|
||||
pending_release(cli, req.src_pts);
|
||||
resp.flags = DAEDALUS_RESP_FLAG_HAS_PIXELS |
|
||||
DAEDALUS_RESP_FLAG_SRC_CONSUMED;
|
||||
resp.output_src_pts = req.src_pts;
|
||||
(void) send_response(cli, DAEDALUS_MSG_RESP_FRAME,
|
||||
hdr->cookie, &resp, sizeof(resp));
|
||||
src_consumed_emitted = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pending_lookup(cli, resp.output_src_pts,
|
||||
&owner_cookie, &owner_req) != 0) {
|
||||
/*
|
||||
* Frame's pts has no registered owner — implies a
|
||||
* daemon-side tracking bug or a codec output for a
|
||||
* packet we never registered (e.g. a B-frame that
|
||||
* was queued before the daemon caught up). Drop the
|
||||
* frame; can't safely route it.
|
||||
*/
|
||||
log_warn("drain: no pending entry for output_src_pts=%llu (codec dropped a frame?)",
|
||||
(unsigned long long) resp.output_src_pts);
|
||||
continue;
|
||||
}
|
||||
|
||||
flags = DAEDALUS_RESP_FLAG_HAS_PIXELS;
|
||||
if (owner_cookie == hdr->cookie) {
|
||||
flags |= DAEDALUS_RESP_FLAG_SRC_CONSUMED;
|
||||
src_consumed_emitted = 1;
|
||||
}
|
||||
|
||||
(void) deliver_frame_to_cookie(cli, owner_cookie, &owner_req,
|
||||
&resp, flags);
|
||||
pending_release(cli, resp.output_src_pts);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the drain loop didn't already SRC_CONSUMED this REQ's cookie
|
||||
* (libavcodec held the frame for display-order reorder — the
|
||||
* pixels will arrive in a future drain), emit a standalone
|
||||
* SRC_CONSUMED RESP now. Kernel releases src_buf + runs
|
||||
* job_finish; dst_buf parked until the matching HAS_PIXELS
|
||||
* shows up later.
|
||||
*/
|
||||
if (!src_consumed_emitted) {
|
||||
struct daedalus_resp_frame resp = { 0 };
|
||||
|
||||
resp.status = DAEDALUS_DECODE_OK;
|
||||
resp.codec_id = req.codec_id;
|
||||
resp.flags = DAEDALUS_RESP_FLAG_SRC_CONSUMED;
|
||||
(void) send_response(cli, DAEDALUS_MSG_RESP_FRAME,
|
||||
hdr->cookie, &resp, sizeof(resp));
|
||||
}
|
||||
|
||||
return 0;
|
||||
return send_response(cli, DAEDALUS_MSG_RESP_FRAME, hdr->cookie,
|
||||
&resp, sizeof(resp));
|
||||
}
|
||||
|
||||
static int handle_ping(struct chardev_client *cli,
|
||||
|
||||
@@ -18,44 +18,18 @@
|
||||
struct ffmpeg_loader;
|
||||
struct daedalus_decoder;
|
||||
|
||||
/*
|
||||
* Per-inflight (cookie, src_pts) tracking for the H.264 B-frame
|
||||
* display-reorder fix (daedalus-v4l2#6). When the daemon drains a
|
||||
* frame from libavcodec, frame->pts (= src_pts of the OUTPUT bitstream
|
||||
* that contained the frame's slices) identifies which cookie's CAPTURE
|
||||
* buffer the pixels belong in — distinct from the cookie of the REQ
|
||||
* that triggered the receive_frame call. Mapping is small (bounded
|
||||
* by the V4L2 client's buffer pool depth, typically ≤24) so a linear
|
||||
* array beats a hashtable for cache-locality.
|
||||
*
|
||||
* cached_req carries the capture geometry (num_planes, plane sizes,
|
||||
* strides, pix_fmt) so a later drain — which may target this cookie
|
||||
* from a DIFFERENT REQ's drain loop — can call GET_DMABUF + open
|
||||
* planes with the original REQ's parameters.
|
||||
*/
|
||||
#define DAEDALUS_MAX_PENDING_COOKIES 64
|
||||
|
||||
struct chardev_pending_cookie {
|
||||
int used;
|
||||
uint64_t src_pts;
|
||||
uint32_t cookie;
|
||||
struct daedalus_req_decode cached_req;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct chardev_client - daemon-side chardev state
|
||||
* @fd: open /dev/daedalus-v4l2 descriptor (-1 if not open)
|
||||
* @loader: dlopen'd FFmpeg loader (borrowed; not owned)
|
||||
* @decoder: per-codec AVCodecContext cache (owned)
|
||||
* @stop_flag: set non-zero from a signal handler to break the loop
|
||||
* @pending: pts → cookie lookup table for split SRC/DST RESPs
|
||||
*/
|
||||
struct chardev_client {
|
||||
int fd;
|
||||
struct ffmpeg_loader *loader;
|
||||
struct daedalus_decoder *decoder;
|
||||
volatile sig_atomic_t *stop_flag;
|
||||
struct chardev_pending_cookie pending[DAEDALUS_MAX_PENDING_COOKIES];
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+126
-154
@@ -348,30 +348,31 @@ static int pack_nv12_to_planes(struct AVFrame *fr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Per-codec assemble + send_packet. Returns 0 on success, or one
|
||||
* of DAEDALUS_DECODE_ERR_* on failure (errors here propagate via
|
||||
* the caller's RESP_FRAME status field — they are NOT logged as a
|
||||
* silent skip). pkt->pts is stamped from req->src_pts so the
|
||||
* resulting frame->pts comes back identifiable on the drain side.
|
||||
*/
|
||||
int daedalus_decoder_submit(struct daedalus_decoder *dec,
|
||||
const struct daedalus_req_decode *req,
|
||||
const uint8_t *bitstream,
|
||||
const struct daedalus_h264_meta *h264_meta)
|
||||
int daedalus_decoder_run_request(struct daedalus_decoder *dec,
|
||||
const struct daedalus_req_decode *req,
|
||||
const uint8_t *bitstream,
|
||||
const struct daedalus_h264_meta *h264_meta,
|
||||
struct daedalus_resp_frame *resp,
|
||||
const struct daedalus_capture_planes *planes)
|
||||
{
|
||||
struct ffmpeg_loader *fm = dec->loader;
|
||||
struct AVCodecContext *ctx = NULL;
|
||||
uint8_t *assembled = NULL;
|
||||
size_t assembled_len = 0;
|
||||
int rc;
|
||||
int status = 0;
|
||||
|
||||
memset(resp, 0, sizeof(*resp));
|
||||
resp->codec_id = req->codec_id;
|
||||
|
||||
rc = decoder_open_codec(dec, req->codec_id, &ctx);
|
||||
if (rc == -ENOSYS)
|
||||
return DAEDALUS_DECODE_ERR_CODEC;
|
||||
if (rc < 0)
|
||||
return DAEDALUS_DECODE_ERR_OPEN;
|
||||
if (rc == -ENOSYS) {
|
||||
resp->status = DAEDALUS_DECODE_ERR_CODEC;
|
||||
goto out;
|
||||
}
|
||||
if (rc < 0) {
|
||||
resp->status = DAEDALUS_DECODE_ERR_OPEN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fm->av_packet_unref(dec->pkt);
|
||||
|
||||
@@ -396,14 +397,14 @@ int daedalus_decoder_submit(struct daedalus_decoder *dec,
|
||||
if (sps_len == 0 || pps_len == 0) {
|
||||
log_err("decoder: SPS/PPS NAL synth failed (sps=%zu pps=%zu)",
|
||||
sps_len, pps_len);
|
||||
status = DAEDALUS_DECODE_ERR_SEND;
|
||||
resp->status = DAEDALUS_DECODE_ERR_SEND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
assembled_len = sps_len + pps_len + req->bitstream_len;
|
||||
assembled = malloc(assembled_len + AV_INPUT_BUFFER_PADDING_SIZE);
|
||||
if (!assembled) {
|
||||
status = DAEDALUS_DECODE_ERR_SEND;
|
||||
resp->status = DAEDALUS_DECODE_ERR_SEND;
|
||||
goto out;
|
||||
}
|
||||
memcpy(assembled, sps_nal, sps_len);
|
||||
@@ -440,162 +441,133 @@ int daedalus_decoder_submit(struct daedalus_decoder *dec,
|
||||
dec->pkt->size = (int) req->bitstream_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stamp pkt->pts from REQ_DECODE's src_pts (the V4L2 OUTPUT
|
||||
* buffer's vb2 timestamp captured by the kernel at device_run
|
||||
* time). libavcodec carries pkt->pts forward to frame->pts on
|
||||
* the receive_frame side — even after display-order reordering
|
||||
* inside the H.264 DPB — which lets the chardev_client identify
|
||||
* which cookie's CAPTURE buffer a drained frame's pixels belong
|
||||
* in. Without this stamp, every drained frame would look like
|
||||
* it came from the current REQ; pairs of B/P would swap places
|
||||
* in the visible output (daedalus-v4l2#6).
|
||||
*/
|
||||
dec->pkt->pts = (int64_t) req->src_pts;
|
||||
|
||||
rc = fm->avcodec_send_packet(ctx, dec->pkt);
|
||||
if (rc < 0) {
|
||||
log_err("decoder: avcodec_send_packet failed: %d", rc);
|
||||
status = DAEDALUS_DECODE_ERR_SEND;
|
||||
resp->status = DAEDALUS_DECODE_ERR_SEND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
free(assembled);
|
||||
(void) assembled_len;
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pull the next display-ordered frame out of libavcodec's DPB.
|
||||
* Returns 0 if a frame was returned (dec->frame holds it and resp
|
||||
* is populated with metadata + output_src_pts == frame->pts),
|
||||
* -EAGAIN if libavcodec needs more input, or DAEDALUS_DECODE_ERR_*
|
||||
* on a hard codec error. Caller may immediately invoke
|
||||
* daedalus_decoder_pack_current() to copy this frame's pixels into
|
||||
* a CAPTURE buffer's mapped planes, then call drain_one again for
|
||||
* any further frames in the DPB.
|
||||
*/
|
||||
int daedalus_decoder_drain_one(struct daedalus_decoder *dec,
|
||||
uint32_t codec_id,
|
||||
struct daedalus_resp_frame *resp)
|
||||
{
|
||||
struct ffmpeg_loader *fm = dec->loader;
|
||||
struct AVCodecContext *ctx = NULL;
|
||||
struct AVFrame *fr;
|
||||
const AVPixFmtDescriptor *desc;
|
||||
uint32_t h, luma_len = 0, chroma_len = 0;
|
||||
int rc;
|
||||
|
||||
memset(resp, 0, sizeof(*resp));
|
||||
resp->codec_id = codec_id;
|
||||
|
||||
rc = decoder_open_codec(dec, codec_id, &ctx);
|
||||
if (rc == -ENOSYS) {
|
||||
resp->status = DAEDALUS_DECODE_ERR_CODEC;
|
||||
return DAEDALUS_DECODE_ERR_CODEC;
|
||||
}
|
||||
if (rc < 0) {
|
||||
resp->status = DAEDALUS_DECODE_ERR_OPEN;
|
||||
return DAEDALUS_DECODE_ERR_OPEN;
|
||||
}
|
||||
|
||||
fm->av_frame_unref(dec->frame);
|
||||
rc = fm->avcodec_receive_frame(ctx, dec->frame);
|
||||
if (rc == AVERROR(EAGAIN) || rc == AVERROR_EOF)
|
||||
return -EAGAIN;
|
||||
if (rc == AVERROR(EAGAIN) || rc == AVERROR_EOF) {
|
||||
log_debug("decoder: no frame ready yet (rc=%d)", rc);
|
||||
resp->status = DAEDALUS_DECODE_NO_FRAME;
|
||||
goto out;
|
||||
}
|
||||
if (rc < 0) {
|
||||
log_err("decoder: avcodec_receive_frame failed: %d", rc);
|
||||
resp->status = DAEDALUS_DECODE_ERR_RECV;
|
||||
return DAEDALUS_DECODE_ERR_RECV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fr = dec->frame;
|
||||
desc = fm->av_pix_fmt_desc_get(fr->format);
|
||||
h = fnv1a32_init();
|
||||
{
|
||||
struct AVFrame *fr = dec->frame;
|
||||
const AVPixFmtDescriptor *desc =
|
||||
fm->av_pix_fmt_desc_get(fr->format);
|
||||
uint32_t h = fnv1a32_init();
|
||||
uint32_t luma_len = 0, chroma_len = 0;
|
||||
|
||||
resp->status = DAEDALUS_DECODE_OK;
|
||||
resp->width = (uint32_t) fr->width;
|
||||
resp->height = (uint32_t) fr->height;
|
||||
resp->pix_fmt = fr->format;
|
||||
resp->output_src_pts = (uint64_t) fr->pts;
|
||||
resp->status = DAEDALUS_DECODE_OK;
|
||||
resp->width = (uint32_t) fr->width;
|
||||
resp->height = (uint32_t) fr->height;
|
||||
resp->pix_fmt = fr->format;
|
||||
|
||||
if (!desc) {
|
||||
log_warn("decoder: no descriptor for pix_fmt %d", fr->format);
|
||||
} else {
|
||||
int p, max_plane = 0;
|
||||
int i;
|
||||
/*
|
||||
* Walk every plane reported by the AVPixFmtDescriptor.
|
||||
* For each component, byte width = ((plane_w *
|
||||
* step_minus1) >> 0) — but the descriptor only tells
|
||||
* us which plane each component sits in, not the
|
||||
* plane's byte stride per pixel. In practice for the
|
||||
* formats we care about (YUV420P, YUV422P, YUV444P,
|
||||
* GBRP, NV12), each plane has exactly one component
|
||||
* at 1 byte/sample. Hash each plane at
|
||||
* (width >> log2_chroma_w) × (height >> log2_chroma_h)
|
||||
* for chroma planes, full-size for plane 0.
|
||||
*
|
||||
* This generalises cleanly to anything 8-bit-per-
|
||||
* sample-per-plane; 10/12-bit (P010, YUV420P10LE) will
|
||||
* need depth handling when Phase 8.6 brings HDR
|
||||
* content into play.
|
||||
*/
|
||||
if (!desc) {
|
||||
log_warn("decoder: no descriptor for pix_fmt %d",
|
||||
fr->format);
|
||||
} else {
|
||||
int p, max_plane = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < desc->nb_components; i++) {
|
||||
if (desc->comp[i].plane > max_plane)
|
||||
max_plane = desc->comp[i].plane;
|
||||
}
|
||||
|
||||
for (p = 0; p <= max_plane; p++) {
|
||||
int pw, ph;
|
||||
if (!fr->data[p] || !fr->linesize[p])
|
||||
continue;
|
||||
if (p == 0) {
|
||||
pw = fr->width;
|
||||
ph = fr->height;
|
||||
luma_len += (uint32_t) pw * (uint32_t) ph;
|
||||
} else {
|
||||
pw = AV_CEIL_RSHIFT(fr->width,
|
||||
desc->log2_chroma_w);
|
||||
ph = AV_CEIL_RSHIFT(fr->height,
|
||||
desc->log2_chroma_h);
|
||||
chroma_len += (uint32_t) pw * (uint32_t) ph;
|
||||
for (i = 0; i < desc->nb_components; i++) {
|
||||
if (desc->comp[i].plane > max_plane)
|
||||
max_plane = desc->comp[i].plane;
|
||||
}
|
||||
|
||||
for (p = 0; p <= max_plane; p++) {
|
||||
int pw, ph;
|
||||
if (!fr->data[p] || !fr->linesize[p])
|
||||
continue;
|
||||
if (p == 0) {
|
||||
pw = fr->width;
|
||||
ph = fr->height;
|
||||
luma_len += (uint32_t) pw *
|
||||
(uint32_t) ph;
|
||||
} else {
|
||||
pw = AV_CEIL_RSHIFT(fr->width,
|
||||
desc->log2_chroma_w);
|
||||
ph = AV_CEIL_RSHIFT(fr->height,
|
||||
desc->log2_chroma_h);
|
||||
chroma_len += (uint32_t) pw *
|
||||
(uint32_t) ph;
|
||||
}
|
||||
h = fnv1a32_plane(h, fr->data[p], pw, ph,
|
||||
fr->linesize[p]);
|
||||
}
|
||||
h = fnv1a32_plane(h, fr->data[p], pw, ph,
|
||||
fr->linesize[p]);
|
||||
}
|
||||
|
||||
resp->luma_len = luma_len;
|
||||
resp->chroma_len = chroma_len;
|
||||
resp->fnv1a_yuv = h;
|
||||
|
||||
/*
|
||||
* Pack pixels directly into the mapped CAPTURE dmabuf
|
||||
* planes. Dispatch on the V4L2 fourcc the kernel
|
||||
* negotiated:
|
||||
* V4L2_PIX_FMT_NV12M (default, 8-bit, 2 planes)
|
||||
* V4L2_PIX_FMT_P010 (10-bit HDR, 1 plane)
|
||||
*/
|
||||
if (planes && planes->nr >= 1) {
|
||||
int prc = 0;
|
||||
switch (req->capture_pix_fmt) {
|
||||
case V4L2_PIX_FMT_NV12M:
|
||||
prc = pack_nv12_to_planes(fr, desc, planes);
|
||||
break;
|
||||
case V4L2_PIX_FMT_NV12:
|
||||
prc = pack_nv12_single_to_plane(fr, desc, planes);
|
||||
break;
|
||||
case V4L2_PIX_FMT_P010:
|
||||
prc = pack_p010_to_plane(fr, desc, planes);
|
||||
break;
|
||||
default:
|
||||
log_warn("decoder: unsupported capture fourcc 0x%08x",
|
||||
req->capture_pix_fmt);
|
||||
prc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (prc < 0)
|
||||
log_warn("decoder: pack failed (pix_fmt=%d cap_fourcc=0x%08x) — kernel will see metadata only",
|
||||
fr->format, req->capture_pix_fmt);
|
||||
}
|
||||
|
||||
log_info("decoder: OK %dx%d fmt=%d (%s) fnv1a=0x%08x luma=%u chroma=%u",
|
||||
fr->width, fr->height, fr->format,
|
||||
desc ? desc->name : "?",
|
||||
h, luma_len, chroma_len);
|
||||
}
|
||||
|
||||
resp->luma_len = luma_len;
|
||||
resp->chroma_len = chroma_len;
|
||||
resp->fnv1a_yuv = h;
|
||||
|
||||
log_info("decoder: OK %dx%d fmt=%d (%s) fnv1a=0x%08x luma=%u chroma=%u src_pts=%llu",
|
||||
fr->width, fr->height, fr->format,
|
||||
desc ? desc->name : "?",
|
||||
h, luma_len, chroma_len,
|
||||
(unsigned long long) fr->pts);
|
||||
fm->av_frame_unref(dec->frame);
|
||||
|
||||
out:
|
||||
free(assembled);
|
||||
(void) assembled_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int daedalus_decoder_pack_current(struct daedalus_decoder *dec,
|
||||
const struct daedalus_capture_planes *planes,
|
||||
uint32_t capture_pix_fmt)
|
||||
{
|
||||
struct ffmpeg_loader *fm = dec->loader;
|
||||
struct AVFrame *fr = dec->frame;
|
||||
const AVPixFmtDescriptor *desc;
|
||||
int prc;
|
||||
|
||||
if (!planes || planes->nr < 1 || !fr || !fr->width || !fr->height)
|
||||
return -EINVAL;
|
||||
|
||||
desc = fm->av_pix_fmt_desc_get(fr->format);
|
||||
|
||||
switch (capture_pix_fmt) {
|
||||
case V4L2_PIX_FMT_NV12M:
|
||||
prc = pack_nv12_to_planes(fr, desc, planes);
|
||||
break;
|
||||
case V4L2_PIX_FMT_NV12:
|
||||
prc = pack_nv12_single_to_plane(fr, desc, planes);
|
||||
break;
|
||||
case V4L2_PIX_FMT_P010:
|
||||
prc = pack_p010_to_plane(fr, desc, planes);
|
||||
break;
|
||||
default:
|
||||
log_warn("decoder: unsupported capture fourcc 0x%08x",
|
||||
capture_pix_fmt);
|
||||
prc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (prc < 0)
|
||||
log_warn("decoder: pack failed (pix_fmt=%d cap_fourcc=0x%08x)",
|
||||
fr->format, capture_pix_fmt);
|
||||
return prc;
|
||||
}
|
||||
|
||||
+22
-57
@@ -56,68 +56,33 @@ int daedalus_decoder_init(struct daedalus_decoder *dec,
|
||||
void daedalus_decoder_cleanup(struct daedalus_decoder *dec);
|
||||
|
||||
/**
|
||||
* daedalus_decoder_submit - send one REQ_DECODE's bitstream into libavcodec
|
||||
* daedalus_decoder_run_request - decode one REQ_DECODE payload
|
||||
* @dec: initialised decoder
|
||||
* @req: REQ_DECODE prefix (from the wire); src_pts is stamped on
|
||||
* the AVPacket so libavcodec returns frame->pts == src_pts
|
||||
* when it eventually outputs the matching frame in display
|
||||
* order (daedalus-v4l2#6).
|
||||
* @req: REQ_DECODE prefix (from the wire)
|
||||
* @bitstream: bitstream blob (req->bitstream_len bytes)
|
||||
* @h264_meta: optional H.264 SPS/PPS metadata; non-NULL only when
|
||||
* codec_id == H264 and the kernel set DAEDALUS_REQ_FLAG_
|
||||
* H264_META. See decoder.c for the AnnexB synthesis.
|
||||
* H264_META. Used to synthesise the AnnexB SPS+PPS NALs
|
||||
* libavcodec needs before any slice (libva-v4l2-request
|
||||
* passes only the slice in @bitstream per the V4L2
|
||||
* stateless API contract). NULL for VP9/AV1 paths.
|
||||
* @resp: caller-allocated RESP_FRAME output (zeroed by callee)
|
||||
* @planes: mapped CAPTURE planes (Phase 8.6 dmabuf path). If
|
||||
* NULL or planes->nr == 0, the decoder runs but
|
||||
* writes no pixels — caller still gets dims + digest.
|
||||
*
|
||||
* Calls avcodec_send_packet on the codec's per-codec AVCodecContext.
|
||||
* Returns 0 on success; one of DAEDALUS_DECODE_ERR_* on failure
|
||||
* (which the caller should propagate as the RESP_FRAME status for
|
||||
* the cookie of this REQ). Does NOT call avcodec_receive_frame —
|
||||
* use daedalus_decoder_drain_one for that.
|
||||
* Populates @resp with the decode outcome and writes decoded
|
||||
* pixels (NV12 layout: Y to plane 0, interleaved CbCr to plane
|
||||
* 1) directly into the mapped dmabuf planes. Always returns
|
||||
* 0; decode-level failures are reported via @resp->status so
|
||||
* the kernel sees a structured response rather than a dropped
|
||||
* request.
|
||||
*/
|
||||
int daedalus_decoder_submit(struct daedalus_decoder *dec,
|
||||
const struct daedalus_req_decode *req,
|
||||
const uint8_t *bitstream,
|
||||
const struct daedalus_h264_meta *h264_meta);
|
||||
|
||||
/**
|
||||
* daedalus_decoder_drain_one - pop the next display-ordered frame, if any
|
||||
* @dec: initialised decoder
|
||||
* @codec_id: which codec context to drain (matches the REQ that just
|
||||
* called submit). VP9/AV1/H264 use independent contexts.
|
||||
* @resp: caller-allocated RESP_FRAME output (zeroed by callee).
|
||||
* On a successful drain (return 0), resp's status / width /
|
||||
* height / pix_fmt / luma_len / chroma_len / fnv1a_yuv /
|
||||
* output_src_pts are populated; flags is left at 0 (caller
|
||||
* adds HAS_PIXELS / SRC_CONSUMED). On EAGAIN, resp is
|
||||
* zeroed.
|
||||
*
|
||||
* Return: 0 on a frame returned, -EAGAIN if libavcodec needs more
|
||||
* input (display-order frame held inside DPB), <0 on a hard codec
|
||||
* error (resp->status set).
|
||||
*
|
||||
* After a successful drain, the dec's internal AVFrame holds the
|
||||
* decoded picture. Caller may immediately call
|
||||
* daedalus_decoder_pack_current(planes) to write that picture into
|
||||
* a CAPTURE buffer's dmabuf-mapped planes. Subsequent calls to
|
||||
* drain_one (without another submit) try to pull additional frames
|
||||
* from libavcodec's DPB.
|
||||
*/
|
||||
int daedalus_decoder_drain_one(struct daedalus_decoder *dec,
|
||||
uint32_t codec_id,
|
||||
struct daedalus_resp_frame *resp);
|
||||
|
||||
/**
|
||||
* daedalus_decoder_pack_current - pack the last drained frame into planes
|
||||
* @dec: initialised decoder; must have a frame from drain_one
|
||||
* @planes: mapped CAPTURE planes (open via GET_DMABUF using the
|
||||
* cookie that owns the frame's output_src_pts).
|
||||
* @capture_pix_fmt: V4L2 fourcc on the CAPTURE side (NV12M, NV12,
|
||||
* P010).
|
||||
*
|
||||
* Return: 0 on success, <0 on a pack failure (kernel sees only the
|
||||
* metadata, not pixels — typical when a format isn't wired yet).
|
||||
*/
|
||||
int daedalus_decoder_pack_current(struct daedalus_decoder *dec,
|
||||
const struct daedalus_capture_planes *planes,
|
||||
uint32_t capture_pix_fmt);
|
||||
int daedalus_decoder_run_request(struct daedalus_decoder *dec,
|
||||
const struct daedalus_req_decode *req,
|
||||
const uint8_t *bitstream,
|
||||
const struct daedalus_h264_meta *h264_meta,
|
||||
struct daedalus_resp_frame *resp,
|
||||
const struct daedalus_capture_planes *planes);
|
||||
|
||||
#endif /* DAEDALUS_V4L2_DECODER_H */
|
||||
|
||||
Reference in New Issue
Block a user