2017-03-20 1 views
1

J'ai écrit un code pour l'affichage et l'enregistrement de v4l2src en même temps. Mon pipeline ressemble:Envoi d'EoS à un fichier pendant la suppression d'une branche du tee

   /[queue] ! [videosink] 
v4l2src ! tee ! 
       \ [queue] ! [filesink] 

Actuellement, je suis en mesure d'afficher + disque ensemble, et aussi dynamiquement démarrer et arrêter la branche d'enregistrement à volonté (en utilisant ctrl + c gestionnaire Sigint pour le démarrage/arrêt). J'ai utilisé le conseil @thiagoss dans this réponse, et des parties de this article.

Question:

Le seul problème que je suis confronté envoie EoS à filesink branche sur Dissocier. A quel élément dois-je envoyer l'événement gst_element_send_event(-->?<--, gst_event_new_eos());? Je ne peux pas l'envoyer à l'ensemble du pipeline parce que 1. la branche est maintenant non liée et 2. même si je le fais, il fermera le videosink. Ce que j'ai essayé: En supprimant mp4mux et en enregistrant simplement la vidéo codée en h264, je peux utiliser gst-playbin pour voir la vidéo, ce qui signifie que la création et la dissociation de la branche se déroulent correctement.

Ce qui suit est mon code.

#include <string.h> 
#include <gst/gst.h> 
#include <signal.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 

// v4l2src ! tee name=t t. ! x264enc ! mp4mux ! filesink location=/home/rish/Desktop/okay.264 t. ! videoconvert ! autovideosink 

static GMainLoop *loop; 
static GstElement *pipeline, *src, *tee, *encoder, *muxer, *filesink, *videoconvert, *videosink, *queue_record, *queue_display; 
static GstBus *bus; 
static GstPad *teepad; 
static gboolean recording = FALSE; 
static gint counter = 0; 

static gboolean 
message_cb (GstBus * bus, GstMessage * message, gpointer user_data) 
{ 
switch (GST_MESSAGE_TYPE (message)) { 
case GST_MESSAGE_ERROR:{ 
    GError *err = NULL; 
    gchar *name, *debug = NULL; 

    name = gst_object_get_path_string (message->src); 
    gst_message_parse_error (message, &err, &debug); 

    g_printerr ("ERROR: from element %s: %s\n", name, err->message); 
    if (debug != NULL) 
    g_printerr ("Additional debug info:\n%s\n", debug); 

    g_error_free (err); 
    g_free (debug); 
    g_free (name); 

    g_main_loop_quit (loop); 
    break; 
} 
case GST_MESSAGE_WARNING:{ 
    GError *err = NULL; 
    gchar *name, *debug = NULL; 

    name = gst_object_get_path_string (message->src); 
    gst_message_parse_warning (message, &err, &debug); 

    g_printerr ("ERROR: from element %s: %s\n", name, err->message); 
    if (debug != NULL) 
    g_printerr ("Additional debug info:\n%s\n", debug); 

    g_error_free (err); 
    g_free (debug); 
    g_free (name); 
    break; 
} 
case GST_MESSAGE_EOS:{ 
    g_print ("Got EOS\n"); 
    g_main_loop_quit (loop); 
    gst_element_set_state (pipeline, GST_STATE_NULL); 
    g_main_loop_unref (loop); 
    gst_object_unref (pipeline); 
    exit(0); 
    break; 
} 
default: 
    break; 
} 

return TRUE; 
} 

static GstPadProbeReturn unlink_cb(GstPad *pad, GstPadProbeInfo *info, gpointer user_data) { 
g_print("Unlinking..."); 
GstPad *sinkpad; 
sinkpad = gst_element_get_static_pad (queue_record, "sink"); 
gst_pad_unlink (teepad, sinkpad); 
gst_object_unref (sinkpad); 

gst_element_send_event(filesink, gst_event_new_eos()); 

sleep(1); 
gst_bin_remove(GST_BIN (pipeline), queue_record); 
gst_bin_remove(GST_BIN (pipeline), encoder); 
// gst_bin_remove(GST_BIN (pipeline), muxer); 
gst_bin_remove(GST_BIN (pipeline), filesink); 

gst_element_set_state(queue_record, GST_STATE_NULL); 
gst_element_set_state(encoder, GST_STATE_NULL); 
// gst_element_set_state(muxer, GST_STATE_NULL); 
gst_element_set_state(filesink, GST_STATE_NULL); 

gst_object_unref(queue_record); 
gst_object_unref(encoder); 
// gst_object_unref(muxer); 
gst_object_unref(filesink); 

gst_element_release_request_pad (tee, teepad); 
gst_object_unref (teepad); 

g_print("Unlinked\n"); 

return GST_PAD_PROBE_REMOVE; 
} 

