Hi,
I’m trying to create a program that generates a video clip from a bigger mp4 file. It works by decoding and encoding, but I wonder whether it is possible to just copy the video stream from a certain key frame.
In gstreamer, seeking to a specific key frame can be done with the KEY_UNIT flag. If it works, the process can be much faster. The demo program is like this (main.c):
#include <gst/gst.h>
GMainLoop* loop = NULL;
GstElement* pipeline = NULL;
void print_gst_message(GstMessage* msg)
{
GError* err = NULL;
gchar* debug_info = NULL;
if (msg->type == GST_MESSAGE_ERROR) {
gst_message_parse_error(msg, &err, &debug_info);
g_printerr("Error: [%s] %s\n", msg->src->name, err->message);
if (debug_info) {
g_printerr("Error: [%s] - %s", msg->src->name, debug_info);
}
}
else if (msg->type == GST_MESSAGE_WARNING) {
gst_message_parse_warning(msg, &err, &debug_info);
g_printerr("Warn: [%s] %s\n", msg->src->name, err->message);
if (debug_info) {
g_printerr("Warn: [%s] - %s", msg->src->name, debug_info);
}
}
else if (msg->type == GST_MESSAGE_INFO) {
gst_message_parse_info(msg, &err, &debug_info);
g_printerr("Info: [%s] %s\n", msg->src->name, err->message);
if (debug_info) {
g_printerr("Info: [%s] - %s", msg->src->name, debug_info);
}
}
g_clear_error(&err);
g_free(debug_info);
}
gboolean gst_bus_handler(GstBus* bus, GstMessage* message, gpointer data)
{
GstMessageType type = message->type;
if (type == GST_MESSAGE_CLOCK_LOST) {
gst_element_set_state(pipeline, GST_STATE_PAUSED);
gst_element_set_state(pipeline, GST_STATE_PLAYING);
}
else if (type == GST_MESSAGE_EOS) {
g_main_loop_quit(loop);
}
else if (type == GST_MESSAGE_INFO) {
print_gst_message(message);
}
else if (type == GST_MESSAGE_WARNING) {
print_gst_message(message);
}
else if (type == GST_MESSAGE_ERROR) {
print_gst_message(message);
}
else if (type == GST_MESSAGE_LATENCY) {
gst_bin_recalculate_latency(GST_BIN(pipeline));
}
return TRUE;
}
int main(int argc, char* argv[])
{
gst_init(NULL, NULL);
// create the pipeline
// - filesrc ---> demux ---> h264parse ---> mp4mux --> filesink
pipeline = gst_parse_launch(
"filesrc location=output.mp4 ! qtdemux name=demux"
" demux.video_0 ! h264parse ! mux.video_0"
" mp4mux name=mux ! filesink location=clip.mp4", NULL);
if (!pipeline) {
return 1;
}
// create the main loop
loop = g_main_loop_new(NULL, FALSE);
// set gst bus handler
GstBus* bus = gst_element_get_bus(pipeline);
guint watch_id = gst_bus_add_watch(bus, gst_bus_handler, NULL);
gst_object_unref(bus);
// pause
GstStateChangeReturn ret = gst_element_set_state(pipeline, GST_STATE_PAUSED);
if (ret == GST_STATE_CHANGE_FAILURE) {
g_printerr("Unable to set the pipeline to the PAUSED state\n");
goto end;
}
// seek
#if 1
if (!gst_element_seek(
pipeline, 1.0, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT),
GST_SEEK_TYPE_SET, 30 * GST_SECOND,
GST_SEEK_TYPE_SET, 90 * GST_SECOND)) {
g_printerr("Seek failed\n");
goto end;
}
#endif
// play
ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
g_printerr("Unable to set the pipeline to the PLAYING state\n");
}
else {
g_printerr("Entering main loop\n");
g_main_loop_run(loop);
}
end:
g_printerr("Setting pipeline state to NULL\n");
gst_element_set_state(pipeline, GST_STATE_NULL);
g_source_remove(watch_id);
g_main_loop_unref(loop);
g_printerr("Freeing pipeline\n");
gst_object_unref(pipeline);
gst_deinit();
return 0;
}
I find that if gst_element_seek() is disabled, the pipeline can generate a new file successfully with full range.
If gst_element_seek() is executed, it throws an exception: “GStreamer-CRITICAL **: gst_segment_do_seek: assertion ‘segment->format == format’ failed”.
Is there something wrong with the code?