Skip to content

Commit 973961c

Browse files
feat: harden codec lifecycle probes
1 parent fb62095 commit 973961c

5 files changed

Lines changed: 151 additions & 41 deletions

File tree

src/android/fake-media-acceld.c

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <sys/mman.h>
2020
#include <sys/socket.h>
2121
#include <sys/stat.h>
22+
#include <time.h>
2223
#include <unistd.h>
2324

2425
typedef bool (*binder_set_max_threads_fn)(uint32_t);
@@ -33,13 +34,26 @@ struct decoder_session {
3334
int pool_fd;
3435
uint8_t *pool_map;
3536
size_t pool_bytes;
36-
bool slots[16];
37+
bool slots[FMA_MAX_SLOTS];
3738
uint32_t next_slot;
3839
bool started;
40+
uint32_t codec_id;
41+
uint64_t packets;
42+
uint64_t input_bytes;
43+
uint64_t frames;
44+
uint64_t frame_bytes;
45+
uint64_t acquire_ns;
46+
uint64_t copy_ns;
3947
};
4048

4149
static volatile sig_atomic_t running = 1;
4250

51+
static uint64_t monotonic_ns(void) {
52+
struct timespec time;
53+
return clock_gettime(CLOCK_MONOTONIC, &time) == 0 ?
54+
(uint64_t)time.tv_sec * UINT64_C(1000000000) + (uint64_t)time.tv_nsec : 0;
55+
}
56+
4357
static void on_signal(int signal_number) {
4458
(void)signal_number;
4559
running = 0;
@@ -86,6 +100,19 @@ static int send_error(int fd, const struct fma_message *request, const char *tex
86100
}
87101

88102
static void destroy_decoder(struct decoder_session *session) {
103+
if (session->packets) {
104+
printf("metrics codec=%s packets=%llu input_bytes=%llu frames=%llu "
105+
"frame_bytes=%llu acquire_ms=%.3f copy_ms=%.3f "
106+
"socket_frame_bytes=0\n",
107+
fma_codec_name(session->codec_id),
108+
(unsigned long long)session->packets,
109+
(unsigned long long)session->input_bytes,
110+
(unsigned long long)session->frames,
111+
(unsigned long long)session->frame_bytes,
112+
(double)session->acquire_ns / 1000000.0,
113+
(double)session->copy_ns / 1000000.0);
114+
fflush(stdout);
115+
}
89116
if (session->codec && session->started)
90117
AMediaCodec_stop(session->codec);
91118
if (session->codec)
@@ -122,6 +149,7 @@ static int create_decoder(struct decoder_session *session,
122149
const char *mime = fma_codec_mime(config->codec);
123150
if (!mime)
124151
return -1;
152+
session->codec_id = config->codec;
125153

126154
uint32_t stride = (config->width + 63u) & ~63u;
127155
session->pool = (struct fma_frame_pool) {
@@ -317,16 +345,25 @@ static int emit_output(int fd, const struct fma_message *request,
317345
AImage *image = NULL;
318346
uint32_t slot = UINT32_MAX;
319347
struct fma_frame frame;
320-
if (find_free_slot(session, &slot) < 0 ||
321-
acquire_image(session->reader, &image) < 0 ||
322-
copy_image_to_nv12(image, session, slot, &frame) < 0) {
348+
uint64_t started_ns = monotonic_ns();
349+
int slot_status = find_free_slot(session, &slot);
350+
int acquire_status = slot_status == 0 ?
351+
acquire_image(session->reader, &image) : -1;
352+
session->acquire_ns += monotonic_ns() - started_ns;
353+
started_ns = monotonic_ns();
354+
int copy_status = acquire_status == 0 ?
355+
copy_image_to_nv12(image, session, slot, &frame) : -1;
356+
session->copy_ns += monotonic_ns() - started_ns;
357+
if (slot_status < 0 || acquire_status < 0 || copy_status < 0) {
323358
if (image)
324359
AImage_delete(image);
325360
if (slot < session->pool.slot_count)
326361
session->slots[slot] = false;
327362
return -1;
328363
}
329364
AImage_delete(image);
365+
session->frames++;
366+
session->frame_bytes += frame.bytes_used;
330367
uint8_t payload[24];
331368
fma_encode_frame(&frame, payload);
332369
struct fma_message frame_request = *request;
@@ -438,6 +475,8 @@ static int serve_client(int fd) {
438475
case FMA_MSG_QUEUE_PACKET: {
439476
uint32_t flags = (request.flags & FMA_PACKET_CODEC_CONFIG) ?
440477
AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG : 0;
478+
session.packets++;
479+
session.input_bytes += request.payload_size;
441480
if (!session.started || queue_input(&session, &request, flags) < 0 ||
442481
emit_output(fd, &request, &session, 0, false) < 0)
443482
result = send_error(fd, &request, "MediaCodec packet failed");

src/client/client.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,14 @@ int fma_client_drain(struct fma_client *client) {
132132
}
133133

134134
int fma_client_flush(struct fma_client *client) {
135-
return send_simple(client, FMA_MSG_FLUSH, NULL, 0, 0, 0, NULL);
135+
uint64_t request_id;
136+
if (send_simple(client, FMA_MSG_FLUSH, NULL, 0, 0, 0, &request_id) < 0)
137+
return -1;
138+
struct fma_message reply;
139+
if (expect(client, FMA_MSG_FLUSHED, request_id, &reply) < 0)
140+
return -1;
141+
fma_message_release(&reply);
142+
return 0;
136143
}
137144

138145
int fma_client_release_frame(struct fma_client *client, uint32_t slot) {

tests/fake-roundtrip.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ while [ ! -S "$socket" ]; do
1818
sleep 0.01
1919
done
2020

21-
"$decoder" "$socket" "$input" 64 64 30 "$output"
21+
result=$("$decoder" --repeat 3 "$socket" "$input" 64 64 30 "$output")
22+
printf '%s\n' "$result"
23+
case "$result" in
24+
*"repeats=3 packets=3 frames=3 "*) ;;
25+
*) exit 1 ;;
26+
esac
2227
wait "$daemon_pid"
2328
trap - EXIT INT TERM
2429
test -s "$output"

tests/protocol-test.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,20 @@ static void test_rejects_bad_wire(void) {
4646
CHECK(errno == EPROTO);
4747
}
4848

49+
static void test_rejects_unbounded_decoder(void) {
50+
struct fma_decoder_config config = {
51+
.codec = FMA_CODEC_H264,
52+
.width = FMA_MAX_DIMENSION + 1,
53+
.height = 1080,
54+
.slot_count = FMA_DEFAULT_SLOTS,
55+
};
56+
uint8_t wire[16];
57+
fma_encode_decoder_config(&config, wire);
58+
struct fma_decoder_config decoded;
59+
CHECK(fma_decode_decoder_config(wire, sizeof(wire), &decoded) == -1);
60+
CHECK(errno == EINVAL);
61+
}
62+
4963
static void test_payload_and_fd_roundtrip(void) {
5064
int sockets[2];
5165
CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == 0);
@@ -74,6 +88,7 @@ static void test_payload_and_fd_roundtrip(void) {
7488
int main(void) {
7589
test_header_roundtrip();
7690
test_rejects_bad_wire();
91+
test_rejects_unbounded_decoder();
7792
test_payload_and_fd_roundtrip();
7893
puts("protocol tests passed");
7994
return 0;

tools/fma-decode.c

Lines changed: 79 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ static int read_file(const char *path, uint8_t **data, size_t *size) {
5454
}
5555

5656
static size_t find_access_units(const uint8_t *data, size_t size,
57+
uint32_t codec,
5758
struct access_unit **units_out) {
5859
size_t capacity = 64;
5960
size_t count = 0;
@@ -69,7 +70,12 @@ static size_t find_access_units(const uint8_t *data, size_t size,
6970
else if (i + 5 < size && data[i] == 0 && data[i + 1] == 0 &&
7071
data[i + 2] == 0 && data[i + 3] == 1)
7172
start_code = 4;
72-
if (!start_code || (data[i + start_code] & 0x1f) != 9)
73+
if (!start_code)
74+
continue;
75+
uint32_t nal_type = codec == FMA_CODEC_HEVC ?
76+
(data[i + start_code] >> 1) & 0x3f : data[i + start_code] & 0x1f;
77+
uint32_t aud_type = codec == FMA_CODEC_HEVC ? 35u : 9u;
78+
if (nal_type != aud_type)
7379
continue;
7480
if (found_aud) {
7581
if (count == capacity) {
@@ -98,6 +104,14 @@ static size_t find_access_units(const uint8_t *data, size_t size,
98104
return count;
99105
}
100106

107+
static uint32_t parse_codec(const char *name) {
108+
if (strcmp(name, "h264") == 0 || strcmp(name, "avc") == 0)
109+
return FMA_CODEC_H264;
110+
if (strcmp(name, "hevc") == 0 || strcmp(name, "h265") == 0)
111+
return FMA_CODEC_HEVC;
112+
return 0;
113+
}
114+
101115
static int process_message(struct fma_client *client,
102116
const struct fma_frame_pool *pool, uint8_t *pool_map,
103117
FILE *output, uint64_t *frames, bool *packet_ack,
@@ -137,40 +151,59 @@ static int process_message(struct fma_client *client,
137151
}
138152

139153
int main(int argc, char **argv) {
140-
if (argc < 6 || argc > 7) {
141-
fprintf(stderr, "usage: %s SOCKET INPUT.h264 WIDTH HEIGHT FPS [OUTPUT.nv12]\n",
142-
argv[0]);
154+
uint32_t codec = FMA_CODEC_H264;
155+
uint32_t repeats = 1;
156+
int argument = 1;
157+
while (argument < argc && strncmp(argv[argument], "--", 2) == 0) {
158+
if (strcmp(argv[argument], "--codec") == 0 && argument + 1 < argc) {
159+
codec = parse_codec(argv[argument + 1]);
160+
argument += 2;
161+
} else if (strcmp(argv[argument], "--repeat") == 0 && argument + 1 < argc) {
162+
repeats = (uint32_t)strtoul(argv[argument + 1], NULL, 10);
163+
argument += 2;
164+
} else {
165+
codec = 0;
166+
break;
167+
}
168+
}
169+
int remaining = argc - argument;
170+
if (!codec || !repeats || repeats > 1000 || remaining < 5 || remaining > 6) {
171+
fprintf(stderr, "usage: %s [--codec h264|hevc] [--repeat N] "
172+
"SOCKET INPUT WIDTH HEIGHT FPS [OUTPUT.nv12]\n", argv[0]);
143173
return 2;
144174
}
145-
uint32_t width = (uint32_t)strtoul(argv[3], NULL, 10);
146-
uint32_t height = (uint32_t)strtoul(argv[4], NULL, 10);
147-
uint32_t fps = (uint32_t)strtoul(argv[5], NULL, 10);
175+
const char *socket_path = argv[argument + 0];
176+
const char *input_path = argv[argument + 1];
177+
uint32_t width = (uint32_t)strtoul(argv[argument + 2], NULL, 10);
178+
uint32_t height = (uint32_t)strtoul(argv[argument + 3], NULL, 10);
179+
uint32_t fps = (uint32_t)strtoul(argv[argument + 4], NULL, 10);
180+
const char *output_path = remaining == 6 ? argv[argument + 5] : NULL;
148181
if (!width || !height || !fps)
149182
return 2;
150183

151184
uint8_t *input = NULL;
152185
size_t input_size = 0;
153-
if (read_file(argv[2], &input, &input_size) < 0) {
186+
if (read_file(input_path, &input, &input_size) < 0) {
154187
perror("input");
155188
return 1;
156189
}
157190
struct access_unit *units = NULL;
158-
size_t unit_count = find_access_units(input, input_size, &units);
191+
size_t unit_count = find_access_units(input, input_size, codec, &units);
159192
if (!unit_count) {
160193
fprintf(stderr, "could not split input\n");
161194
free(input);
162195
return 1;
163196
}
164197

165198
struct fma_client client;
166-
if (fma_client_connect(&client, argv[1]) < 0) {
199+
if (fma_client_connect(&client, socket_path) < 0) {
167200
perror("connect");
168201
free(units);
169202
free(input);
170203
return 1;
171204
}
172205
struct fma_decoder_config config = {
173-
.codec = FMA_CODEC_H264, .width = width, .height = height,
206+
.codec = codec, .width = width, .height = height,
174207
.slot_count = FMA_DEFAULT_SLOTS,
175208
};
176209
struct fma_frame_pool pool;
@@ -192,8 +225,8 @@ int main(int argc, char **argv) {
192225
free(input);
193226
return 1;
194227
}
195-
FILE *output = argc == 7 ? fopen(argv[6], "wb") : NULL;
196-
if (argc == 7 && !output) {
228+
FILE *output = output_path ? fopen(output_path, "wb") : NULL;
229+
if (output_path && !output) {
197230
perror("output");
198231
return 1;
199232
}
@@ -204,35 +237,46 @@ int main(int argc, char **argv) {
204237
uint64_t started_ns = monotonic_ns();
205238
bool eos = false;
206239
bool failed = false;
207-
for (size_t i = 0; i < unit_count; ++i) {
208-
int64_t pts_us = (int64_t)(i * UINT64_C(1000000) / fps);
209-
bool packet_ack = false;
210-
if (fma_client_queue_packet(&client, input + units[i].offset,
211-
units[i].size, pts_us, 0) < 0) {
212-
perror("decode");
213-
failed = true;
214-
break;
240+
for (uint32_t repeat = 0; repeat < repeats && !failed; ++repeat) {
241+
eos = false;
242+
for (size_t i = 0; i < unit_count; ++i) {
243+
int64_t pts_us = (int64_t)(i * UINT64_C(1000000) / fps);
244+
bool packet_ack = false;
245+
if (fma_client_queue_packet(&client, input + units[i].offset,
246+
units[i].size, pts_us, 0) < 0) {
247+
perror("decode");
248+
failed = true;
249+
break;
250+
}
251+
input_bytes += units[i].size;
252+
while (!packet_ack &&
253+
process_message(&client, &pool, pool_map, output, &frames,
254+
&packet_ack, &eos) == 0) {}
255+
if (!packet_ack) {
256+
perror("decode");
257+
failed = true;
258+
break;
259+
}
215260
}
216-
input_bytes += units[i].size;
217-
while (!packet_ack &&
218-
process_message(&client, &pool, pool_map, output, &frames,
219-
&packet_ack, &eos) == 0) {}
220-
if (!packet_ack) {
221-
perror("decode");
261+
if (!failed && fma_client_drain(&client) == 0) {
262+
bool packet_ack = false;
263+
while (!eos && process_message(&client, &pool, pool_map, output,
264+
&frames, &packet_ack, &eos) == 0) {}
265+
}
266+
if (!eos)
267+
failed = true;
268+
if (!failed && repeat + 1 < repeats && fma_client_flush(&client) < 0) {
269+
perror("flush");
222270
failed = true;
223-
break;
224271
}
225272
}
226-
if (!failed && fma_client_drain(&client) == 0) {
227-
bool packet_ack = false;
228-
while (!eos && process_message(&client, &pool, pool_map, output,
229-
&frames, &packet_ack, &eos) == 0) {}
230-
}
231273
uint64_t elapsed_ns = monotonic_ns() - started_ns;
232274
frame_bytes = frames * pool.slot_size;
233275
double seconds = elapsed_ns ? (double)elapsed_ns / 1000000000.0 : 0.0;
234-
printf("packets=%zu frames=%llu elapsed_ms=%.3f decode_fps=%.2f\n",
235-
unit_count, (unsigned long long)frames, seconds * 1000.0,
276+
printf("codec=%s repeats=%u packets=%zu frames=%llu elapsed_ms=%.3f "
277+
"decode_fps=%.2f\n",
278+
fma_codec_name(codec), repeats, unit_count * (size_t)repeats,
279+
(unsigned long long)frames, seconds * 1000.0,
236280
seconds > 0.0 ? (double)frames / seconds : 0.0);
237281
printf("compressed_bytes=%llu frame_bytes=%llu shared_pool_bytes=%zu "
238282
"socket_frame_bytes=0\n",

0 commit comments

Comments
 (0)