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)