#include #include #include #include #include #include #include #include #include #include /* For setting the alpha IOCTL */ #define MXCFB_SET_GBL_ALPHA _IOW('F', 0x21, struct mxcfb_gbl_alpha) #define VID_PORT 5600 // The video port from app_streamer #define OOB_PORT 5551 // The oob data port /* Maximum length of an sprop string */ const int sprop_max_len = 256; GstElement *pipeline; GstElement *source; GstElement *depayloader; GstElement *decoder; GstElement *sink; /* Creates a pipeline of the form * udpsrc ! rtph264depay ! vpudec ! mfw_isink * and initializes all default parameters */ int create_pipeline(int port, char *sprop) { /* Build the pipeline */ pipeline = gst_pipeline_new("video_stream"); source = gst_element_factory_make("udpsrc", "vidsrc"); depayloader = gst_element_factory_make("rtph264depay", "depayloader"); decoder = gst_element_factory_make("vpudec", "decoder"); sink = gst_element_factory_make("mfw_isink", "sink"); if (!pipeline || !source || !depayloader || !decoder || !sink) { g_printerr("One element could not be created. Exiting.\n"); return -1; } /* Caps for the udpsrc. rtph264depay needs these */ GstCaps *caps = gst_caps_new_simple("application/x-rtp", "media", G_TYPE_STRING, "video", "clock-rate", G_TYPE_INT, 90000, "encoding-name", G_TYPE_STRING, "H264", "sprop-parameter-sets", G_TYPE_STRING, sprop, "payload", G_TYPE_INT, 96, NULL); g_object_set(source, "caps", caps, NULL); gst_caps_unref(caps); /* Set the udpsrc port */ g_object_set(G_OBJECT(source), "port", port, NULL); /* For non-choppy video output */ g_object_set(G_OBJECT(decoder), "low-latency", 1, NULL); /* we add all elements into the pipeline */ gst_bin_add_many(GST_BIN(pipeline), source, depayloader, decoder, sink, NULL); /* we link the elements together */ gst_element_link_many(source, depayloader, decoder, sink, NULL); return 0; } /* destroys the pipeline when shutting down */ int destroy_pipeline(void) { gst_element_set_state(pipeline, GST_STATE_NULL); g_object_unref(pipeline); return 0; } /* Restarts the pipeline without destroying it. Accepts * a new sprop parameter as an arguement. This assumes * the pipeline needs to be restarted but not destroyed when * a resolution change occurs to the pipe */ int restart_pipeline(char *sprop) { gst_element_set_state(pipeline, GST_STATE_NULL); /* Caps for the udpsrc. rtph264depay needs these */ GstCaps *caps = gst_caps_new_simple("application/x-rtp", "media", G_TYPE_STRING, "video", "clock-rate", G_TYPE_INT, 90000, "encoding-name", G_TYPE_STRING, "H264", "sprop-parameter-sets", G_TYPE_STRING, sprop, "payload", G_TYPE_INT, 96, NULL); g_object_set(source, "caps", caps, NULL); gst_caps_unref(caps); gst_element_set_state(pipeline, GST_STATE_READY); gst_element_set_state(pipeline, GST_STATE_PLAYING); return 0; } /* Checks the status of the HDMI connection to * determine if we should start or shut down the * pipeline */ int hdmi_connected(void) { char buf[64]; memset(buf, 0, 64); // Read the cable_state from the sysfs int fd = open("/sys/devices/soc0/soc.1/20e0000.hdmi_video/cable_state", O_RDONLY | O_NONBLOCK, 0); if (fd < 0) { syslog(LOG_ERR, "Unable to open cable state file"); // By default assume its connected goto return_connected; } int len = 0; len = read(fd, buf, 64); if (len <= 0) { syslog(LOG_ERR, "Unable to read cable state file"); close(fd); goto return_connected; } // Strip the tailing return buf[len - 1] = 0; if (!strcmp(buf, "plugout")) { close(fd); return 0; } else close(fd); return_connected: return 1; } /* For handling ctrl-c */ void int_handler(int sig) { destroy_pipeline(); exit(0); } /* For setting the vsalpha. This eliminates the need for * a VSALPHA setting in the environment variables, which * did not always seem to work */ int set_alpha(void) { int fd; struct mxcfb_gbl_alpha { int enable; int alpha; } g_alpha; g_alpha.alpha = 0; // alpha value g_alpha.enable = 1; if ((fd = open("/dev/fb0", O_RDWR)) < 0) { syslog(LOG_ERR, "Unable to open frame buffer 0"); return -1; } if (ioctl(fd, MXCFB_SET_GBL_ALPHA, &g_alpha) < 0) { syslog(LOG_ERR, "Set global alpha failed"); close(fd); return -1; } close(fd); return 0; } /* Open the out-of-band socket for sending and * receiving any out-of-band data, such as a new * resolution or an error count. */ int open_socket(void) { int fd; struct sockaddr_in addr; int tos = 0xFF; /* create a UDP socket */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1; /* Bind the socket to any IP, OOB_PORT */ memset((char *)&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(OOB_PORT); if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) return -1; /* Set the socket to VI priority */ if (setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) return -1; fcntl(fd, F_SETFL, O_NONBLOCK); // set to non-blocking return fd; } /* Looks for data on the OOB port. If available, it * returns the width, height and framerate of the * stream. */ int oob_receive_sprop(int fd, char *sprop) { struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); char buf[sprop_max_len]; memset(buf, 0, sprop_max_len); int recvlen = recvfrom(fd, buf, sprop_max_len, 0, (struct sockaddr *)&addr, &addrlen); if (recvlen > 0) { /* Sanity check that we got a null terminated string */ if (buf[sprop_max_len - 1] != 0) { syslog(LOG_INFO, "Got a bad sprop string, ignoring"); return 0; } memset(sprop, 0, sprop_max_len); strcpy(sprop, buf); } return recvlen; } /* The main entry point. Arguments are currently only * for gstreamer */ int main(int argc, char *argv[]) { char sprop[sprop_max_len]; char last_sprop[sprop_max_len]; memset(sprop, 0, sprop_max_len); memset(last_sprop, 0, sprop_max_len); int oob_sock_fd; /* sig handler to shut down the pipeline */ signal(SIGINT, int_handler); openlog("hdmi", LOG_NDELAY, LOG_LOCAL3); /* Set the alphablending off for isink */ set_alpha(); /* Sit here and wait until the HDMI is connected */ syslog(LOG_INFO, "Waiting for an HDMI connection"); while (!hdmi_connected()) sleep(1); /* Initialize GStreamer */ syslog(LOG_INFO, "Initializing HDMI pipeline"); gst_init(&argc, &argv); /* Open the oob socket */ oob_sock_fd = open_socket(); if (oob_sock_fd <= 0) { syslog(LOG_ERR, "Unable to open OOB socket"); return -1; } /* Wait for some OOB data */ while (1) { if (oob_receive_sprop(oob_sock_fd, sprop) > 0) { syslog(LOG_INFO, "Received sprop"); break; } sleep(1); } memcpy(last_sprop, sprop, sprop_max_len); /* Create the pipeline */ syslog(LOG_INFO, "Creating HDMI pipeline"); if (create_pipeline(VID_PORT, sprop) < 0) return -1; /* Start playing */ syslog(LOG_INFO, "Starting HDMI playing"); gst_element_set_state(pipeline, GST_STATE_PLAYING); // Check the incoming frame size while (1) { // Check the HDMI connection state if (!hdmi_connected()) { break; // Exit and let inittab restart us } // Look for new data on the OOB port if (oob_receive_sprop(oob_sock_fd, sprop) > 0) { /* If this is a new sprop, restart the pipeline. * We are guaranteed to have a null terminated string * from the oob_receive_sprop call */ if (strcmp(sprop, last_sprop) != 0) { syslog(LOG_INFO, "Received new sprop: %s", sprop); /* Restart the pipeline */ restart_pipeline(sprop); memset(last_sprop, 0, sizeof(last_sprop)); strcpy(last_sprop, sprop); } } usleep(100000); } /* Free resources */ destroy_pipeline(); close(oob_sock_fd); return 0; }