Re: Recover h264parsed h264 stream dumped to disk

Nicolas Dufresne via gstreamer-devel <[email protected]>
Newsgroups gmane.comp.video.gstreamer.devel
Message-ID <[email protected]>
Hi,

Le dimanche 02 juillet 2023 à 23:40 -0400, Judemir Ribeiro via gstreamer-devel a
écrit :
> Hello,
> 
> I created a few videos using a wrong pipeline and now I dont know how to read
> the streams to recover them.
> 
> The pipeline was:
> 
> libcamera-vid -t 0 -n -o - | gst-launch1.0 fdsrc fd=0 ! h264parse config-
> interval=-1 ! tee name=output .output ! queue ! rtph264pay ! udpsink
> host=127.0.0.1 port=5600 .output ! queue ! filesink
> location=/video/$(date).h264 
> As we can see I created the tee after the h264parse step, when it should have
> been before. 
> 
> How can I convert these files back to h264 streams? I have tried ffmpeg, and
> other programs but nothing can recognize the files dumped by this wrong
> pipeline.

Looking at rtph264pay, AVC is placed before Byte Stream (the usual file format).
In short, I suspect that the file is made of AVCc NALs (size of nal header
instead of startcodes/0x000001). I simulated that, and its a bit hard to
recover. Some work on h264parse would be needed to make this work inside gst-
launch-1.0. Please find attached a python script to help with your task. Please
follow the procedure:

1. Find the original codec_data

For this, rerun the "libcamera-vid -t 0 -n -o - | gst-launch1.0 fdsrc..."
pipeline but add the -v option. Look for 
"codec_data=(buffer)<somebighex>" and same that. This is the stream headers,
they will always be the same for the same source/sensor/pipeline, but they are
not being saved in your erroneous pipeline.

2. Run the recovery script provided, a file called fixed.h264 will be created

python3 recover.py <avc_filename> <codec_data>

In my test, I produced a bad file this way:

gst-launch-1.0 videotestsrc num-buffers=1000 ! video/x-raw,format=I420 ! x264enc tune=zerolatency ! h264parse ! tee name=t t. ! queue ! rtph264pay ! fakesink t. ! queue ! filesink location=test.avc -v
python3 recover.py test.avc 01640014ffe1001d67640014acb20283f602d418181a94000003000400000300f23c50a92001000568ebccb22c

regards,
Nicolas

p.s. for those who want to know, but don't want to open the script, the recovery
pipeline is:

f"appsrc name=src block=true caps=\"video/x-h264,stream-
format=(string)avc,codec_data=(buffer){codec_data}\"" +
 " ! h264parse ! video/x-h264,stream-format=(string)byte-stream ! filesink
location=fixed.h264"


And the script just push the data using python API into appsrc. The difference
with that and filesrc, is that appsrc will push a caps event, which properly
signals AVCc, codec_data and no alignment to h264parse. If anyone have
suggestion on how to fix this, one way could be to introduce a caps properly to
filesrc, like udpsrc has.
recover.py (text/x-python3, 1.7 KB)
#!/usr/bin/env python3
import argparse
import gi
import os
import sys

gi.require_version('GLib', '2.0')
gi.require_version('Gst', '1.0')

from gi.repository import Gst, GLib

loop = GLib.MainLoop()
pipeline = None
segment_start_time = 0

def on_gst_message(bus, message):
    type = message.type
    if type == Gst.MessageType.EOS:
        print("Received EOS")
        loop.quit()
    elif type == Gst.MessageType.ERROR:
        print("Received ERROR: %s" % message.parse_error()[1])
        loop.quit()

# initialize GStreamer
Gst.init(sys.argv)

if len(sys.argv) != 3:
    print("usage: recover.py <filename> <codec_data>")
    exit()


# build the pipeline
filename=sys.argv[1]
codec_data=sys.argv[2]
pipeline = Gst.parse_launch(
    f"appsrc name=src block=true caps=\"video/x-h264,stream-format=(string)avc,codec_data=(buffer){codec_data}\"" +
    " ! h264parse ! video/x-h264,stream-format=(string)byte-stream ! filesink location=fixed.h264"
)

# connect to messages
bus = pipeline.get_bus()
bus.add_signal_watch()
bus.connect("message", on_gst_message)

# start playing
if pipeline.set_state(Gst.State.PLAYING) == Gst.StateChangeReturn.FAILURE:
    print("Error while starting the pipeline")
    exit()

print("Pipeline running")

appsrc = pipeline.get_by_name("src");
chunk_size = 4096 # 4 KiB
with open(filename, "rb") as in_file:
    while True:
        chunk = in_file.read(chunk_size)
        if chunk == b"":
            print("Done pushing data.")
            break # end of file
        appsrc.emit("push-buffer", Gst.Buffer.new_wrapped(chunk))

appsrc.emit("end-of-stream")

def send_eos():
    pipeline.send_event(Gst.Event.new_eos())
try:
    loop.run()
except KeyboardInterrupt:
    send_eos()
    loop.run()

pipeline.set_state(Gst.State.NULL)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.