Skip to content

Commit

Permalink
Adding the equivalent of ff_copyout_frame_ptr for packets, ff_copyout…
Browse files Browse the repository at this point in the history
…_packet_ptr
  • Loading branch information
Yahweasel committed Oct 18, 2023
1 parent 3c24529 commit d30bdd0
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 8 deletions.
24 changes: 22 additions & 2 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<number, Packet[]>]>
```
Expand Down Expand Up @@ -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<Packet>
```

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`
```
Expand Down
4 changes: 4 additions & 0 deletions funcs.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"]],
Expand Down Expand Up @@ -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",
Expand Down
43 changes: 38 additions & 5 deletions post.in.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<number, Packet[]>]@
*/
Expand All @@ -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
Expand All @@ -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];

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1917,13 +1921,42 @@ 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
* @param packet Packet to copy in.
*/
/// @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);

[
Expand Down
10 changes: 9 additions & 1 deletion tests/tests/624-decode-and-filter-combined.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,22 @@ 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);

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", {
Expand Down

0 comments on commit d30bdd0

Please sign in to comment.