PyAV-Org / PyAV

Pythonic bindings for FFmpeg's libraries.
https://pyav.basswood-io.com/
BSD 3-Clause "New" or "Revised" License
2.39k stars 354 forks source link

Help in remuxing without decoding #1424

Closed hmaarrfk closed 1 month ago

hmaarrfk commented 1 month ago

I would effectively like to slice a video along keyframes in a lossless way.

It seems that a first step would be to simply go through all the packets, without decoding them, and ensure that we can send them to an other similar file.

I think this should be straight forward (this little tutorial says as much at least https://www.bookstack.cn/read/ffmpeg-libav-tutorial/spilt.3.spilt.4.blank )

Overview

I would like to effectively copy the command:

ffmpeg -i ${HOME}/.pyav/datasets/pyav-curated/pexels/time-lapse-video-of-night-sky-857195.mp4' -frames:v 50 -c copy output.mp4

which should copy my contents, without decoding it re-encoding it. It should be doing this losslessly for nearly free.

Desired Behavior

ffprobe

should give me a meaningful output from my file generated in python

``` ffprobe -hide_banner -i new.mp4 [h264 @ 0x5d7b68075e40] No start code is found. Last message repeated 1 times [h264 @ 0x5d7b68075e40] missing picture in access unit with size 1 [h264 @ 0x5d7b68075e40] no frame! [h264 @ 0x5d7b68075e40] missing picture in access unit with size 1 [h264 @ 0x5d7b68075e40] no frame! [h264 @ 0x5d7b68075e40] missing picture in access unit with size 1 [h264 @ 0x5d7b68075e40] no frame! [h264 @ 0x5d7b68075e40] missing picture in access unit with size 1 [h264 @ 0x5d7b68075e40] no frame! [h264 @ 0x5d7b68075e40] missing picture in access unit with size 1 [h264 @ 0x5d7b68075e40] no frame! [h264 @ 0x5d7b68075e40] missing picture in access unit with size 1 [h264 @ 0x5d7b68075e40] no frame! [h264 @ 0x5d7b68075e40] missing picture in access unit with size 1 [...] [mov,mp4,m4a,3gp,3g2,mj2 @ 0x5d7b68074ec0] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none, 1280x720, 0 kb/s): unspecified pixel format Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'new.mp4': Metadata: major_brand : isom minor_version : 512 compatible_brands: isomiso2avc1mp41 encoder : Lavf60.16.100 Duration: 00:00:06.72, start: 0.000000, bitrate: 125 kb/s Stream #0:0[0x1](und): Video: h264 (avc1 / 0x31637661), none, 1280x720, 0 kb/s, 25 fps, 25 tbr, 12800 tbn (default) Metadata: handler_name : VideoHandler vendor_id : [0][0][0][0] [h264 @ 0x5d7b6807b6c0] No start code is found. ```

Example API

import av
import av.datasets
filename = av.datasets.curated("pexels/time-lapse-video-of-night-sky-857195.mp4")
new_filename = 'well_A1_copy.mp4'

container = av.open(str(filename))
stream = container.streams.video[0]

write_container = av.open("new.mp4", mode="w", format="mp4") 

write_stream = write_container.add_stream(
    stream.codec,
    rate=stream.average_rate
)
write_stream.pix_fmt = stream.pix_fmt
write_stream.width = stream.width
write_stream.height = stream.height
# We need to set the time base to something meaningful
# since mux_one will overwrite our time_base...
write_stream.time_base = stream.time_base
# write_container.flush_packets = True
# write_stream.time_base = stream.time_base
# Called in mux_one
# write_container.start_encoding()

for packet in container.demux(stream):
    packet.stream = write_stream
    # Setting the pos to None (or -1) should make the 
    # packet the "next" frame.
    # packet.pos = None
    write_container.mux_one(packet)
write_stream = None
write_container.close()

Additional context

my little patch to make pos writeable ```patch diff --git a/av/packet.pyx b/av/packet.pyx index 63b0b50..76e0bf0 100644 --- a/av/packet.pyx +++ b/av/packet.pyx @@ -156,6 +156,13 @@ cdef class Packet(Buffer): if self.ptr.pos != -1: return self.ptr.pos + @pos.setter + def pos(self, v): + if v is None: + self.ptr.pos = -1 + else: + self.ptr.pos = v + @property def size(self): """ ```

Thank you for your time!

hmaarrfk commented 1 month ago

I found the curated dataset from: https://github.com/PyAV-Org/PyAV/blob/5ab63db8c557cdd0bb48fd9a70e3ba6475954b70/examples/basics/remux.py#L4

but i didn't read the name of the file earlier....

man that worked.

template....

genius!