void stopRecording() { 
    g_print("stopRecording\n"); 
    gst_pad_add_probe(teepad, GST_PAD_PROBE_TYPE_IDLE, unlink_cb, NULL, (GDestroyNotify) g_free); 
    recording = FALSE; 
} 

void startRecording() { 
g_print("startRecording\n"); 
GstPad *sinkpad; 
GstPadTemplate *templ; 

templ = gst_element_class_get_pad_template(GST_ELEMENT_GET_CLASS(tee), "src_%u"); 
teepad = gst_element_request_pad(tee, templ, NULL, NULL); 
queue_record = gst_element_factory_make("queue", "queue_record"); 
encoder = gst_element_factory_make("x264enc", NULL); 
// muxer = gst_element_factory_make("mp4mux", NULL); 
filesink = gst_element_factory_make("filesink", NULL); 
char *file_name = (char*) malloc(100*sizeof(char)); 
sprintf(file_name, "/home/rish/Desktop/rec%d.mp4", counter++); 
g_print(file_name); 
g_object_set(filesink, "location", file_name, NULL); 
g_object_set(encoder, "tune", 4, NULL); 
free(file_name); 

gst_bin_add_many(GST_BIN(pipeline), gst_object_ref(queue_record), gst_object_ref(encoder), gst_object_ref(filesink), NULL); 
gst_element_link_many(queue_record, encoder, filesink, NULL); 

gst_element_sync_state_with_parent(queue_record); 
gst_element_sync_state_with_parent(encoder); 
// gst_element_sync_state_with_parent(muxer); 
gst_element_sync_state_with_parent(filesink); 

sinkpad = gst_element_get_static_pad(queue_record, "sink"); 
gst_pad_link(teepad, sinkpad); 
gst_object_unref(sinkpad); 

recording = TRUE; 
} 

int sigintHandler(int unused) { 
g_print("You ctrl-c!\n"); 
if (recording) 
    stopRecording(); 
else 
    startRecording(); 
return 0; 
} 

int main(int argc, char *argv[]) 
{ 
signal(SIGINT, sigintHandler); 
gst_init (&argc, &argv); 

pipeline = gst_pipeline_new(NULL); 
src = gst_element_factory_make("v4l2src", NULL); 
tee = gst_element_factory_make("tee", "tee"); 
queue_display = gst_element_factory_make("queue", "queue_display"); 
videoconvert = gst_element_factory_make("videoconvert", NULL); 
videosink = gst_element_factory_make("autovideosink", NULL); 

if (!pipeline || !src || !tee || !videoconvert || !videosink || !queue_display) { 
    g_error("Failed to create elements"); 
    return -1; 
} 

gst_bin_add_many(GST_BIN(pipeline), src, tee, queue_display, videoconvert, videosink, NULL); 
if (!gst_element_link_many(src, tee, NULL) 
    || !gst_element_link_many(tee, queue_display, videoconvert, videosink, NULL)) { 
    g_error("Failed to link elements"); 
    return -2; 
} 

startRecording(); 
loop = g_main_loop_new(NULL, FALSE); 

bus = gst_pipeline_get_bus(GST_PIPELINE (pipeline)); 
gst_bus_add_signal_watch(bus); 
g_signal_connect(G_OBJECT(bus), "message", G_CALLBACK(message_cb), NULL); 
gst_object_unref(GST_OBJECT(bus)); 

gst_element_set_state(pipeline, GST_STATE_PLAYING); 

g_print("Starting loop\n"); 
g_main_loop_run(loop); 

return 0; 
} 

Répondre

3

Vous devez l'envoyer aux codeurs, afin qu'ils puissent bien finaliser leur travail et ils transmettront au muxer qui doit également conclure le dossier par écrit les parties des en-têtes qu'ils ne peuvent écrire à la fin.

En outre, supprimez cette veille et vous devrez vous assurer que le système d'archivage a publié un message EOS pour s'assurer qu'il a bien été traité et qu'il est sécuritaire de le supprimer. Vous devrez peut-être activer message-forward pour le pipeline, sinon il contiendra les messages EOS jusqu'à ce que tous les puits affichent leur EOS (ce qui ne se produit pas dans votre cas à cause du lien vidéo).

+0

Merci beaucoup pour ce @thiagoss! L'envoi de l'EOS à l'encodeur enregistre le fichier correctement. Oui, le sommeil() était un hack - ne savait pas comment obtenir le rappel EOS pour cette branche particulière. Se penchera sur 'message-forward'. Merci encore! –

0

J'ai un système de fichiers avec des encodeurs dans une corbeille séparée avec handle_message (How to override handle_message in GstBin?) ignoré pour ignorer EOS et le transformer en un message personnalisé.

L'EOS initial est envoyé au Ghostpad de ce bin. Ce pad est connecté à l'encodeur (ce bin contient l'encodeur, muxer et filesink).