From 757586e0f978748594cf3ea597996ee14129096e Mon Sep 17 00:00:00 2001 From: Lynne Date: Wed, 6 Mar 2024 19:14:45 +0100 Subject: [PATCH] More updates, UDP-Lite support --- README.md | 8 +- draft-avtransport-spec.bs | 36 +-- libavtransport/address.c | 4 + libavtransport/connection.c | 37 ++- .../include/avtransport/connection.h | 4 +- libavtransport/io_common.c | 8 +- libavtransport/io_common.h | 9 +- libavtransport/io_socket_common.c | 10 +- libavtransport/io_udp.c | 21 +- libavtransport/output_packet.c | 43 ---- libavtransport/protocol_common.h | 5 +- libavtransport/protocol_noop.c | 10 +- libavtransport/scheduler.c | 5 + libavtransport/scheduler.h | 3 + .../{file_io_template.c => file_io_common.c} | 30 +-- libavtransport/tests/file_io_common.h | 34 +++ libavtransport/tests/io_fd.c | 4 +- libavtransport/tests/io_file.c | 4 +- libavtransport/tests/io_udp.c | 156 +----------- libavtransport/tests/io_udp_lite.c | 42 ++++ libavtransport/tests/meson.build | 14 +- libavtransport/tests/net_common.c | 223 ++++++++++++++++++ libavtransport/tests/net_common.h | 44 ++++ libavtransport/tests/udp_common.c | 26 ++ meson.build | 5 +- tools/README.md | 8 + tools/avcat.c | 14 +- tools/avtdump.c | 31 +++ tools/meson.build | 14 ++ 29 files changed, 585 insertions(+), 267 deletions(-) rename libavtransport/tests/{file_io_template.c => file_io_common.c} (84%) create mode 100644 libavtransport/tests/file_io_common.h create mode 100644 libavtransport/tests/io_udp_lite.c create mode 100644 libavtransport/tests/net_common.c create mode 100644 libavtransport/tests/net_common.h create mode 100644 libavtransport/tests/udp_common.c create mode 100644 tools/avtdump.c diff --git a/README.md b/README.md index 918c1b2..d3abdb6 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,6 @@ You can read the current rendered draft by opening the [autogenerated webpage](h - Timestamp clock, allowing for jitter compensation - Unified, cycle-accurate clock for exact synchronization between multiple streams. -### File extension -The `.avt` file extensions should be used. - ## Tools The project provides tools to enable the creation, conversion, and provide information @@ -41,6 +38,11 @@ Information on its usage is provided in the README of the tool. avcat is automatically built when [libavtransport](#libavtransport) is enabled. +### avtdump + +**avtdump** is a utility to inspect AVTransport streams and files, logging any issues, +stream changes, packet sizes and so on. + ## libavtransport The project provides a reference implementation, **libavtransport** with full support for the standard. diff --git a/draft-avtransport-spec.bs b/draft-avtransport-spec.bs index 6c2e1d7..1dece95 100644 --- a/draft-avtransport-spec.bs +++ b/draft-avtransport-spec.bs @@ -263,9 +263,9 @@ Below is a table of how they're allocated: -Note: range ''0x8000'' to ''0x80FF'' is reserved for [[#reverse-signalling]]. +Note: Range ''0x8000'' to ''0x80FF'' is reserved for [[#reverse-signalling]]. -Note: the 16-bit descriptor may be split into two sections, a constant upper 8-bits +Note: The 16-bit descriptor may be split into two sections, a constant upper 8-bits and variable bottom 8-bits, hence some descriptors are considered a range. Anything not specified in the table is reserved and must not be used. Future additions @@ -281,7 +281,7 @@ A special notation is used to describe sequences of bits: - u(N) : specifies the data that follows is an unsigned integer of ''N'' bits. - i(N) : the same, but the data describes a signed integer is signed. - b(N) : the data is an opaque sequence of ''N'' bits that clients must not interpret. - - R(N) : the data is a rational number, with a numerator of i(N/2) and following that, a denominator of i(N/2). The denominator must be greater than ''0''. + - R(N) : the data is a rational number, with a numerator (''num'') of i(N/2) and following that, a denominator (''den'') of i(N/2). The denominator must be greater than ''0''. All packets are at least 36 bytes long (288 bits), and always have an 8 byte (64 bits) [[#ldpc]] code after the first 28 bytes to verify and correct their data. @@ -333,7 +333,7 @@ Note: [=global_seq=] should start at ''0x0'', but receivers MUST be prepared for stream_id is a 16-bit unsigned integer, uniquely identifying each stream contained within an [TITLE] stream. -Note: senders should never reuse [=stream_id=] indices, but receivers should be prepared +Note: Senders should never reuse [=stream_id=] indices, but receivers should be prepared to deal with this scenario. @@ -351,7 +351,7 @@ or duration v and a rational timebase b is the t = v * b.num / b.den -Note: all mathematical operations on timestamps should be done in the integer domain, +Note: All mathematical operations on timestamps should be done in the integer domain, as floating-point operations may be inaccurate and inconsistent. The timebase of [[#stream-data-packets]], [[#stream-duration-packets]], @@ -424,7 +424,7 @@ On every received packet: This is a very simple example which depends on the local receiver oscillator simply being *more precise* than the transmitter's oscillator. -Note: the way receivers handle jitter is intentionally left undefined. As a recommendation, +Note: The way receivers handle jitter is intentionally left undefined. As a recommendation, implementations should resampple the audio and adjust video frame duration such that synchronization is maintained. Otherwise, implementations can drop or duplicate frames. @@ -501,7 +501,7 @@ Users are also free to measure the latency in a similar way, by measuring their Finally, if explicitly requested, implementations are allowed to delay presentation by interpreting a negative latency value of ''Δ'' as a delay. -Note: packets which had a negative timestamp before must still be dropped to permit for correct decoding +Note: Packets which had a negative timestamp before must still be dropped to permit for correct decoding and presentation. Note: The ''stream latency'' and ''synchronization'' actions depend on all devices having accurately synchronized @@ -538,6 +538,8 @@ Note: Alternatively, for UDP-only, the udp://<address>:<port>< at the risk of conflict with other protocols (''MPEG-TS'' or ''Matroska''). Note: When handling AVTransport files, it is recommended to use the .avt file extension. +Alternatively, for image-only files, .ati is recommended, and for subtitles, +the recommendation is .avs. # Packet structure # {#packet-structure} @@ -646,6 +648,10 @@ as an AVTransport session. The syntax is as follows: Multiple session packets may be present in a session, but must remain bytewise-identical. +The [=producer_name=] field is a fixed-length string, meaning that +the field is always 16 bytes. If the name is less than 16 bytes, it must +be zero-padded. + ## Stream Session Flags Enumeration (enum SessionFlags) ## {#enum-SessionFlags} The [=session_flags=] field must be interpreted in the following way: @@ -731,7 +737,7 @@ The structure of the data in a [[#time-synchronization-packets]] packet is as fo The ts_clock_freq field is equal to [=ts_clock_freq=] = [=ts_clock_hz=] + [=ts_clock_hz2=]/65536. Implementations should prefer to use integer math, and instead have the [=ts_clock_freq=] in increments of 1/65536 Hz. -Note: if [=ts_clock_freq=] is 0, then the sender should be assumed to be relying entirely on its real-time clock. +Note: If [=ts_clock_freq=] is 0, then the sender should be assumed to be relying entirely on its real-time clock. The field defines a strictly monotonic clock signal with a rate of [=ts_clock_freq=], which atomically increments a counter, [=ts_clock_seq=] on the rising edge of the waveform. @@ -2165,7 +2171,7 @@ The following structure MUST be followed: The stream ID for which to make the font available. - Note: may be set to ''0xffff'' to make the font available for all streams. + Note: May be set to ''0xffff'' to make the font available for all streams. @@ -2520,7 +2526,7 @@ The [=colorspace=] field must be interpreted in the following way: : CSP_XYB = 0x6 :: Video contains XYB color data, as defined by [[ISO18181]]. - Note: [matrix] must be equal to ''0xFF'' and the [=custom_matrix=] must be a valid matrix to transform XYB into RGB. + Note: [=matrix=] must be equal to ''0xFF'' and the [=custom_matrix=] must be a valid matrix to transform XYB into RGB. : CSP_ICTCP = 0x5 :: Video contains [[BT2100]] ICtCp color data. @@ -2543,7 +2549,7 @@ The [=colorspace=] field must be interpreted in the following way: : COLOR_RANGE_LIMITED = 0x1 :: Video sample values contains the limited range of the [=bit_depth=]. - Note: this describes the cannonical limited range representation: + Note: This describes the cannonical limited range representation: (219 * E + 16) * 2([=bit_depth=]-8), where E, the input range, is ''0.0'' to ''1.0'' for luma planes and ''-0.5'' to ''0.5'' for chroma planes. This means, for 8-bits, the luma @@ -2572,7 +2578,7 @@ to interpret the value of [=field_id=] in stream data packet headers. : ILACE_PROG = 0x0 :: Video contains progressive data, or interlacing does not apply. - Note: in this mode, the [=field_id=] bit is free to use by users. Implementations must ignore it, and preserve it. + Note: In this mode, the [=field_id=] bit is free to use by users. Implementations must ignore it, and preserve it. : ILACE_TFF = 0x1 :: Video is interlaced. One [[#stream-data-packets]] packet per field. If the data packet's [=field_id=] bit is unset, indicates the field contained is the top field, otherwise it's the bottom field. @@ -2683,7 +2689,7 @@ These values are copied verbatim from [[H.273]]. :: SMPTE EG 432-2 (2010) -Note: this list may not be up to date in this version of the AVTransport specifications. +Note: This list may not be up to date in this version of the AVTransport specifications. Users should consult [[H.273]] for up-to-date values and how to interpret them. @@ -4617,7 +4623,7 @@ The matrices are also listed as standard .alist files, directly usable for analy #### ldpc_h_matrix_288_224 #### {#ldpc_h_matrix_288_224} -Note: spaceholder, working and optimal tables to be done +Note: Spaceholder, working and optimal tables to be done alist code: @@ -4707,7 +4713,7 @@ const uint64_t ldpc_h_matrix_288_224[288 /* (64 rows/64) * 288 cols */] = { #### ldpc_h_matrix_2784_2016 #### {#ldpc_h_matrix_2784_2016} -Note: spaceholder, working and optimal tables to be done +Note: Spaceholder, working and optimal tables to be done alist code: diff --git a/libavtransport/address.c b/libavtransport/address.c index dc24c80..dbfa704 100644 --- a/libavtransport/address.c +++ b/libavtransport/address.c @@ -112,6 +112,10 @@ int avt_addr_from_url(void *log_ctx, AVTAddress *addr, /* Unofficial and conflicting, but let's accept it */ addr->proto = AVT_PROTOCOL_UDP; next += strlen("udp://"); + } else if (!strncmp(next, "udplite://", strlen("udplite://"))) { + /* Unofficial, but let's accept it */ + addr->proto = AVT_PROTOCOL_UDP_LITE; + next += strlen("udplite://"); } else if (!strncmp(next, "quic://", strlen("quic://"))) { /* Unofficial, but let's accept it */ addr->proto = AVT_PROTOCOL_QUIC; diff --git a/libavtransport/connection.c b/libavtransport/connection.c index d2ff44d..6417b98 100644 --- a/libavtransport/connection.c +++ b/libavtransport/connection.c @@ -96,25 +96,54 @@ int avt_connection_send(AVTConnection *conn, { int err; - err = avt_pkt_fifo_push(&conn->out_fifo_pre, pkt, pl); - if (err < 0) - return err; +// err = avt_pkt_fifo_push(&conn->out_fifo_pre, pkt, pl); +// if (err < 0) +// return err; err = avt_scheduler_push(&conn->out_scheduler, pkt, pl); if (err < 0) return err; +// AVTPacketFifo *seq; +// err = avt_scheduler_pop(&conn->out_scheduler, &seq); +// if (err < 0) +// return err; + + return 0; +} + +int avt_connection_process(AVTConnection *conn, int64_t timeout) +{ + int err; + AVTPacketFifo *seq; err = avt_scheduler_pop(&conn->out_scheduler, &seq); if (err < 0) return err; + err = conn->p->send_seq(conn->ctx, conn->p_ctx, seq, timeout); + if (err < 0) + avt_scheduler_done(&conn->out_scheduler, seq); + return 0; } int avt_connection_flush(AVTConnection *conn, int64_t timeout) { - return 0; + int err; + + AVTPacketFifo *seq; + err = avt_scheduler_flush(&conn->out_scheduler, &seq); + if (err < 0) + return err; + + if (seq) { + err = conn->p->send_seq(conn->ctx, conn->p_ctx, seq, timeout); + if (err < 0) + avt_scheduler_done(&conn->out_scheduler, seq); + } + + return conn->p->flush(conn->ctx, conn->p_ctx, timeout); } int avt_connection_destroy(AVTConnection **_conn) diff --git a/libavtransport/include/avtransport/connection.h b/libavtransport/include/avtransport/connection.h index a7497f8..672bee5 100644 --- a/libavtransport/include/avtransport/connection.h +++ b/libavtransport/include/avtransport/connection.h @@ -126,12 +126,12 @@ typedef struct AVTConnectionInfo { * NOTE: dup()-licated on success, users can close() this freely */ int fd; - /* AVT_CONNECTION_NET: opened and bound network socket */ + /* AVT_CONNECTION_NET: opened and bound/connected network socket */ struct { /* Bound socket * NOTE: dup()'d on success, users can close() this freely */ int socket; - /* Sending: destination IP (IPv4: mapped in IPv6) */ + /* Sending: destination IP (IPv4 must be always mapped in IPv6) */ uint8_t dst[16]; /* Connection port */ uint16_t port; diff --git a/libavtransport/io_common.c b/libavtransport/io_common.c index 0062aa0..0773849 100644 --- a/libavtransport/io_common.c +++ b/libavtransport/io_common.c @@ -35,9 +35,8 @@ extern const AVTIO avt_io_fd; extern const AVTIO avt_io_fd_path; #endif -#ifndef _WIN32 extern const AVTIO avt_io_udp; -#endif +extern const AVTIO avt_io_udp_lite; #define MAX_NB_BACKENDS 4 @@ -58,9 +57,10 @@ static const AVTIO *avt_io_list[][MAX_NB_BACKENDS] = { #endif }, [AVT_IO_UDP] = { -#ifndef _WIN32 &avt_io_udp, -#endif + }, + [AVT_IO_UDP_LITE] = { + &avt_io_udp_lite, }, }; diff --git a/libavtransport/io_common.h b/libavtransport/io_common.h index 166d017..af09011 100644 --- a/libavtransport/io_common.h +++ b/libavtransport/io_common.h @@ -31,10 +31,11 @@ #include "packet_common.h" enum AVTIOType { - AVT_IO_FILE, /* Takes a path */ - AVT_IO_FD, /* Takes an integer handle fd */ - AVT_IO_UDP, /* UDP network connection */ - AVT_IO_NULL, /* Takes nothing */ + AVT_IO_FILE, /* Takes a path */ + AVT_IO_FD, /* Takes an integer handle fd */ + AVT_IO_UDP, /* UDP network connection */ + AVT_IO_UDP_LITE, /* UDP-Lite network connection */ + AVT_IO_NULL, /* Takes nothing */ }; /* Low level interface */ diff --git a/libavtransport/io_socket_common.c b/libavtransport/io_socket_common.c index 7446d69..e3d3e79 100644 --- a/libavtransport/io_socket_common.c +++ b/libavtransport/io_socket_common.c @@ -99,7 +99,7 @@ int avt_socket_open(void *log_ctx, AVTSocketCommon *sc, AVTAddress *addr) int ret; int proto = addr->proto == AVT_PROTOCOL_UDP_LITE ? IPPROTO_UDPLITE : IPPROTO_UDP; - if ((sc->socket = socket(AF_INET6, SOCK_DGRAM, proto)) < 0) { + if ((sc->socket = socket(AF_INET6, SOCK_DGRAM, proto)) < 0) { ret = avt_handle_errno(log_ctx, "Failed to open socket: %i %s\n"); sc->socket = -1; return ret; @@ -154,6 +154,14 @@ int avt_socket_open(void *log_ctx, AVTSocketCommon *sc, AVTAddress *addr) /* Enable MTU monitoring */ // SET_SOCKET_OPT(log_ctx, sc, IPPROTO_IPV6, IPV6_RECVPATHMTU, (int)1); +#ifdef IPV6_FREEBIND + /* Enable free binding when no interface is given. + * This lets us not have to deal with dynamic reconfiguration, new + * devices being added, and so on */ + if (addr->listen && !addr->interface) + SET_SOCKET_OPT(log_ctx, sc, IPPROTO_IPV6, IPV6_FREEBIND, (int)1); +#endif + /* Setup binding */ sc->local_addr.sin6_family = AF_INET6; sc->local_addr.sin6_port = addr->port; diff --git a/libavtransport/io_udp.c b/libavtransport/io_udp.c index cd23883..fe4a590 100644 --- a/libavtransport/io_udp.c +++ b/libavtransport/io_udp.c @@ -125,7 +125,7 @@ static int64_t udp_write_vec(AVTContext *ctx, AVTIOCtx *io, AVTPktd *iov, uint32_t nb_iov, int64_t timeout) { - int64_t ret; + int64_t ret = 0; int64_t timeout_per = timeout / nb_iov; for (int i = 0; i < nb_iov; i++) { ret = udp_write_pkt(ctx, io, &iov[i], timeout_per); @@ -138,7 +138,7 @@ static int64_t udp_write_vec(AVTContext *ctx, AVTIOCtx *io, static int64_t udp_read_input(AVTContext *ctx, AVTIOCtx *io, AVTBuffer **_buf, size_t len, int64_t timeout) { - int ret, err; + int ret; uint8_t *data; size_t buf_len, off = 0; AVTBuffer *buf = *_buf; @@ -166,13 +166,14 @@ static int64_t udp_read_input(AVTContext *ctx, AVTIOCtx *io, struct sockaddr_in6 remote_addr = { }; socklen_t remote_addr_len = sizeof(remote_addr); - ret = recvfrom(io->sc.socket, data, buf_len, + ret = recvfrom(io->sc.socket, data + off, buf_len, !timeout ? MSG_DONTWAIT : 0, &remote_addr, &remote_addr_len); if (ret < 0) return avt_handle_errno(io, "Unable to receive message: %s"); /* Adjust new size in case of underreads */ + [[maybe_unused]] int err; err = avt_buffer_resize(buf, off + len); avt_assert2(err >= 0); @@ -203,3 +204,17 @@ const AVTIO avt_io_udp = { .flush = NULL, .close = udp_close, }; + +const AVTIO avt_io_udp_lite = { + .name = "udp_lite", + .type = AVT_IO_UDP_LITE, + .init = udp_init, + .get_max_pkt_len = udp_max_pkt_len, + .read_input = udp_read_input, + .write_vec = udp_write_vec, + .write_pkt = udp_write_pkt, + .rewrite = NULL, + .seek = NULL, + .flush = NULL, + .close = udp_close, +}; diff --git a/libavtransport/output_packet.c b/libavtransport/output_packet.c index 4087ce8..92e6557 100644 --- a/libavtransport/output_packet.c +++ b/libavtransport/output_packet.c @@ -201,49 +201,6 @@ int avt_send_stream_register(AVTOutput *out, AVTStream *st) return send_pkt(out, pkt, nullptr); } -#define INIT_SEGMENTED(buf, desc) \ - int err; \ - void *series = NULL; \ - size_t maxp = avt_packet_get_max_size(out); \ - size_t payload_size = avt_buffer_get_data_len(buf); \ - size_t pbytes = payload_size; \ - size_t seg_len = AVT_MIN(pbytes, maxp); \ - \ - enum AVTDataCompression data_compression; \ - err = avt_payload_compress(out, &buf, desc, &data_compression); \ - if (err < 0) \ - return err; \ - \ - AVTBuffer tmp; \ - avt_buffer_quick_ref(&tmp, buf, 0, seg_len); - -#define SEGMENT(buf, seg_desc) \ - avt_buffer_quick_unref(&tmp); \ - if (err < 0) \ - return err; \ - \ - pbytes -= seg_len; \ - while (pbytes) { \ - const uint32_t seq = atomic_fetch_add(&out->seq, 1ULL) & UINT32_MAX; \ - seg_len = AVT_MIN(pbytes, maxp); \ - avt_buffer_quick_ref(&tmp, buf, payload_size - pbytes, seg_len); \ - union AVTPacketData seg = { .generic_segment = { \ - .generic_segment_descriptor = seg_desc, \ - .stream_id = st->id, \ - .pkt_total_data = payload_size, \ - .seg_offset = payload_size - pbytes, \ - .seg_length = seg_len, \ - }}; \ - \ - avt_buffer_quick_unref(&tmp); \ - if (err < 0) \ - break; \ - \ - pbytes -= seg_len; \ - } while (pbytes); \ - \ - return err; - int avt_send_stream_data(AVTOutput *out, AVTStream *st, AVTPacket *pkt) { /* Compress payload if necessary */ diff --git a/libavtransport/protocol_common.h b/libavtransport/protocol_common.h index 986b468..c75c2cc 100644 --- a/libavtransport/protocol_common.h +++ b/libavtransport/protocol_common.h @@ -50,12 +50,13 @@ typedef struct AVTProtocol { /* Send. Returns positive offset on success, otherwise negative error. * Returns offset to which packet was written to. */ int64_t (*send_packet)(AVTContext *ctx, AVTProtocolCtx *p, - union AVTPacketData pkt, AVTBuffer *pl); + union AVTPacketData pkt, AVTBuffer *pl, + int64_t timeout); /* Send a sequence of packets. Returns positive offset on success, otherwise negative error */ int64_t (*send_seq)(AVTContext *ctx, AVTProtocolCtx *p, - AVTPacketFifo *seq); + AVTPacketFifo *seq, int64_t timeout); /* Overwrite a packet at a given offset */ int (*update_packet)(AVTContext *ctx, AVTProtocolCtx *p, diff --git a/libavtransport/protocol_noop.c b/libavtransport/protocol_noop.c index fb5f053..c774b52 100644 --- a/libavtransport/protocol_noop.c +++ b/libavtransport/protocol_noop.c @@ -66,7 +66,8 @@ static int noop_rm_dst(AVTContext *ctx, AVTProtocolCtx *p, AVTAddress *addr) } static int64_t noop_send_packet(AVTContext *ctx, AVTProtocolCtx *p, - union AVTPacketData pkt, AVTBuffer *pl) + union AVTPacketData pkt, AVTBuffer *pl, + int64_t timeout) { uint8_t hdr[AVT_MAX_HEADER_LEN]; size_t hdr_len = 0; @@ -78,12 +79,11 @@ static int64_t noop_send_packet(AVTContext *ctx, AVTProtocolCtx *p, } static int64_t noop_send_seq(AVTContext *ctx, AVTProtocolCtx *p, - AVTPacketFifo *seq) + AVTPacketFifo *seq, int64_t timeout) { - uint8_t hdr[AVT_MAX_HEADER_LEN]; - size_t hdr_len = 0; + int err; - // TODO + err = p->io->write_vec(ctx, p->io_ctx, seq->data, seq->nb, timeout); return 0; } diff --git a/libavtransport/scheduler.c b/libavtransport/scheduler.c index 7c10e31..4e7bf5c 100644 --- a/libavtransport/scheduler.c +++ b/libavtransport/scheduler.c @@ -385,6 +385,11 @@ int avt_scheduler_pop(AVTScheduler *s, AVTPacketFifo **seq) return 0; } +int avt_scheduler_flush(AVTScheduler *s, AVTPacketFifo **seq) +{ + return 0; +} + int avt_scheduler_done(AVTScheduler *s, AVTPacketFifo *seq) { avt_assert1(seq->nb); /* Scheduler FIFO was not fully consumed */ diff --git a/libavtransport/scheduler.h b/libavtransport/scheduler.h index 0bd7264..3a86d6c 100644 --- a/libavtransport/scheduler.h +++ b/libavtransport/scheduler.h @@ -100,6 +100,9 @@ int avt_scheduler_push(AVTScheduler *s, union AVTPacketData pkt, AVTBuffer *pl); int avt_scheduler_pop(AVTScheduler *s, AVTPacketFifo **seq); + +int avt_scheduler_flush(AVTScheduler *s, AVTPacketFifo **seq); + int avt_scheduler_done(AVTScheduler *s, AVTPacketFifo *seq); void avt_scheduler_free(AVTScheduler *s); diff --git a/libavtransport/tests/file_io_template.c b/libavtransport/tests/file_io_common.c similarity index 84% rename from libavtransport/tests/file_io_template.c rename to libavtransport/tests/file_io_common.c index fd9acc0..67937cd 100644 --- a/libavtransport/tests/file_io_template.c +++ b/libavtransport/tests/file_io_common.c @@ -24,10 +24,12 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include -#include +#include + +#include "file_io_common.h" +#include #include "packet_common.h" static int read_fn(AVTContext *avt, const AVTIO *io, AVTIOCtx *io_ctx, @@ -44,26 +46,26 @@ static int read_fn(AVTContext *avt, const AVTIO *io, AVTIOCtx *io_ctx, /* Read */ ret = io->read_input(avt, io_ctx, test_buf, bytes, INT64_MAX); if (ret <= 0) { - printf("No bytes read\n"); + avt_log(avt, AVT_LOG_ERROR, "No bytes read\n"); return AVT_ERROR(EINVAL); } if ((ret - pre) != bytes) { - printf("Too few bytes read: got %" PRIi64 "; wanted %i\n", ret, bytes); + avt_log(avt, AVT_LOG_ERROR, "Too few bytes read: got %" PRIi64 "; wanted %i\n", ret, bytes); return AVT_ERROR(EINVAL); } /* Check data read */ test_buf_data = avt_buffer_get_data(*test_buf, &test_buf_size); if (memcmp(test_buf_data, test_pkt->hdr, test_buf_size)) { - printf("Mismatch between read and written data!\n"); + avt_log(avt, AVT_LOG_ERROR, "Mismatch between read and written data!\n"); return AVT_ERROR(EINVAL); } return 0; } -static int test_io(AVTContext *avt, const AVTIO *io, AVTIOCtx *io_ctx) +int file_io_test(AVTContext *avt, const AVTIO *io, AVTIOCtx *io_ctx) { int64_t ret; @@ -84,12 +86,12 @@ static int test_io(AVTContext *avt, const AVTIO *io, AVTIOCtx *io_ctx) if (ret < 0) { goto fail; } else if (ret == 0) { - printf("No bytes written\n"); + avt_log(avt, AVT_LOG_ERROR,"No bytes written\n"); ret = AVT_ERROR(EINVAL); goto fail; } else if (ret != sum) { - printf("Bytes written do not match: got %" PRIi64 "; wanted %" PRIi64 "\n", - ret, sum); + avt_log(avt, AVT_LOG_ERROR,"Bytes written do not match: got %" PRIi64 "; wanted %" PRIi64 "\n", + ret, sum); ret = AVT_ERROR(EINVAL); goto fail; } @@ -110,11 +112,11 @@ static int test_io(AVTContext *avt, const AVTIO *io, AVTIOCtx *io_ctx) int64_t off = io->read_input(avt, io_ctx, &buf, test_pkt[i].hdr_len, INT64_MAX); if (off == 0) { - printf("No bytes read\n"); + avt_log(avt, AVT_LOG_ERROR,"No bytes read\n"); ret = AVT_ERROR(EINVAL); goto fail; } else if (off < 0) { - printf("Error reading\n"); + avt_log(avt, AVT_LOG_ERROR,"Error reading\n"); ret = off; goto fail; } @@ -124,8 +126,8 @@ static int test_io(AVTContext *avt, const AVTIO *io, AVTIOCtx *io_ctx) if ((diff != test_pkt[i].hdr_len) || ((i == (AVT_ARRAY_ELEMS(test_pkt) - 1)) && (sum != off))) { - printf("Too few bytes read: got %" PRIi64 "; wanted %i\n", - diff, test_pkt[i].hdr_len); + avt_log(avt, AVT_LOG_ERROR,"Too few bytes read: got %" PRIi64 "; wanted %i\n", + diff, test_pkt[i].hdr_len); ret = AVT_ERROR(EINVAL); goto fail; } @@ -133,7 +135,7 @@ static int test_io(AVTContext *avt, const AVTIO *io, AVTIOCtx *io_ctx) size_t test_buf_size; uint8_t *test_buf_data = avt_buffer_get_data(buf, &test_buf_size); if (memcmp(test_buf_data, test_pkt[i].hdr, test_buf_size)) { - printf("Mismatch between read and written data!\n"); + avt_log(avt, AVT_LOG_ERROR,"Mismatch between read and written data!\n"); goto fail; } diff --git a/libavtransport/tests/file_io_common.h b/libavtransport/tests/file_io_common.h new file mode 100644 index 0000000..33bc992 --- /dev/null +++ b/libavtransport/tests/file_io_common.h @@ -0,0 +1,34 @@ +/* + * Copyright © 2024, Lynne + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TEST_FILE_IO_COMMON_H +#define TEST_FILE_IO_COMMON_H + +#include "io_common.h" + +int file_io_test(AVTContext *avt, const AVTIO *io, AVTIOCtx *io_ctx); + +#endif /* TEST_FILE_IO_COMMON_H */ diff --git a/libavtransport/tests/io_fd.c b/libavtransport/tests/io_fd.c index 6a5c2ff..3760063 100644 --- a/libavtransport/tests/io_fd.c +++ b/libavtransport/tests/io_fd.c @@ -29,7 +29,7 @@ #include #include "io_common.h" -#include "file_io_template.c" +#include "file_io_common.h" extern const AVTIO avt_io_fd_path; @@ -55,7 +55,7 @@ int main(void) return AVT_ERROR(ret); } - ret = test_io(avt, io, io_ctx); + ret = file_io_test(avt, io, io_ctx); if (ret) io->close(avt, &io_ctx); diff --git a/libavtransport/tests/io_file.c b/libavtransport/tests/io_file.c index ad2197a..25f46b0 100644 --- a/libavtransport/tests/io_file.c +++ b/libavtransport/tests/io_file.c @@ -29,7 +29,7 @@ #include #include "io_common.h" -#include "file_io_template.c" +#include "file_io_common.h" /* Always available, on all platforms */ extern const AVTIO avt_io_file; @@ -56,7 +56,7 @@ int main(void) return AVT_ERROR(ret); } - ret = test_io(avt, io, io_ctx); + ret = file_io_test(avt, io, io_ctx); if (ret) io->close(avt, &io_ctx); diff --git a/libavtransport/tests/io_udp.c b/libavtransport/tests/io_udp.c index 2860e69..6863b34 100644 --- a/libavtransport/tests/io_udp.c +++ b/libavtransport/tests/io_udp.c @@ -24,163 +24,19 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include -#include -#include -#include -#include - -#include "address.h" -#include "io_common.h" +#include "net_common.h" extern const AVTIO avt_io_udp; -static const AVTIO *io = &avt_io_udp; - -typedef struct ThreadCtx { - AVTContext *avt; - - AVTAddress addr; - - AVTIOCtx *ioctx; - AVTPktd test_pkts[16]; - int nb_pkts; - - AVTBuffer *output; - - int err; -} ThreadCtx; - -static int free_context(thrd_t tctx, ThreadCtx *ctx) -{ - int ret; - thrd_join(tctx, &ret); - avt_addr_free(&ctx->addr); - avt_buffer_unref(&ctx->output); - if (ctx->ioctx) { - if (ret < 0) - io->close(ctx->avt, &ctx->ioctx); - else - ret = io->close(ctx->avt, &ctx->ioctx); - } - free(ctx); - - return ret; -} - -static int client_fn(void *_ctx) -{ - int64_t ret; - ThreadCtx *ctx = _ctx; - - /* Write single packet test */ - ret = io->write_pkt(ctx->avt, ctx->ioctx, &ctx->test_pkts[0], INT64_MAX); - if (ret <= 0) - printf("Error writing %" PRIi64 "\n", ret); - else - printf("Wrote %" PRIi64 " bytes\n", ret); - - thrd_exit(ret); -} - -static int server_fn(void *_ctx) -{ - int64_t ret; - ThreadCtx *ctx = _ctx; - - /* Read */ - ret = io->read_input(ctx->avt, ctx->ioctx, &ctx->output, - ctx->test_pkts[0].hdr_len, - INT64_MAX); - if (ret <= 0) { - printf("No bytes read\n"); - } else { - printf("Received %" PRIi64 " bytes\n", ret); - - size_t buf_len; - uint8_t *data = avt_buffer_get_data(ctx->output, &buf_len); - if (memcmp(data, ctx->test_pkts[0].hdr, ctx->test_pkts[0].hdr_len)) { - printf("Mismatch between data sent and received!\n"); - ret = AVT_ERROR(EINVAL); - } - } - - thrd_exit(ret); -} int main(void) { - int64_t ret; - AVTContext *avt; - - thrd_t server_thread = 0; - ThreadCtx *server_ctx = calloc(1, sizeof(ThreadCtx)); - if (!server_ctx) - return AVT_ERROR(ENOMEM); - - thrd_t client_thread = 0; - ThreadCtx *client_ctx = calloc(1, sizeof(ThreadCtx)); - if (!client_ctx) { - free(server_ctx); - return AVT_ERROR(ENOMEM); - } - - ret = avt_init(&avt, NULL); - if (ret < 0) - goto end; - server_ctx->avt = avt; - client_ctx->avt = avt; - - /** Server */ - ret = avt_addr_from_url(avt, &server_ctx->addr, true, "udp://[::1]"); - if (ret < 0) - goto end; - - ret = io->init(avt, &server_ctx->ioctx, &server_ctx->addr); - if (ret < 0) - goto end; - - /** Client */ - ret = avt_addr_from_url(avt, &client_ctx->addr, false, "udp://[::1]"); + NetTestContext ntc; + int ret = net_io_init(&ntc, &avt_io_udp, "udp://[::1]"); if (ret < 0) - goto end; - - ret = io->init(avt, &client_ctx->ioctx, &client_ctx->addr); - if (ret < 0) - goto end; - - uint32_t mtu = io->get_max_pkt_len(avt, client_ctx->ioctx); - if (!mtu) { - ret = AVT_ERROR(EINVAL); - goto end; - } - - printf("MTU received = %u\n", mtu); - - /* Packet data */ - AVTPktd test_pkt[16] = { }; - for (int i = 0; i < AVT_ARRAY_ELEMS(test_pkt); i++) { - test_pkt[i].hdr_len = sizeof(test_pkt[i].hdr); - for (int j = 0; j < test_pkt[i].hdr_len; j++) - test_pkt[i].hdr[j] = (i + j) & 0xFF; - } - - memcpy(server_ctx->test_pkts, test_pkt, sizeof(test_pkt)); - thrd_create(&server_thread, server_fn, server_ctx); - - memcpy(client_ctx->test_pkts, test_pkt, sizeof(test_pkt)); - thrd_create(&client_thread, client_fn, client_ctx); - -end: - int err; - - err = free_context(client_thread, client_ctx); - if (ret >= 0) - ret = err; + return AVT_ERROR(ret); - err = free_context(server_thread, server_ctx); - if (ret >= 0) - ret = err; + ret = net_io_test(&ntc); - avt_close(&avt); + net_io_free(&ntc); return AVT_ERROR(ret); } diff --git a/libavtransport/tests/io_udp_lite.c b/libavtransport/tests/io_udp_lite.c new file mode 100644 index 0000000..70c6923 --- /dev/null +++ b/libavtransport/tests/io_udp_lite.c @@ -0,0 +1,42 @@ +/* + * Copyright © 2024, Lynne + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "net_common.h" + +extern const AVTIO avt_io_udp_lite; + +int main(void) +{ + NetTestContext ntc; + int ret = net_io_init(&ntc, &avt_io_udp_lite, "udplite://[::1]"); + if (ret < 0) + return AVT_ERROR(ret); + + ret = net_io_test(&ntc); + + net_io_free(&ntc); + return AVT_ERROR(ret); +} diff --git a/libavtransport/tests/meson.build b/libavtransport/tests/meson.build index 68647d7..76f1c26 100644 --- a/libavtransport/tests/meson.build +++ b/libavtransport/tests/meson.build @@ -31,7 +31,7 @@ ldpc_encode_test = executable('ldpc_encode', test('LDPC encoding', ldpc_encode_test) io_file_test = executable('io_file', - sources : [ 'io_file.c' ], + sources : [ 'file_io_common.c', 'io_file.c' ], include_directories : [ '../' ], objects : [ avtransport_lib.extract_objects([ 'buffer.c', 'io_file.c' ]) ], dependencies : [ avtransport_deps_list, avtransport_dep ], @@ -40,7 +40,7 @@ test('File I/O', io_file_test) if host_machine.system() != 'windows' io_file_test = executable('io_fd', - sources : [ 'io_fd.c' ], + sources : [ 'file_io_common.c', 'io_fd.c' ], include_directories : [ '../' ], objects : [ avtransport_lib.extract_objects([ 'buffer.c', 'io_fd.c' ]) ], dependencies : [ avtransport_deps_list, avtransport_dep ], @@ -49,13 +49,21 @@ if host_machine.system() != 'windows' endif io_udp_test = executable('io_udp', - sources : [ 'io_udp.c' ], + sources : [ 'net_common.c', 'io_udp.c' ], include_directories : [ '../' ], objects : [ avtransport_lib.extract_objects([ 'address.c', 'io_udp.c', 'io_socket_common.c', 'buffer.c']) ], dependencies : [ avtransport_deps_list, avtransport_dep ], ) test('UDP I/O', io_udp_test) +io_udp_lite_test = executable('io_udp_lite', + sources : [ 'net_common.c', 'io_udp_lite.c' ], + include_directories : [ '../' ], + objects : [ avtransport_lib.extract_objects([ 'address.c', 'io_udp.c', 'io_socket_common.c', 'buffer.c']) ], + dependencies : [ avtransport_deps_list, avtransport_dep ], +) +test('UDP-Lite I/O', io_udp_lite_test) + address_test = executable('address', sources : [ 'address.c' ], include_directories : [ '../' ], diff --git a/libavtransport/tests/net_common.c b/libavtransport/tests/net_common.c new file mode 100644 index 0000000..a93a659 --- /dev/null +++ b/libavtransport/tests/net_common.c @@ -0,0 +1,223 @@ +/* + * Copyright © 2024, Lynne + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "net_common.h" + +#include + +typedef struct NetListenerContext { + AVTContext *avt; + const AVTIO *io; + AVTIOCtx *ioctx; + AVTBuffer *output; + size_t pkt_len; + int nb_pkts; +} NetListenerContext; + +static int listener_fn(void *_ctx) +{ + int64_t prev = 0, ret = 0; + NetListenerContext *ctx = _ctx; + + /* Read */ + for (int i = 0; i < ctx->nb_pkts; i++) { + ret = ctx->io->read_input(ctx->avt, ctx->ioctx, &ctx->output, + ctx->pkt_len, INT64_MAX); + if (ret < 0) { + avt_log(ctx->avt, AVT_LOG_ERROR, "No bytes read\n"); + thrd_exit(AVT_ERROR(EINVAL)); + } else if ((ret - prev) != ctx->pkt_len) { + avt_log(ctx->avt, AVT_LOG_ERROR, "Read length mismatch: received " + "%" PRIi64 " bytes, expected %zu bytes\n", ret, ctx->pkt_len); + thrd_exit(AVT_ERROR(EINVAL)); + } else { + avt_log(ctx->avt, AVT_LOG_INFO, "Received %" PRIi64 " bytes\n", ret - prev); + } + prev = ret; + } + + if (ret != avt_buffer_get_data_len(ctx->output)) { + avt_log(ctx->avt, AVT_LOG_ERROR, "Read length mismatch: received " + "%" PRIi64 " bytes, expected %zu bytes\n", + ret, avt_buffer_get_data_len(ctx->output)); + thrd_exit(AVT_ERROR(EINVAL)); + } + + thrd_exit(ret); +} + +int net_io_test(NetTestContext *ntc) +{ + int64_t ret = 0; + int t_err = thrd_success, t_res = 0; + thrd_t listener_thread = 0; + NetListenerContext listener_ctx = { + .avt = ntc->avt, + .io = ntc->io, + .ioctx = ntc->ioctx_listener, + }; + + uint32_t mtu = ntc->io->get_max_pkt_len(ntc->avt, ntc->ioctx_sender); + if (!mtu) { + avt_log(ntc->avt, AVT_LOG_ERROR, "MTU received of zero!\n"); + ret = AVT_ERROR(EINVAL); + goto fail; + } + + avt_log(ntc->avt, AVT_LOG_INFO, "MTU received = %u\n", mtu); + + /* Random packet data */ + int64_t sum = 0; + AVTPktd test_pkt[16] = { }; + for (int i = 0; i < AVT_ARRAY_ELEMS(test_pkt); i++) { + test_pkt[i].hdr_len = AVT_MIN(mtu, sizeof(test_pkt[i].hdr)); + sum += test_pkt[i].hdr_len; + for (int j = 0; j < test_pkt[i].hdr_len; j++) + test_pkt[i].hdr[j] = (rand() & 0xFF); + printf("Shit %i = %x\n", i, test_pkt[i].hdr[0]); + } + + listener_ctx.output = NULL; + listener_ctx.pkt_len = test_pkt[0].hdr_len; + listener_ctx.nb_pkts = AVT_ARRAY_ELEMS(test_pkt); + t_err = thrd_create(&listener_thread, listener_fn, &listener_ctx); + if (t_err != thrd_success) + goto fail; + + /* Write vector test */ + ret = ntc->io->write_vec(ntc->avt, ntc->ioctx_sender, test_pkt, + AVT_ARRAY_ELEMS(test_pkt), INT64_MAX); + if (ret < 0) { + goto fail; + } else if (ret == 0) { + avt_log(ntc->avt, AVT_LOG_ERROR,"No bytes written\n"); + ret = AVT_ERROR(EINVAL); + goto fail; + } else if (ret != sum) { + avt_log(ntc->avt, AVT_LOG_ERROR,"Bytes written do not match: got %" PRIi64 "; wanted %" PRIi64 "\n", + ret, sum); + ret = AVT_ERROR(EINVAL); + goto fail; + } + + if (thrd_join(listener_thread, &t_res) != thrd_success) + goto fail; + + size_t buf_size; + uint8_t *buf_data = avt_buffer_get_data(listener_ctx.output, &buf_size); + + /* Two loops as packets may be received out of order */ + for (int i = 0; i < listener_ctx.nb_pkts; i++) { + bool found = false; + for (int j = (listener_ctx.nb_pkts - 1); j >= 0; j--) { + if (!memcmp(buf_data, test_pkt[j].hdr, test_pkt[j].hdr_len)) { + found = true; + break; + } + } + if (!found) { + avt_log(ntc->avt, AVT_LOG_ERROR,"Data written and read does not match (pkt %i)\n", i); + ret = AVT_ERROR(EINVAL); + goto fail; + } + buf_data += test_pkt[i].hdr_len; + } + +fail: + if (ret >= 0 && t_res) + ret = t_res; + + if (ret >= 0 && (t_err != thrd_success)) { + avt_log(ntc->avt, AVT_LOG_ERROR, "Threading error\n"); + ret = AVT_ERROR(EINVAL); + listener_thread = 0; + } + + if (listener_thread) + thrd_join(listener_thread, NULL); + + avt_buffer_unref(&listener_ctx.output); + + return ret; +} + +int net_io_init(NetTestContext *ntc, const AVTIO *io, const char *url) +{ + int ret; + AVTAddress listener_addr = { }; + AVTAddress sender_addr = { }; + + memset(ntc, 0, sizeof(*ntc)); + ntc->io = io; + + ret = avt_init(&ntc->avt, NULL); + if (ret < 0) + goto fail; + + /* Listener */ + ret = avt_addr_from_url(ntc->avt, &listener_addr, true, url); + if (ret < 0) + goto fail; + + ret = ntc->io->init(ntc->avt, &ntc->ioctx_listener, &listener_addr); + if (ret < 0) + goto fail; + + /* Sender */ + ret = avt_addr_from_url(ntc->avt, &sender_addr, false, url); + if (ret < 0) + goto fail; + + ret = ntc->io->init(ntc->avt, &ntc->ioctx_sender, &sender_addr); + if (ret < 0) + goto fail; + + avt_addr_free(&listener_addr); + avt_addr_free(&sender_addr); + return ret; + +fail: + avt_addr_free(&listener_addr); + avt_addr_free(&sender_addr); + net_io_free(ntc); + return ret; +} + +int net_io_free(NetTestContext *ntc) +{ + int err = ntc->io->close(ntc->avt, &ntc->ioctx_sender); + if (err) + ntc->io->close(ntc->avt, &ntc->ioctx_listener); + else + err = ntc->io->close(ntc->avt, &ntc->ioctx_listener); + avt_close(&ntc->avt); + return err; +} diff --git a/libavtransport/tests/net_common.h b/libavtransport/tests/net_common.h new file mode 100644 index 0000000..288edff --- /dev/null +++ b/libavtransport/tests/net_common.h @@ -0,0 +1,44 @@ +/* + * Copyright © 2024, Lynne + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TEST_NET_COMMON_H +#define TEST_NET_COMMON_H + +#include "io_common.h" + +typedef struct NetTestContext { + AVTContext *avt; + + const AVTIO *io; + AVTIOCtx *ioctx_sender; + AVTIOCtx *ioctx_listener; +} NetTestContext; + +int net_io_init(NetTestContext *ntc, const AVTIO *io, const char *url); +int net_io_test(NetTestContext *ntc); +int net_io_free(NetTestContext *ntc); + +#endif /* TEST_NET_COMMON_H */ diff --git a/libavtransport/tests/udp_common.c b/libavtransport/tests/udp_common.c new file mode 100644 index 0000000..5e4cfbd --- /dev/null +++ b/libavtransport/tests/udp_common.c @@ -0,0 +1,26 @@ +/* + * Copyright © 2024, Lynne + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + diff --git a/meson.build b/meson.build index 20e627d..306cf79 100644 --- a/meson.build +++ b/meson.build @@ -28,12 +28,11 @@ project('avtransport', license_files: [ 'LICENSE.md' ], default_options: [ 'buildtype=debugoptimized', - 'c_std=c2x', -# 'c_std=c23', + 'c_std=c23', 'warning_level=1' ], version: '0.0.4', - meson_version: '>=1.1.0', + meson_version: '>=1.4.0', ) spec_file = files('draft-avtransport-spec.bs') diff --git a/tools/README.md b/tools/README.md index a013007..ae8e6f4 100644 --- a/tools/README.md +++ b/tools/README.md @@ -12,3 +12,11 @@ anything libavformat supports (mkv, mp4, MPEG-TS). avcat can combine or append inputs, extract streams, add metadata, reconstruct timestamps, fix errors, and verify integrity. + + +avtdump +------- + +avtdump is a utility to inspect AVTransport streams and files, logging any issues, +stream changes, packet sizes and so on. It can also provide such information as a JSON +stream. diff --git a/tools/avcat.c b/tools/avcat.c index f96b3e4..d660cea 100644 --- a/tools/avcat.c +++ b/tools/avcat.c @@ -78,21 +78,20 @@ static enum IOMode path_is_avt(const char *path, enum AVTConnectionType *conn_ty !strncmp(path, "file://", strlen("file://"))) { *conn_type = AVT_CONNECTION_URL; return IO_AVT; - } else if (!strcmp(&path[len - 4], ".avt") || !strcmp(&path[len - 3], ".at")) { + } else if (!strcmp(&path[len - 4], ".avt") || + !strcmp(&path[len - 3], ".ati") || /* Images */ + !strcmp(&path[len - 3], ".ats")) { /* Subtitles */ *conn_type = AVT_CONNECTION_FILE; return IO_AVT; } else if (is_out && /* For output, anything that can be concat'd payload */ (!strcmp(&path[len - 5], ".h264") || !strcmp(&path[len - 5], ".h265") || !strcmp(&path[len - 5], ".hevc") || - !strcmp(&path[len - 5], ".tiff") || - !strcmp(&path[len - 5], ".jpeg") || - !strcmp(&path[len - 4], ".dng") || - !strcmp(&path[len - 4], ".png") || - !strcmp(&path[len - 4], ".jpg") || !strcmp(&path[len - 4], ".aac") || !strcmp(&path[len - 4], ".ac3") || - !strcmp(&path[len - 4], ".svg"))) { + !strcmp(&path[len - 4], ".nal") || /* Raw MPEG NALUs */ + !strcmp(&path[len - 4], ".obu") || /* Raw AV1 OBUs */ + !strcmp(&path[len - 4], ".raw"))) { /* Generic raw data */ return IO_RAW; } else if (!is_out && /* Individual files do not need any framing */ (!strcmp(&path[len - 5], ".tiff") || @@ -100,6 +99,7 @@ static enum IOMode path_is_avt(const char *path, enum AVTConnectionType *conn_ty !strcmp(&path[len - 4], ".dng") || !strcmp(&path[len - 4], ".png") || !strcmp(&path[len - 4], ".jpg") || + !strcmp(&path[len - 4], ".cbor") || !strcmp(&path[len - 4], ".svg"))) { return IO_RAW; } diff --git a/tools/avtdump.c b/tools/avtdump.c new file mode 100644 index 0000000..cb21a3c --- /dev/null +++ b/tools/avtdump.c @@ -0,0 +1,31 @@ +/* Copyright © 2024, Lynne + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +int main(int argc, char **argv) +{ + return 0; +} diff --git a/tools/meson.build b/tools/meson.build index cb68714..dcf9879 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -68,3 +68,17 @@ executable('avcat', summary({ 'ffmpeg libs': lavc_dep.found() and lavf_dep.found() and lavu_dep.found(), }, section: 'avcat', bool_yn: true) + +# avtdump +#============================================================================ +avtdump_sources = files( + 'avtdump.c', +) + +executable('avtdump', + install: true, + sources: avtdump_sources + avtransport_spec_pub_headers, + + include_directories: avtransport_inc, + link_with: [ avtransport_lib ] +)