From d30bdd0dc1d9923ecc6556a0e77209cb71c1bea1 Mon Sep 17 00:00:00 2001 From: Yahweasel Date: Wed, 18 Oct 2023 10:33:02 -0400 Subject: [PATCH] Adding the equivalent of ff_copyout_frame_ptr for packets, ff_copyout_packet_ptr --- docs/API.md | 24 ++++++++++- funcs.json | 4 ++ post.in.js | 43 ++++++++++++++++--- tests/tests/624-decode-and-filter-combined.js | 10 ++++- 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/docs/API.md b/docs/API.md index bee77a1d..271a5a48 100644 --- a/docs/API.md +++ b/docs/API.md @@ -99,7 +99,8 @@ ff_read_multi( fmt_ctx: number, pkt: number, devfile?: string, opts?: { limit?: number, // OUTPUT limit, in bytes devLimit?: number, // INPUT limit, in bytes (don't read if less than this much data is available) - unify?: boolean // If true, unify the packets into a single stream (called 0), so that the output is in the same order as the input + unify?: boolean, // If true, unify the packets into a single stream (called 0), so that the output is in the same order as the input + copyoutPacket?: string // Version of ff_copyout_packet to use } ): Promise<[number, Record]> ``` @@ -127,16 +128,35 @@ are the indices of the `Stream` objects in the `Stream[]` array given by `ff_init_demuxer_file`. Alternatively, you can use `unify`, in which case all packets will be in input order in a single array, `packets[0]`. +There are multiple versions of `ff_copyout_packet`, only one of which is +actually used to copy out packets. See the documentation of `ff_copyout_packet` +below for how to use the `copyoutPacket` option. + ## Data manipulation -### `ff_copyout_packet` +### `ff_copyout_packet` and variants ``` ff_copyout_packet(pkt: number): Promise ``` +Variants: `ff_copyout_packet_ptr` + Copy a packet from internal libav memory (`pkt`) as a libav.js object. +The `ff_copyout_packet_ptr` function is also available, and copies the packet +into a separate `AVPacket` pointer, instead of actually copying out any data. +This is a good compromise if you're building pipelines, e.g. reading then +decoding, to avoid copying data back and forth when that data is just going back +into libav.js. Be careful, though! `ff_read_multi` reads from every stream, and +if you're only using data from one of them, copied packets using +`ff_copyout_packet_ptr` will leak memory! Use `ff_copyout_packet_ptr` carefully. + +Metafunctions that use `ff_copyout_packet` internally, namely `ff_read_multi`, +have a configuration option, `copyoutPacket`, to specify which version of +`ff_copyout_packet` to use. It is a string option, accepting the following +values: `"default", "ptr"`. + ### `ff_copyin_packet` ``` diff --git a/funcs.json b/funcs.json index 0d8c0f98..d24e974f 100644 --- a/funcs.json +++ b/funcs.json @@ -12,8 +12,10 @@ ["av_frame_ref", "number", ["number", "number"]], ["av_frame_unref", null, ["number"]], ["av_packet_alloc", "number", []], + ["av_packet_clone", "number", ["number"]], ["av_packet_free", null, ["number"]], ["av_packet_new_side_data", "number", ["number", "number", "number"]], + ["av_packet_ref", "number", ["number", "number"]], ["av_packet_rescale_ts_js", null, ["number", "number", "number", "number", "number"]], ["av_packet_unref", null, ["number"]], ["av_strdup", "number", ["string"]], @@ -175,8 +177,10 @@ "ff_frame_video_packed_size", "ff_copyout_frame_video_packed", "ff_copyout_frame_video_imagedata", + "ff_copyout_frame_ptr", "ff_copyin_frame", "ff_copyout_packet", + "ff_copyout_packet_ptr", "ff_copyin_packet", "ff_malloc_int32_list", "ff_malloc_int64_list", diff --git a/post.in.js b/post.in.js index 34a783ec..aa236cf7 100644 --- a/post.in.js +++ b/post.in.js @@ -1099,7 +1099,8 @@ var ff_write_multi = Module.ff_write_multi = function(oc, pkt, inPackets, interl * fmt_ctx: number, pkt: number, devfile?: string, opts?: { * limit?: number, // OUTPUT limit, in bytes * devLimit?: number, // INPUT limit, in bytes (don't read if less than this much data is available) - * unify?: boolean // If true, unify the packets into a single stream (called 0), so that the output is in the same order as the input + * unify?: boolean, // If true, unify the packets into a single stream (called 0), so that the output is in the same order as the input + * copyoutPacket?: string // Version of ff_copyout_packet to use * } * ): @promsync@[number, Record]@ */ @@ -1116,6 +1117,9 @@ function ff_read_multi(fmt_ctx, pkt, devfile, opts) { if (opts.devLimit) devLimit = opts.devLimit; var unify = !!opts.unify; + var copyoutPacket = ff_copyout_packet; + if (opts.copyoutPacket) + copyoutPacket = ff_copyout_packet_versions[opts.copyoutPacket]; function step() { // If we risk running past the end of the currently-read data, stop now @@ -1131,13 +1135,13 @@ function ff_read_multi(fmt_ctx, pkt, devfile, opts) { return [ret, outPackets]; // And copy it out - var packet = ff_copyout_packet(pkt); - var idx = unify ? 0 : packet.stream_index; + var packet = copyoutPacket(pkt); + var idx = unify ? 0 : AVPacket_stream_index(pkt); if (!(idx in outPackets)) outPackets[idx] = []; outPackets[idx].push(packet); + sz += AVPacket_size(pkt); av_packet_unref(pkt); - sz += packet.data.length; if (opts.limit && sz >= opts.limit) return [-6 /* EAGAIN */, outPackets]; @@ -1749,7 +1753,7 @@ var ff_copyin_frame = Module.ff_copyin_frame = function(framePtr, frame) { throw new Error("Failed to reference frame data: " + ff_error(ret)); av_frame_unref(frame); av_frame_free_js(frame); - return 0; + return; } if (frame.width) @@ -1917,6 +1921,24 @@ var ff_copyout_side_data = Module.ff_copyout_side_data = function(pkt) { return ret; }; +/** + * Copy "out" a packet by just copying its data into a new AVPacket. + * @param pkt AVPacket + */ +/// @types ff_copyout_packet_ptr@sync(pkt: number): @promise@number@ +var ff_copyout_packet_ptr = Module.ff_copyout_packet_ptr = function(pkt) { + var ret = av_packet_clone(pkt); + if (!ret) + throw new Error("Failed to clone packet"); + return ret; +}; + +// Versions of ff_copyout_packet +var ff_copyout_packet_versions = { + default: ff_copyout_packet, + ptr: ff_copyout_packet_ptr +}; + /** * Copy in a packet. * @param pktPtr AVPacket @@ -1924,6 +1946,17 @@ var ff_copyout_side_data = Module.ff_copyout_side_data = function(pkt) { */ /// @types ff_copyin_packet@sync(pktPtr: number, packet: Packet): @promise@void@ var ff_copyin_packet = Module.ff_copyin_packet = function(pktPtr, packet) { + if (typeof packet === "number") { + // Input packet is an AVPacket pointer, duplicate it + av_packet_unref(pktPtr); + var res = av_packet_ref(pktPtr, packet); + if (res < 0) + throw new Error("Failed to reference packet: " + ff_error(res)); + av_packet_unref(packet); + av_packet_free_js(packet); + return; + } + ff_set_packet(pktPtr, packet.data); [ diff --git a/tests/tests/624-decode-and-filter-combined.js b/tests/tests/624-decode-and-filter-combined.js index 2b66c8d9..bd3607f9 100644 --- a/tests/tests/624-decode-and-filter-combined.js +++ b/tests/tests/624-decode-and-filter-combined.js @@ -31,7 +31,9 @@ const stream = streams[streamIdx]; const [, c, pkt, frame] = await libav.ff_init_decoder( "libopus", streams[streamIdx].codecpar); -const [res, packets] = await libav.ff_read_multi(fmt_ctx, pkt); +const [res, packets] = await libav.ff_read_multi(fmt_ctx, pkt, null, { + copyoutPacket: "ptr" +}); if (res !== libav.AVERROR_EOF) throw new Error("Failed to read packets"); await libav.avformat_close_input_js(fmt_ctx); @@ -39,6 +41,12 @@ await libav.avformat_close_input_js(fmt_ctx); if (!packets[streamIdx].length) throw new Error("No packets found for the appropriate stream"); +// Make sure it didn't actually copy +if (typeof packets[streamIdx][0] !== "number") + throw new Error("ff_copyout_packet_ptr was not used"); + +// FIXME: Discard other streams (MEMORY LEAK!) + // Get a filter graph that won't really do anything const [filter_graph, buffersrc_ctx, buffersink_ctx] = await libav.ff_init_filter_graph("volume=0.8,volume=1.25", {