/* Thread */ void *janus_pfunix_thread(void *data) { JANUS_LOG(LOG_INFO, "Unix Sockets thread started\n"); int fds = 0; struct pollfd poll_fds[1024]; /* FIXME Should we allow for more clients? */ char buffer[BUFFER_SIZE]; struct iovec iov[1]; struct msghdr msg; memset(&msg, 0, sizeof(msg)); memset(iov, 0, sizeof(iov)); iov[0].iov_base = buffer; iov[0].iov_len = sizeof(buffer); msg.msg_iov = iov; msg.msg_iovlen = 1; while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) { /* Prepare poll list of file descriptors */ fds = 0; /* Writeable monitor */ poll_fds[fds].fd = write_fd[0]; poll_fds[fds].events = POLLIN; fds++; if(pfd > -1) { /* Janus API */ poll_fds[fds].fd = pfd; poll_fds[fds].events = POLLIN; fds++; } if(admin_pfd > -1) { /* Admin API */ poll_fds[fds].fd = admin_pfd; poll_fds[fds].events = POLLIN; fds++; } /* Iterate on available clients, to see if we need to POLLIN or POLLOUT too */ janus_mutex_lock(&clients_mutex); GHashTableIter iter; gpointer value; g_hash_table_iter_init(&iter, clients_by_fd); while(g_hash_table_iter_next(&iter, NULL, &value)) { janus_pfunix_client *client = value; if(client->fd > -1) { poll_fds[fds].fd = client->fd; poll_fds[fds].events = g_async_queue_length(client->messages) > 0 ? POLLIN | POLLOUT : POLLIN; fds++; } } janus_mutex_unlock(&clients_mutex); /* Start polling */ int res = poll(poll_fds, fds, -1); if(res == 0) continue; if(res < 0) { JANUS_LOG(LOG_ERR, "poll() failed\n"); break; } int i = 0; for(i=0; i<fds; i++) { if(poll_fds[i].revents & (POLLERR | POLLHUP)) { /* Socket error? Shall we do something? */ if(poll_fds[i].fd == write_fd[0]) { /* Error in the wake-up socketpair, that sucks: try recreating it */ JANUS_LOG(LOG_WARN, "Error polling wake-up socketpair: %s...\n", poll_fds[i].revents & POLLERR ? "POLLERR" : "POLLHUP"); close(write_fd[0]); write_fd[0] = -1; close(write_fd[1]); write_fd[1] = -1; if(socketpair(PF_LOCAL, SOCK_STREAM, 0, write_fd) < 0) { JANUS_LOG(LOG_FATAL, "Error creating socket pair for writeable events: %d, %s\n", errno, strerror(errno)); continue; } } else if(poll_fds[i].fd == pfd) { /* Error in the Janus API socket */ JANUS_LOG(LOG_WARN, "Error polling Unix Sockets Janus API interface (%s), disabling it\n", poll_fds[i].revents & POLLERR ? "POLLERR" : "POLLHUP"); close(pfd); pfd = -1; continue; } else if(poll_fds[i].fd == admin_pfd) { /* Error in the Admin API socket */ JANUS_LOG(LOG_WARN, "Error polling Unix Sockets Admin API interface (%s), disabling it\n", poll_fds[i].revents & POLLERR ? "POLLERR" : "POLLHUP"); close(admin_pfd); admin_pfd = -1; continue; } else { /* Error in a client socket, find and remove it */ janus_mutex_lock(&clients_mutex); janus_pfunix_client *client = g_hash_table_lookup(clients_by_fd, GINT_TO_POINTER(poll_fds[i].fd)); if(client == NULL) { /* We're not handling this, ignore */ continue; } JANUS_LOG(LOG_INFO, "Unix Sockets client disconnected (%d)\n", poll_fds[i].fd); /* Notify core */ gateway->transport_gone(&janus_pfunix_transport, client); /* Close socket */ shutdown(SHUT_RDWR, poll_fds[i].fd); close(poll_fds[i].fd); client->fd = -1; /* Destroy the client */ g_hash_table_remove(clients_by_fd, GINT_TO_POINTER(poll_fds[i].fd)); g_hash_table_remove(clients, client); if(client->messages != NULL) { char *response = NULL; while((response = g_async_queue_try_pop(client->messages)) != NULL) { g_free(response); } g_async_queue_unref(client->messages); } g_free(client); janus_mutex_unlock(&clients_mutex); continue; } continue; } if(poll_fds[i].revents & POLLOUT) { /* Find the client from its file descriptor */ janus_mutex_lock(&clients_mutex); janus_pfunix_client *client = g_hash_table_lookup(clients_by_fd, GINT_TO_POINTER(poll_fds[i].fd)); if(client != NULL) { char *payload = NULL; while((payload = g_async_queue_try_pop(client->messages)) != NULL) { int res = 0; do { res = write(client->fd, payload, strlen(payload)); } while(res == -1 && errno == EINTR); /* FIXME Should we check if sent everything? */ JANUS_LOG(LOG_HUGE, "Written %d/%zu bytes on %d\n", res, strlen(payload), client->fd); g_free(payload); } } janus_mutex_unlock(&clients_mutex); } if(poll_fds[i].revents & POLLIN) { if(poll_fds[i].fd == write_fd[0]) { /* Read and ignore: we use this to unlock the poll if there's data to write */ res = read(poll_fds[i].fd, buffer, BUFFER_SIZE); } else if(poll_fds[i].fd == pfd || poll_fds[i].fd == admin_pfd) { /* Janus/Admin API: accept the new client (SOCK_SEQPACKET) or receive data (SOCK_DGRAM) */ struct sockaddr_un address; socklen_t addrlen = sizeof(address); if((poll_fds[i].fd == pfd && !dgram) || (poll_fds[i].fd == admin_pfd && !admin_dgram)) { /* SOCK_SEQPACKET */ int cfd = accept(poll_fds[i].fd, (struct sockaddr *) &address, &addrlen); if(cfd > -1) { JANUS_LOG(LOG_INFO, "Got new Unix Sockets %s API client: %d\n", poll_fds[i].fd == pfd ? "Janus" : "Admin", cfd); /* Allocate new client */ janus_pfunix_client *client = g_malloc0(sizeof(janus_pfunix_client)); client->fd = cfd; client->admin = (poll_fds[i].fd == admin_pfd); /* API client type */ client->messages = g_async_queue_new(); client->session_timeout = FALSE; /* Take note of this new client */ janus_mutex_lock(&clients_mutex); g_hash_table_insert(clients_by_fd, GINT_TO_POINTER(cfd), client); g_hash_table_insert(clients, client, client); janus_mutex_unlock(&clients_mutex); } } else { /* SOCK_DGRAM */ struct sockaddr_storage address; res = recvfrom(poll_fds[i].fd, buffer, sizeof(buffer), 0, (struct sockaddr *)&address, &addrlen); if(res < 0) { if(errno != EAGAIN && errno != EWOULDBLOCK) { JANUS_LOG(LOG_ERR, "Error reading from client (%s API)...\n", poll_fds[i].fd == pfd ? "Janus" : "Admin"); } continue; } buffer[res] = '\0'; /* Is this a new client, or one we knew about already? */ struct sockaddr_un *uaddr = (struct sockaddr_un *)&address; if(strlen(uaddr->sun_path) == 0) { /* No path provided, drop the packet */ JANUS_LOG(LOG_WARN, "Dropping packet from unknown source (no path provided)\n"); continue; } janus_mutex_lock(&clients_mutex); janus_pfunix_client *client = g_hash_table_lookup(clients_by_path, uaddr->sun_path); if(client == NULL) { JANUS_LOG(LOG_INFO, "Got new Unix Sockets %s API client: %s\n", poll_fds[i].fd == pfd ? "Janus" : "Admin", uaddr->sun_path); /* Allocate new client */ client = g_malloc0(sizeof(janus_pfunix_client)); client->fd = -1; memcpy(&client->addr, uaddr, sizeof(struct sockaddr_un)); client->admin = (poll_fds[i].fd == admin_pfd); /* API client type */ client->messages = g_async_queue_new(); client->session_timeout = FALSE; /* Take note of this new client */ g_hash_table_insert(clients_by_path, uaddr->sun_path, client); g_hash_table_insert(clients, client, client); } janus_mutex_unlock(&clients_mutex); JANUS_LOG(LOG_VERB, "Message from client %s (%d bytes)\n", uaddr->sun_path, res); JANUS_LOG(LOG_HUGE, "%s\n", buffer); /* Parse the JSON payload */ json_error_t error; json_t *root = json_loads(buffer, 0, &error); /* Notify the core, passing both the object and, since it may be needed, the error */ gateway->incoming_request(&janus_pfunix_transport, client, NULL, client->admin, root, &error); } } else { /* Client data: receive message */ iov[0].iov_len = sizeof(buffer); res = recvmsg(poll_fds[i].fd, &msg, MSG_WAITALL); if(res < 0) { if(errno != EAGAIN && errno != EWOULDBLOCK) { JANUS_LOG(LOG_ERR, "Error reading from client %d...\n", poll_fds[i].fd); } continue; } if(msg.msg_flags & MSG_TRUNC) { /* Apparently our buffer is not large enough? */ JANUS_LOG(LOG_WARN, "Incoming message from client %d truncated (%d bytes), dropping it...\n", poll_fds[i].fd, res); continue; } /* Find the client from its file descriptor */ janus_mutex_lock(&clients_mutex); janus_pfunix_client *client = g_hash_table_lookup(clients_by_fd, GINT_TO_POINTER(poll_fds[i].fd)); if(client == NULL) { janus_mutex_unlock(&clients_mutex); JANUS_LOG(LOG_WARN, "Got data from unknown Unix Sockets client %d, closing connection...\n", poll_fds[i].fd); /* Close socket */ shutdown(SHUT_RDWR, poll_fds[i].fd); close(poll_fds[i].fd); continue; } if(res == 0) { JANUS_LOG(LOG_INFO, "Unix Sockets client disconnected (%d)\n", poll_fds[i].fd); /* Notify core */ gateway->transport_gone(&janus_pfunix_transport, client); /* Close socket */ shutdown(SHUT_RDWR, poll_fds[i].fd); close(poll_fds[i].fd); client->fd = -1; /* Destroy the client */ g_hash_table_remove(clients_by_fd, GINT_TO_POINTER(poll_fds[i].fd)); g_hash_table_remove(clients, client); if(client->messages != NULL) { char *response = NULL; while((response = g_async_queue_try_pop(client->messages)) != NULL) { g_free(response); } g_async_queue_unref(client->messages); } g_free(client); janus_mutex_unlock(&clients_mutex); continue; } janus_mutex_unlock(&clients_mutex); /* If we got here, there's data to handle */ buffer[res] = '\0'; JANUS_LOG(LOG_VERB, "Message from client %d (%d bytes)\n", poll_fds[i].fd, res); JANUS_LOG(LOG_HUGE, "%s\n", buffer); /* Parse the JSON payload */ json_error_t error; json_t *root = json_loads(buffer, 0, &error); /* Notify the core, passing both the object and, since it may be needed, the error */ gateway->incoming_request(&janus_pfunix_transport, client, NULL, client->admin, root, &error); } } } } if(pfd > -1) close(pfd); pfd = -1; if(admin_pfd > -1) close(admin_pfd); admin_pfd = -1; g_hash_table_destroy(clients_by_path); g_hash_table_destroy(clients_by_fd); g_hash_table_destroy(clients); /* Done */ JANUS_LOG(LOG_INFO, "Unix Sockets thread ended\n"); return NULL; }
void* ags_thread_pool_creation_thread(void *ptr) { AgsThreadPool *thread_pool; AgsThread *thread; GList *tmplist; guint n_threads, max_threads; guint i, i_stop; thread_pool = AGS_THREAD_POOL(ptr); #ifdef AGS_DEBUG g_message("ags_thread_pool_creation_thread\0"); #endif while((AGS_THREAD_POOL_RUNNING & (g_atomic_int_get(&(thread_pool->flags)))) != 0){ #ifdef AGS_DEBUG g_message("ags_thread_pool_creation_thread@loopStart\0"); #endif pthread_mutex_lock(thread_pool->creation_mutex); g_atomic_int_or(&(thread_pool->flags), AGS_THREAD_POOL_READY); while(g_atomic_int_get(&(thread_pool->newly_pulled)) == 0){ pthread_cond_wait(thread_pool->creation_cond, thread_pool->creation_mutex); } n_threads = g_atomic_int_get(&(thread_pool->n_threads)); max_threads = g_atomic_int_get(&(thread_pool->max_threads)); i_stop = g_atomic_int_get(&(thread_pool->newly_pulled)); g_atomic_int_set(&(thread_pool->newly_pulled), 0); #ifdef AGS_DEBUG g_message("ags_thread_pool_creation_thread@loop0\0"); #endif g_atomic_int_and(&(thread_pool->flags), (~AGS_THREAD_POOL_READY)); if(n_threads < max_threads){ for(i = 0; i < i_stop && n_threads < max_threads; i++){ thread = (AgsThread *) ags_returnable_thread_new(thread_pool); tmplist = g_atomic_pointer_get(&(thread_pool->returnable_thread)); g_atomic_pointer_set(&(thread_pool->returnable_thread), g_list_prepend(tmplist, thread)); ags_thread_add_child(AGS_THREAD(thread_pool->parent), thread); ags_connectable_connect(AGS_CONNECTABLE(thread)); g_atomic_int_inc(&(thread_pool->n_threads)); n_threads++; } } pthread_mutex_unlock(thread_pool->creation_mutex); #ifdef AGS_DEBUG g_message("ags_thread_pool_creation_thread@loopEND\0"); #endif } }
/** * gst_buffer_pool_set_config: * @pool: a #GstBufferPool * @config: (transfer full): a #GstStructure * * Set the configuration of the pool. If the pool is already configured, and * the configuration haven't change, this function will return %TRUE. If the * pool is active, this function will try deactivating it. Buffers allocated * form this pool must be returned or else this function will do nothing and * return %FALSE. * * @config is a #GstStructure that contains the configuration parameters for * the pool. A default and mandatory set of parameters can be configured with * gst_buffer_pool_config_set_params(), gst_buffer_pool_config_set_allocator() * and gst_buffer_pool_config_add_option(). * * If the parameters in @config can not be set exactly, this function returns * %FALSE and will try to update as much state as possible. The new state can * then be retrieved and refined with gst_buffer_pool_get_config(). * * This function takes ownership of @config. * * Returns: %TRUE when the configuration could be set. */ gboolean gst_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config) { gboolean result; GstBufferPoolClass *pclass; GstBufferPoolPrivate *priv; g_return_val_if_fail (GST_IS_BUFFER_POOL (pool), FALSE); g_return_val_if_fail (config != NULL, FALSE); priv = pool->priv; GST_BUFFER_POOL_LOCK (pool); /* nothing to do if config is unchanged */ if (priv->configured && gst_structure_is_equal (config, priv->config)) goto config_unchanged; /* can't change the settings when active */ if (priv->active) { GST_BUFFER_POOL_UNLOCK (pool); if (!gst_buffer_pool_set_active (pool, FALSE)) { GST_BUFFER_POOL_LOCK (pool); goto was_active; } GST_BUFFER_POOL_LOCK (pool); /* not likely but as we released the lock */ if (priv->active) goto was_active; } /* we can't change when outstanding buffers */ if (g_atomic_int_get (&priv->outstanding) != 0) goto have_outstanding; pclass = GST_BUFFER_POOL_GET_CLASS (pool); /* set the new config */ if (G_LIKELY (pclass->set_config)) result = pclass->set_config (pool, config); else result = FALSE; /* save the config regardless of the result so user can read back the * modified config and evaluate if the changes are acceptable */ if (priv->config) gst_structure_free (priv->config); priv->config = config; if (result) { /* now we are configured */ priv->configured = TRUE; } GST_BUFFER_POOL_UNLOCK (pool); return result; config_unchanged: { gst_structure_free (config); GST_BUFFER_POOL_UNLOCK (pool); return TRUE; } /* ERRORS */ was_active: { gst_structure_free (config); GST_WARNING_OBJECT (pool, "can't change config, we are active"); GST_BUFFER_POOL_UNLOCK (pool); return FALSE; } have_outstanding: { gst_structure_free (config); GST_WARNING_OBJECT (pool, "can't change config, have outstanding buffers"); GST_BUFFER_POOL_UNLOCK (pool); return FALSE; } }
static GstFlowReturn gst_avdtp_src_create (GstBaseSrc * bsrc, guint64 offset, guint length, GstBuffer ** outbuf) { GstAvdtpSrc *avdtpsrc = GST_AVDTP_SRC (bsrc); GstBuffer *buf = NULL; GstMapInfo info; int ret; if (g_atomic_int_get (&avdtpsrc->unlocked)) return GST_FLOW_FLUSHING; /* We don't operate in GST_FORMAT_BYTES, so offset is ignored */ while ((ret = gst_poll_wait (avdtpsrc->poll, GST_CLOCK_TIME_NONE))) { if (g_atomic_int_get (&avdtpsrc->unlocked)) /* We're unlocked, time to gtfo */ return GST_FLOW_FLUSHING; if (ret < 0) /* Something went wrong */ goto read_error; if (ret > 0) /* Got some data */ break; } ret = GST_BASE_SRC_CLASS (parent_class)->alloc (bsrc, offset, length, outbuf); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto alloc_failed; buf = *outbuf; gst_buffer_map (buf, &info, GST_MAP_WRITE); ret = read (avdtpsrc->pfd.fd, info.data, length); if (ret < 0) goto read_error; else if (ret == 0) { GST_INFO_OBJECT (avdtpsrc, "Got EOF on the transport fd"); goto eof; } if (ret < length) gst_buffer_set_size (buf, ret); GST_LOG_OBJECT (avdtpsrc, "Read %d bytes", ret); gst_buffer_unmap (buf, &info); *outbuf = buf; return GST_FLOW_OK; alloc_failed: { GST_DEBUG_OBJECT (bsrc, "alloc failed: %s", gst_flow_get_name (ret)); return ret; } read_error: GST_ERROR_OBJECT (avdtpsrc, "Error while reading audio data: %s", strerror (errno)); gst_buffer_unref (buf); return GST_FLOW_ERROR; eof: gst_buffer_unref (buf); return GST_FLOW_EOS; }
static liHandlerResult fastcgi_statemachine(liVRequest *vr, fastcgi_connection *fcon) { liPlugin *p = fcon->ctx->plugin; switch (fcon->state) { case FS_WAIT_FOR_REQUEST: /* wait until we have either all data or the cqlimit is full */ if (-1 == vr->request.content_length || vr->request.content_length != vr->in->length) { if (0 != li_chunkqueue_limit_available(vr->in)) return LI_HANDLER_GO_ON; } fcon->state = FS_CONNECT; /* fall through */ case FS_CONNECT: do { fcon->fd = socket(fcon->ctx->socket.addr->plain.sa_family, SOCK_STREAM, 0); } while (-1 == fcon->fd && errno == EINTR); if (-1 == fcon->fd) { if (errno == EMFILE) { li_server_out_of_fds(vr->wrk->srv); } else if (errno != g_atomic_int_get(&fcon->ctx->last_errno)) { g_atomic_int_set(&fcon->ctx->last_errno, errno); VR_ERROR(vr, "Couldn't open socket: %s", g_strerror(errno)); } return LI_HANDLER_ERROR; } li_fd_init(fcon->fd); ev_io_set(&fcon->fd_watcher, fcon->fd, EV_READ | EV_WRITE); ev_io_start(vr->wrk->loop, &fcon->fd_watcher); /* fall through */ case FS_CONNECTING: if (-1 == connect(fcon->fd, &fcon->ctx->socket.addr->plain, fcon->ctx->socket.len)) { switch (errno) { case EINPROGRESS: case EALREADY: case EINTR: fcon->state = FS_CONNECTING; return LI_HANDLER_GO_ON; case EAGAIN: /* backend overloaded */ fastcgi_close(vr, p); li_vrequest_backend_overloaded(vr); return LI_HANDLER_GO_ON; case EISCONN: break; default: if (errno != g_atomic_int_get(&fcon->ctx->last_errno)) { g_atomic_int_set(&fcon->ctx->last_errno, errno); VR_ERROR(vr, "Couldn't connect to '%s': %s", li_sockaddr_to_string(fcon->ctx->socket, vr->wrk->tmp_str, TRUE)->str, g_strerror(errno)); } fastcgi_close(vr, p); li_vrequest_backend_dead(vr); return LI_HANDLER_GO_ON; } } g_atomic_int_set(&fcon->ctx->last_errno, 0); fcon->state = FS_CONNECTED; /* prepare stream */ fastcgi_send_begin(fcon); fastcgi_send_env(vr, fcon); /* fall through */ case FS_CONNECTED: fastcgi_forward_request(vr, fcon); break; case FS_DONE: break; } return LI_HANDLER_GO_ON; }
void ags_soundcard_thread_run(AgsThread *thread) { AgsSoundcardThread *soundcard_thread; GObject *soundcard; GList *poll_fd; gboolean is_playing, is_recording; GError *error; soundcard_thread = AGS_SOUNDCARD_THREAD(thread); soundcard = soundcard_thread->soundcard; /* real-time setup */ #ifdef AGS_WITH_RT if((AGS_THREAD_RT_SETUP & (g_atomic_int_get(&(thread->flags)))) == 0){ struct sched_param param; /* Declare ourself as a real time task */ param.sched_priority = AGS_RT_PRIORITY; if(sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) { perror("sched_setscheduler failed"); } g_atomic_int_or(&(thread->flags), AGS_THREAD_RT_SETUP); } #endif /* playback */ if((AGS_SOUNDCARD_CAPABILITY_PLAYBACK & (soundcard_thread->soundcard_capability)) != 0){ is_playing = ags_soundcard_is_playing(AGS_SOUNDCARD(soundcard)); if(is_playing){ error = NULL; ags_soundcard_play(AGS_SOUNDCARD(soundcard), &error); if(error != NULL){ //TODO:JK: implement me g_warning("%s", error->message); } } } /* capture */ if((AGS_SOUNDCARD_CAPABILITY_CAPTURE & (soundcard_thread->soundcard_capability)) != 0){ is_recording = ags_soundcard_is_recording(AGS_SOUNDCARD(soundcard)); if(is_recording){ error = NULL; ags_soundcard_record(AGS_SOUNDCARD(soundcard), &error); if(error != NULL){ //TODO:JK: implement me g_warning("%s", error->message); } } } /* duplex */ if((AGS_SOUNDCARD_CAPABILITY_DUPLEX & (soundcard_thread->soundcard_capability)) != 0){ //TODO:JK: implement me } }
/* Thread to handle incoming messages */ static void *janus_echotest_handler(void *data) { JANUS_LOG(LOG_VERB, "Joining EchoTest handler thread\n"); janus_echotest_message *msg = NULL; int error_code = 0; char *error_cause = g_malloc0(512); if(error_cause == NULL) { JANUS_LOG(LOG_FATAL, "Memory error!\n"); return NULL; } json_t *root = NULL; while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) { msg = g_async_queue_pop(messages); if(msg == NULL) continue; if(msg == &exit_message) break; if(msg->handle == NULL) { janus_echotest_message_free(msg); continue; } janus_echotest_session *session = NULL; janus_mutex_lock(&sessions_mutex); if(g_hash_table_lookup(sessions, msg->handle) != NULL ) { session = (janus_echotest_session *)msg->handle->plugin_handle; } janus_mutex_unlock(&sessions_mutex); if(!session) { JANUS_LOG(LOG_ERR, "No session associated with this handle...\n"); janus_echotest_message_free(msg); continue; } if(session->destroyed) { janus_echotest_message_free(msg); continue; } /* Handle request */ error_code = 0; root = NULL; JANUS_LOG(LOG_VERB, "Handling message: %s\n", msg->message); if(msg->message == NULL) { JANUS_LOG(LOG_ERR, "No message??\n"); error_code = JANUS_ECHOTEST_ERROR_NO_MESSAGE; g_snprintf(error_cause, 512, "%s", "No message??"); goto error; } json_error_t error; root = json_loads(msg->message, 0, &error); if(!root) { JANUS_LOG(LOG_ERR, "JSON error: on line %d: %s\n", error.line, error.text); error_code = JANUS_ECHOTEST_ERROR_INVALID_JSON; g_snprintf(error_cause, 512, "JSON error: on line %d: %s", error.line, error.text); goto error; } if(!json_is_object(root)) { JANUS_LOG(LOG_ERR, "JSON error: not an object\n"); error_code = JANUS_ECHOTEST_ERROR_INVALID_JSON; g_snprintf(error_cause, 512, "JSON error: not an object"); goto error; } /* Parse request */ json_t *audio = json_object_get(root, "audio"); if(audio && !json_is_boolean(audio)) { JANUS_LOG(LOG_ERR, "Invalid element (audio should be a boolean)\n"); error_code = JANUS_ECHOTEST_ERROR_INVALID_ELEMENT; g_snprintf(error_cause, 512, "Invalid value (audio should be a boolean)"); goto error; } json_t *video = json_object_get(root, "video"); if(video && !json_is_boolean(video)) { JANUS_LOG(LOG_ERR, "Invalid element (video should be a boolean)\n"); error_code = JANUS_ECHOTEST_ERROR_INVALID_ELEMENT; g_snprintf(error_cause, 512, "Invalid value (video should be a boolean)"); goto error; } json_t *bitrate = json_object_get(root, "bitrate"); if(bitrate && (!json_is_integer(bitrate) || json_integer_value(bitrate) < 0)) { JANUS_LOG(LOG_ERR, "Invalid element (bitrate should be a positive integer)\n"); error_code = JANUS_ECHOTEST_ERROR_INVALID_ELEMENT; g_snprintf(error_cause, 512, "Invalid value (bitrate should be a positive integer)"); goto error; } json_t *record = json_object_get(root, "record"); if(record && !json_is_boolean(record)) { JANUS_LOG(LOG_ERR, "Invalid element (record should be a boolean)\n"); error_code = JANUS_ECHOTEST_ERROR_INVALID_ELEMENT; g_snprintf(error_cause, 512, "Invalid value (record should be a boolean)"); goto error; } json_t *recfile = json_object_get(root, "filename"); if(recfile && !json_is_string(recfile)) { JANUS_LOG(LOG_ERR, "Invalid element (filename should be a string)\n"); error_code = JANUS_ECHOTEST_ERROR_INVALID_ELEMENT; g_snprintf(error_cause, 512, "Invalid value (filename should be a string)"); goto error; } /* Enforce request */ if(audio) { session->audio_active = json_is_true(audio); JANUS_LOG(LOG_VERB, "Setting audio property: %s\n", session->audio_active ? "true" : "false"); } if(video) { if(!session->video_active && json_is_true(video)) { /* Send a PLI */ JANUS_LOG(LOG_VERB, "Just (re-)enabled video, sending a PLI to recover it\n"); char buf[12]; memset(buf, 0, 12); janus_rtcp_pli((char *)&buf, 12); gateway->relay_rtcp(session->handle, 1, buf, 12); } session->video_active = json_is_true(video); JANUS_LOG(LOG_VERB, "Setting video property: %s\n", session->video_active ? "true" : "false"); } if(bitrate) { session->bitrate = json_integer_value(bitrate); JANUS_LOG(LOG_VERB, "Setting video bitrate: %"SCNu64"\n", session->bitrate); if(session->bitrate > 0) { /* FIXME Generate a new REMB (especially useful for Firefox, which doesn't send any we can cap later) */ char buf[24]; memset(buf, 0, 24); janus_rtcp_remb((char *)&buf, 24, session->bitrate); JANUS_LOG(LOG_VERB, "Sending REMB\n"); gateway->relay_rtcp(session->handle, 1, buf, 24); /* FIXME How should we handle a subsequent "no limit" bitrate? */ } } if(record) { if(msg->sdp) { session->has_audio = (strstr(msg->sdp, "m=audio") != NULL); session->has_video = (strstr(msg->sdp, "m=video") != NULL); } gboolean recording = json_is_true(record); const char *recording_base = json_string_value(recfile); JANUS_LOG(LOG_VERB, "Recording %s (base filename: %s)\n", recording ? "enabled" : "disabled", recording_base ? recording_base : "not provided"); if(!recording) { /* Not recording (anymore?) */ if(session->arc) { janus_recorder_close(session->arc); JANUS_LOG(LOG_INFO, "Closed audio recording %s\n", session->arc->filename ? session->arc->filename : "??"); janus_recorder_free(session->arc); } session->arc = NULL; if(session->vrc) { janus_recorder_close(session->vrc); JANUS_LOG(LOG_INFO, "Closed video recording %s\n", session->vrc->filename ? session->vrc->filename : "??"); janus_recorder_free(session->vrc); } session->vrc = NULL; } else { /* We've started recording, send a PLI and go on */ char filename[255]; gint64 now = janus_get_real_time(); if(session->has_audio) { /* FIXME We assume we're recording Opus, here */ memset(filename, 0, 255); if(recording_base) { /* Use the filename and path we have been provided */ g_snprintf(filename, 255, "%s-audio", recording_base); session->arc = janus_recorder_create(NULL, "opus", filename); if(session->arc == NULL) { /* FIXME We should notify the fact the recorder could not be created */ JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this EchoTest user!\n"); } } else { /* Build a filename */ g_snprintf(filename, 255, "echotest-%p-%"SCNi64"-audio", session, now); session->arc = janus_recorder_create(NULL, "opus", filename); if(session->arc == NULL) { /* FIXME We should notify the fact the recorder could not be created */ JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this EchoTest user!\n"); } } } if(session->has_video) { /* FIXME We assume we're recording VP8, here */ memset(filename, 0, 255); if(recording_base) { /* Use the filename and path we have been provided */ g_snprintf(filename, 255, "%s-video", recording_base); session->vrc = janus_recorder_create(NULL, "vp8", filename); if(session->vrc == NULL) { /* FIXME We should notify the fact the recorder could not be created */ JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this EchoTest user!\n"); } } else { /* Build a filename */ g_snprintf(filename, 255, "echotest-%p-%"SCNi64"-video", session, now); session->vrc = janus_recorder_create(NULL, "vp8", filename); if(session->vrc == NULL) { /* FIXME We should notify the fact the recorder could not be created */ JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this EchoTest user!\n"); } } /* Send a PLI */ JANUS_LOG(LOG_VERB, "Recording video, sending a PLI to kickstart it\n"); char buf[12]; memset(buf, 0, 12); janus_rtcp_pli((char *)&buf, 12); gateway->relay_rtcp(session->handle, 1, buf, 12); } } } /* Any SDP to handle? */ if(msg->sdp) { JANUS_LOG(LOG_VERB, "This is involving a negotiation (%s) as well:\n%s\n", msg->sdp_type, msg->sdp); session->has_audio = (strstr(msg->sdp, "m=audio") != NULL); session->has_video = (strstr(msg->sdp, "m=video") != NULL); } if(!audio && !video && !bitrate && !record && !msg->sdp) { JANUS_LOG(LOG_ERR, "No supported attributes (audio, video, bitrate, record, jsep) found\n"); error_code = JANUS_ECHOTEST_ERROR_INVALID_ELEMENT; g_snprintf(error_cause, 512, "Message error: no supported attributes (audio, video, bitrate, record, jsep) found"); goto error; } json_decref(root); /* Prepare JSON event */ json_t *event = json_object(); json_object_set_new(event, "echotest", json_string("event")); json_object_set_new(event, "result", json_string("ok")); char *event_text = json_dumps(event, JSON_INDENT(3) | JSON_PRESERVE_ORDER); json_decref(event); JANUS_LOG(LOG_VERB, "Pushing event: %s\n", event_text); if(!msg->sdp) { int ret = gateway->push_event(msg->handle, &janus_echotest_plugin, msg->transaction, event_text, NULL, NULL); JANUS_LOG(LOG_VERB, " >> %d (%s)\n", ret, janus_get_api_error(ret)); } else { /* Forward the same offer to the gateway, to start the echo test */ const char *type = NULL; if(!strcasecmp(msg->sdp_type, "offer")) type = "answer"; if(!strcasecmp(msg->sdp_type, "answer")) type = "offer"; /* Any media direction that needs to be fixed? */ char *sdp = g_strdup(msg->sdp); if(strstr(sdp, "a=recvonly")) { /* Turn recvonly to inactive, as we simply bounce media back */ sdp = janus_string_replace(sdp, "a=recvonly", "a=inactive"); } else if(strstr(sdp, "a=sendonly")) { /* Turn sendonly to recvonly */ sdp = janus_string_replace(sdp, "a=sendonly", "a=recvonly"); /* FIXME We should also actually not echo this media back, though... */ } /* Make also sure we get rid of ULPfec, red, etc. */ if(strstr(sdp, "ulpfec")) { /* FIXME This really needs some better code */ sdp = janus_string_replace(sdp, "a=rtpmap:116 red/90000\r\n", ""); sdp = janus_string_replace(sdp, "a=rtpmap:117 ulpfec/90000\r\n", ""); sdp = janus_string_replace(sdp, "a=rtpmap:96 rtx/90000\r\n", ""); sdp = janus_string_replace(sdp, "a=fmtp:96 apt=100\r\n", ""); sdp = janus_string_replace(sdp, "a=rtpmap:97 rtx/90000\r\n", ""); sdp = janus_string_replace(sdp, "a=fmtp:97 apt=101\r\n", ""); sdp = janus_string_replace(sdp, "a=rtpmap:98 rtx/90000\r\n", ""); sdp = janus_string_replace(sdp, "a=fmtp:98 apt=116\r\n", ""); sdp = janus_string_replace(sdp, " 116", ""); sdp = janus_string_replace(sdp, " 117", ""); sdp = janus_string_replace(sdp, " 96", ""); sdp = janus_string_replace(sdp, " 97", ""); sdp = janus_string_replace(sdp, " 98", ""); } /* How long will the gateway take to push the event? */ g_atomic_int_set(&session->hangingup, 0); gint64 start = janus_get_monotonic_time(); int res = gateway->push_event(msg->handle, &janus_echotest_plugin, msg->transaction, event_text, type, sdp); JANUS_LOG(LOG_VERB, " >> Pushing event: %d (took %"SCNu64" us)\n", res, janus_get_monotonic_time()-start); g_free(sdp); } g_free(event_text); janus_echotest_message_free(msg); continue; error: { if(root != NULL) json_decref(root); /* Prepare JSON error event */ json_t *event = json_object(); json_object_set_new(event, "echotest", json_string("event")); json_object_set_new(event, "error_code", json_integer(error_code)); json_object_set_new(event, "error", json_string(error_cause)); char *event_text = json_dumps(event, JSON_INDENT(3) | JSON_PRESERVE_ORDER); json_decref(event); JANUS_LOG(LOG_VERB, "Pushing event: %s\n", event_text); int ret = gateway->push_event(msg->handle, &janus_echotest_plugin, msg->transaction, event_text, NULL, NULL); JANUS_LOG(LOG_VERB, " >> %d (%s)\n", ret, janus_get_api_error(ret)); g_free(event_text); janus_echotest_message_free(msg); } } g_free(error_cause); JANUS_LOG(LOG_VERB, "Leaving EchoTest handler thread\n"); return NULL; }
static void output_loop (gpointer data) { GstPad *pad; GOmxCore *gomx; GOmxPort *out_port; GstOmxBaseFilter *self; GstFlowReturn ret = GST_FLOW_OK; pad = data; self = GST_OMX_BASE_FILTER (gst_pad_get_parent (pad)); gomx = self->gomx; GST_LOG_OBJECT (self, "begin"); /* do not bother if we have been setup to bail out */ if ((ret = g_atomic_int_get (&self->last_pad_push_return)) != GST_FLOW_OK) goto leave; if (!self->ready) { g_error ("not ready"); return; } out_port = self->out_port; if (G_LIKELY (out_port->enabled)) { OMX_BUFFERHEADERTYPE *omx_buffer = NULL; GST_LOG_OBJECT (self, "request buffer"); omx_buffer = g_omx_port_request_buffer (out_port); GST_LOG_OBJECT (self, "omx_buffer: %p", omx_buffer); if (G_UNLIKELY (!omx_buffer)) { GST_WARNING_OBJECT (self, "null buffer: leaving"); ret = GST_FLOW_WRONG_STATE; goto leave; } log_buffer (self, omx_buffer); if (G_LIKELY (omx_buffer->nFilledLen > 0)) { GstBuffer *buf; #if 1 /** @todo remove this check */ if (G_LIKELY (self->in_port->enabled)) { GstCaps *caps = NULL; caps = gst_pad_get_negotiated_caps (self->srcpad); #ifdef ANDROID if (!caps || gomx->settings_changed) { #else if (!caps) { #endif /** @todo We shouldn't be doing this. */ GST_WARNING_OBJECT (self, "faking settings changed notification"); if (gomx->settings_changed_cb) gomx->settings_changed_cb (gomx); #ifdef ANDROID gomx->settings_changed = FALSE; #endif } else { GST_LOG_OBJECT (self, "caps already fixed: %" GST_PTR_FORMAT, caps); gst_caps_unref (caps); } } #endif /* buf is always null when the output buffer pointer isn't shared. */ buf = omx_buffer->pAppPrivate; /** @todo we need to move all the caps handling to one single * place, in the output loop probably. */ if (G_UNLIKELY (omx_buffer->nFlags & 0x80)) { GstCaps *caps = NULL; GstStructure *structure; GValue value = { 0, {{0} } }; caps = gst_pad_get_negotiated_caps (self->srcpad); caps = gst_caps_make_writable (caps); structure = gst_caps_get_structure (caps, 0); g_value_init (&value, GST_TYPE_BUFFER); buf = gst_buffer_new_and_alloc (omx_buffer->nFilledLen); memcpy (GST_BUFFER_DATA (buf), omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen); gst_value_set_buffer (&value, buf); gst_buffer_unref (buf); gst_structure_set_value (structure, "codec_data", &value); g_value_unset (&value); gst_pad_set_caps (self->srcpad, caps); } else if (buf && !(omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) { GST_BUFFER_SIZE (buf) = omx_buffer->nFilledLen; if (self->use_timestamps) { GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND, OMX_TICKS_PER_SECOND); } omx_buffer->pAppPrivate = NULL; omx_buffer->pBuffer = NULL; ret = push_buffer (self, buf); gst_buffer_unref (buf); } else { /* This is only meant for the first OpenMAX buffers, * which need to be pre-allocated. */ /* Also for the very last one. */ ret = gst_pad_alloc_buffer_and_set_caps (self->srcpad, GST_BUFFER_OFFSET_NONE, omx_buffer->nFilledLen, GST_PAD_CAPS (self->srcpad), &buf); if (G_LIKELY (buf)) { memcpy (GST_BUFFER_DATA (buf), omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen); if (self->use_timestamps) { GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND, OMX_TICKS_PER_SECOND); } if (self->share_output_buffer) { GST_WARNING_OBJECT (self, "couldn't zero-copy"); /* If pAppPrivate is NULL, it means it was a dummy * allocation, free it. */ if (!omx_buffer->pAppPrivate) { g_free (omx_buffer->pBuffer); omx_buffer->pBuffer = NULL; } } ret = push_buffer (self, buf); } else { GST_WARNING_OBJECT (self, "couldn't allocate buffer of size %lu", omx_buffer->nFilledLen); } } } else { GST_WARNING_OBJECT (self, "empty buffer"); } if (self->share_output_buffer && !omx_buffer->pBuffer && omx_buffer->nOffset == 0) { GstBuffer *buf; GstFlowReturn result; GST_LOG_OBJECT (self, "allocate buffer"); result = gst_pad_alloc_buffer_and_set_caps (self->srcpad, GST_BUFFER_OFFSET_NONE, omx_buffer->nAllocLen, GST_PAD_CAPS (self->srcpad), &buf); if (G_LIKELY (result == GST_FLOW_OK)) { gst_buffer_ref (buf); omx_buffer->pAppPrivate = buf; omx_buffer->pBuffer = GST_BUFFER_DATA (buf); omx_buffer->nAllocLen = GST_BUFFER_SIZE (buf); } else { GST_WARNING_OBJECT (self, "could not pad allocate buffer, using malloc"); omx_buffer->pBuffer = g_malloc (omx_buffer->nAllocLen); } } if (self->share_output_buffer && !omx_buffer->pBuffer) { GST_ERROR_OBJECT (self, "no input buffer to share"); } if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) { GST_DEBUG_OBJECT (self, "got eos"); gst_pad_push_event (self->srcpad, gst_event_new_eos ()); omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS; ret = GST_FLOW_UNEXPECTED; } omx_buffer->nFilledLen = 0; GST_LOG_OBJECT (self, "release_buffer"); g_omx_port_release_buffer (out_port, omx_buffer); } leave: self->last_pad_push_return = ret; if (gomx->omx_error != OMX_ErrorNone) ret = GST_FLOW_ERROR; if (ret != GST_FLOW_OK) { GST_INFO_OBJECT (self, "pause task, reason: %s", gst_flow_get_name (ret)); gst_pad_pause_task (self->srcpad); } GST_LOG_OBJECT (self, "end"); gst_object_unref (self); } static GstFlowReturn pad_chain (GstPad * pad, GstBuffer * buf) { GOmxCore *gomx; GOmxPort *in_port; GstOmxBaseFilter *self; GstFlowReturn ret = GST_FLOW_OK; self = GST_OMX_BASE_FILTER (GST_OBJECT_PARENT (pad)); gomx = self->gomx; GST_LOG_OBJECT (self, "begin"); GST_LOG_OBJECT (self, "gst_buffer: size=%u", GST_BUFFER_SIZE (buf)); GST_LOG_OBJECT (self, "state: %d", gomx->omx_state); if (G_UNLIKELY (gomx->omx_state == OMX_StateLoaded)) { g_mutex_lock (self->ready_lock); GST_INFO_OBJECT (self, "omx: prepare"); /** @todo this should probably go after doing preparations. */ if (self->omx_setup) { self->omx_setup (self); } setup_ports (self); g_omx_core_prepare (self->gomx); if (gomx->omx_state == OMX_StateIdle) { self->ready = TRUE; GST_INFO_OBJECT (self, "start srcpad task"); gst_pad_start_task (self->srcpad, output_loop, self->srcpad); } g_mutex_unlock (self->ready_lock); if (gomx->omx_state != OMX_StateIdle) goto out_flushing; } #ifdef ANDROID if (gomx->settings_changed) { GST_DEBUG_OBJECT (self, "settings changed called from streaming thread... Android"); if (gomx->settings_changed_cb) gomx->settings_changed_cb (gomx); gomx->settings_changed = FALSE; } #endif in_port = self->in_port; if (G_LIKELY (in_port->enabled)) { guint buffer_offset = 0; if (G_UNLIKELY (gomx->omx_state == OMX_StateIdle)) { GST_INFO_OBJECT (self, "omx: play"); g_omx_core_start (gomx); if (gomx->omx_state != OMX_StateExecuting) goto out_flushing; /* send buffer with codec data flag */ /** @todo move to util */ if (self->codec_data) { OMX_BUFFERHEADERTYPE *omx_buffer; GST_LOG_OBJECT (self, "request buffer"); omx_buffer = g_omx_port_request_buffer (in_port); if (G_LIKELY (omx_buffer)) { omx_buffer->nFlags |= 0x00000080; /* codec data flag */ omx_buffer->nFilledLen = GST_BUFFER_SIZE (self->codec_data); memcpy (omx_buffer->pBuffer + omx_buffer->nOffset, GST_BUFFER_DATA (self->codec_data), omx_buffer->nFilledLen); GST_LOG_OBJECT (self, "release_buffer"); g_omx_port_release_buffer (in_port, omx_buffer); } } } if (G_UNLIKELY (gomx->omx_state != OMX_StateExecuting)) { GST_ERROR_OBJECT (self, "Whoa! very wrong"); } while (G_LIKELY (buffer_offset < GST_BUFFER_SIZE (buf))) { OMX_BUFFERHEADERTYPE *omx_buffer; if (self->last_pad_push_return != GST_FLOW_OK || !(gomx->omx_state == OMX_StateExecuting || gomx->omx_state == OMX_StatePause)) { goto out_flushing; } GST_LOG_OBJECT (self, "request buffer"); omx_buffer = g_omx_port_request_buffer (in_port); GST_LOG_OBJECT (self, "omx_buffer: %p", omx_buffer); if (G_LIKELY (omx_buffer)) { log_buffer (self, omx_buffer); if (omx_buffer->nOffset == 0 && self->share_input_buffer) { { GstBuffer *old_buf; old_buf = omx_buffer->pAppPrivate; if (old_buf) { gst_buffer_unref (old_buf); } else if (omx_buffer->pBuffer) { g_free (omx_buffer->pBuffer); } } omx_buffer->pBuffer = GST_BUFFER_DATA (buf); omx_buffer->nAllocLen = GST_BUFFER_SIZE (buf); omx_buffer->nFilledLen = GST_BUFFER_SIZE (buf); omx_buffer->pAppPrivate = buf; } else { omx_buffer->nFilledLen = MIN (GST_BUFFER_SIZE (buf) - buffer_offset, omx_buffer->nAllocLen - omx_buffer->nOffset); memcpy (omx_buffer->pBuffer + omx_buffer->nOffset, GST_BUFFER_DATA (buf) + buffer_offset, omx_buffer->nFilledLen); } if (self->use_timestamps) { GstClockTime timestamp_offset = 0; if (buffer_offset && GST_BUFFER_DURATION (buf) != GST_CLOCK_TIME_NONE) { timestamp_offset = gst_util_uint64_scale_int (buffer_offset, GST_BUFFER_DURATION (buf), GST_BUFFER_SIZE (buf)); } omx_buffer->nTimeStamp = gst_util_uint64_scale_int (GST_BUFFER_TIMESTAMP (buf) + timestamp_offset, OMX_TICKS_PER_SECOND, GST_SECOND); } buffer_offset += omx_buffer->nFilledLen; #ifdef ANDROID omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME; log_buffer (self, omx_buffer); #endif GST_LOG_OBJECT (self, "release_buffer"); /** @todo untaint buffer */ g_omx_port_release_buffer (in_port, omx_buffer); } else { GST_WARNING_OBJECT (self, "null buffer"); ret = GST_FLOW_WRONG_STATE; goto out_flushing; } } } else { GST_WARNING_OBJECT (self, "done"); ret = GST_FLOW_UNEXPECTED; } if (!self->share_input_buffer) { gst_buffer_unref (buf); } leave: GST_LOG_OBJECT (self, "end"); return ret; /* special conditions */ out_flushing: { const gchar *error_msg = NULL; if (gomx->omx_error) { error_msg = "Error from OpenMAX component"; } else if (gomx->omx_state != OMX_StateExecuting && gomx->omx_state != OMX_StatePause) { error_msg = "OpenMAX component in wrong state"; } if (error_msg) { GST_ELEMENT_ERROR (self, STREAM, FAILED, (NULL), ("%s", error_msg)); ret = GST_FLOW_ERROR; } gst_buffer_unref (buf); goto leave; } }
/* GstAggregator vmethods default implementations */ static gboolean _sink_event (GstAggregator * self, GstAggregatorPad * aggpad, GstEvent * event) { gboolean res = TRUE; GstPad *pad = GST_PAD (aggpad); GstAggregatorPrivate *priv = self->priv; GstAggregatorPadPrivate *padpriv = aggpad->priv; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_START: { GstBuffer *tmpbuf; g_atomic_int_set (&aggpad->priv->flushing, TRUE); /* Remove pad buffer and wake up the streaming thread */ tmpbuf = gst_aggregator_pad_steal_buffer (aggpad); gst_buffer_replace (&tmpbuf, NULL); if (g_atomic_int_compare_and_exchange (&padpriv->pending_flush_start, TRUE, FALSE) == TRUE) { GST_DEBUG_OBJECT (aggpad, "Expecting FLUSH_STOP now"); g_atomic_int_set (&padpriv->pending_flush_stop, TRUE); } if (g_atomic_int_get (&priv->flush_seeking)) { /* If flush_seeking we forward the first FLUSH_START */ if (g_atomic_int_compare_and_exchange (&priv->pending_flush_start, TRUE, FALSE) == TRUE) { GST_DEBUG_OBJECT (self, "Flushing, pausing srcpad task"); _stop_srcpad_task (self, event); priv->flow_return = GST_FLOW_OK; GST_INFO_OBJECT (self, "Getting STREAM_LOCK while seeking"); GST_PAD_STREAM_LOCK (self->srcpad); GST_LOG_OBJECT (self, "GOT STREAM_LOCK"); event = NULL; goto eat; } } /* We forward only in one case: right after flush_seeking */ goto eat; } case GST_EVENT_FLUSH_STOP: { GST_DEBUG_OBJECT (aggpad, "Got FLUSH_STOP"); _aggpad_flush (aggpad, self); if (g_atomic_int_get (&priv->flush_seeking)) { g_atomic_int_set (&aggpad->priv->pending_flush_stop, FALSE); if (g_atomic_int_get (&priv->flush_seeking)) { if (_all_flush_stop_received (self)) { /* That means we received FLUSH_STOP/FLUSH_STOP on * all sinkpads -- Seeking is Done... sending FLUSH_STOP */ _flush (self); gst_pad_push_event (self->srcpad, event); priv->send_eos = TRUE; event = NULL; _add_aggregate_gsource (self); GST_INFO_OBJECT (self, "Releasing source pad STREAM_LOCK"); GST_PAD_STREAM_UNLOCK (self->srcpad); _start_srcpad_task (self); } } } /* We never forward the event */ goto eat; } case GST_EVENT_EOS: { GST_DEBUG_OBJECT (aggpad, "EOS"); /* We still have a buffer, and we don't want the subclass to have to * check for it. Mark pending_eos, eos will be set when steal_buffer is * called */ PAD_LOCK_EVENT (aggpad); if (!aggpad->buffer) { aggpad->eos = TRUE; } else { aggpad->priv->pending_eos = TRUE; } PAD_UNLOCK_EVENT (aggpad); _add_aggregate_gsource (self); goto eat; } case GST_EVENT_SEGMENT: { PAD_LOCK_EVENT (aggpad); gst_event_copy_segment (event, &aggpad->segment); PAD_UNLOCK_EVENT (aggpad); goto eat; } case GST_EVENT_STREAM_START: { goto eat; } case GST_EVENT_TAG: { GstTagList *tags; gst_event_parse_tag (event, &tags); if (gst_tag_list_get_scope (tags) == GST_TAG_SCOPE_STREAM) { gst_aggregator_merge_tags (self, tags, GST_TAG_MERGE_REPLACE); gst_event_unref (event); event = NULL; goto eat; } break; } default: { break; } } GST_DEBUG_OBJECT (pad, "Forwarding event: %" GST_PTR_FORMAT, event); return gst_pad_event_default (pad, GST_OBJECT (self), event); eat: GST_DEBUG_OBJECT (pad, "Eating event: %" GST_PTR_FORMAT, event); if (event) gst_event_unref (event); return res; }
/** * g_thread_pool_get_num_unused_threads: * * Returns the number of currently unused threads. * * Return value: the number of currently unused threads */ guint g_thread_pool_get_num_unused_threads (void) { return g_atomic_int_get (&unused_threads); }
/* this internal thread does nothing else but write samples to the audio device. * It will write each segment in the ringbuffer and will update the play * pointer. * The start/stop methods control the thread. */ static void audioringbuffer_thread_func (GstAudioRingBuffer * buf) { GstAudioSink *sink; GstAudioSinkClass *csink; GstAudioSinkRingBuffer *abuf = GST_AUDIO_SINK_RING_BUFFER_CAST (buf); WriteFunc writefunc; GstMessage *message; GValue val = { 0 }; sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); csink = GST_AUDIO_SINK_GET_CLASS (sink); GST_DEBUG_OBJECT (sink, "enter thread"); GST_OBJECT_LOCK (abuf); GST_DEBUG_OBJECT (sink, "signal wait"); GST_AUDIO_SINK_RING_BUFFER_SIGNAL (buf); GST_OBJECT_UNLOCK (abuf); writefunc = csink->write; if (writefunc == NULL) goto no_function; message = gst_message_new_stream_status (GST_OBJECT_CAST (buf), GST_STREAM_STATUS_TYPE_ENTER, GST_ELEMENT_CAST (sink)); g_value_init (&val, GST_TYPE_G_THREAD); g_value_set_boxed (&val, g_thread_self ()); gst_message_set_stream_status_object (message, &val); g_value_unset (&val); GST_DEBUG_OBJECT (sink, "posting ENTER stream status"); gst_element_post_message (GST_ELEMENT_CAST (sink), message); while (TRUE) { gint left, len; guint8 *readptr; gint readseg; /* buffer must be started */ if (gst_audio_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) { gint written; left = len; do { written = writefunc (sink, readptr, left); GST_LOG_OBJECT (sink, "transfered %d bytes of %d from segment %d", written, left, readseg); if (written < 0 || written > left) { /* might not be critical, it e.g. happens when aborting playback */ GST_WARNING_OBJECT (sink, "error writing data in %s (reason: %s), skipping segment (left: %d, written: %d)", GST_DEBUG_FUNCPTR_NAME (writefunc), (errno > 1 ? g_strerror (errno) : "unknown"), left, written); break; } left -= written; readptr += written; } while (left > 0); /* clear written samples */ gst_audio_ring_buffer_clear (buf, readseg); /* we wrote one segment */ gst_audio_ring_buffer_advance (buf, 1); } else { GST_OBJECT_LOCK (abuf); if (!abuf->running) goto stop_running; if (G_UNLIKELY (g_atomic_int_get (&buf->state) == GST_AUDIO_RING_BUFFER_STATE_STARTED)) { GST_OBJECT_UNLOCK (abuf); continue; } GST_DEBUG_OBJECT (sink, "signal wait"); GST_AUDIO_SINK_RING_BUFFER_SIGNAL (buf); GST_DEBUG_OBJECT (sink, "wait for action"); GST_AUDIO_SINK_RING_BUFFER_WAIT (buf); GST_DEBUG_OBJECT (sink, "got signal"); if (!abuf->running) goto stop_running; GST_DEBUG_OBJECT (sink, "continue running"); GST_OBJECT_UNLOCK (abuf); } } /* Will never be reached */ g_assert_not_reached (); return; /* ERROR */ no_function: { GST_DEBUG_OBJECT (sink, "no write function, exit thread"); return; } stop_running: { GST_OBJECT_UNLOCK (abuf); GST_DEBUG_OBJECT (sink, "stop running, exit thread"); message = gst_message_new_stream_status (GST_OBJECT_CAST (buf), GST_STREAM_STATUS_TYPE_LEAVE, GST_ELEMENT_CAST (sink)); g_value_init (&val, GST_TYPE_G_THREAD); g_value_set_boxed (&val, g_thread_self ()); gst_message_set_stream_status_object (message, &val); g_value_unset (&val); GST_DEBUG_OBJECT (sink, "posting LEAVE stream status"); gst_element_post_message (GST_ELEMENT_CAST (sink), message); return; } }
/** * g_thread_pool_get_max_unused_threads: * * Returns the maximal allowed number of unused threads. * * Return value: the maximal number of unused threads */ gint g_thread_pool_get_max_unused_threads (void) { return g_atomic_int_get (&max_unused_threads); }
static GRealThreadPool* g_thread_pool_wait_for_new_pool (void) { GRealThreadPool *pool; gint local_wakeup_thread_serial; guint local_max_unused_threads; gint local_max_idle_time; gint last_wakeup_thread_serial; gboolean have_relayed_thread_marker = FALSE; local_max_unused_threads = g_atomic_int_get (&max_unused_threads); local_max_idle_time = g_atomic_int_get (&max_idle_time); last_wakeup_thread_serial = g_atomic_int_get (&wakeup_thread_serial); g_atomic_int_inc (&unused_threads); do { if (g_atomic_int_get (&unused_threads) >= local_max_unused_threads) { /* If this is a superfluous thread, stop it. */ pool = NULL; } else if (local_max_idle_time > 0) { /* If a maximal idle time is given, wait for the given time. */ DEBUG_MSG (("thread %p waiting in global pool for %f seconds.", g_thread_self (), local_max_idle_time / 1000.0)); pool = g_async_queue_timeout_pop (unused_thread_queue, local_max_idle_time * 1000); } else { /* If no maximal idle time is given, wait indefinitely. */ DEBUG_MSG (("thread %p waiting in global pool.", g_thread_self ())); pool = g_async_queue_pop (unused_thread_queue); } if (pool == wakeup_thread_marker) { local_wakeup_thread_serial = g_atomic_int_get (&wakeup_thread_serial); if (last_wakeup_thread_serial == local_wakeup_thread_serial) { if (!have_relayed_thread_marker) { /* If this wakeup marker has been received for * the second time, relay it. */ DEBUG_MSG (("thread %p relaying wakeup message to " "waiting thread with lower serial.", g_thread_self ())); g_async_queue_push (unused_thread_queue, wakeup_thread_marker); have_relayed_thread_marker = TRUE; /* If a wakeup marker has been relayed, this thread * will get out of the way for 100 microseconds to * avoid receiving this marker again. */ g_usleep (100); } } else { if (g_atomic_int_add (&kill_unused_threads, -1) > 0) { pool = NULL; break; } DEBUG_MSG (("thread %p updating to new limits.", g_thread_self ())); local_max_unused_threads = g_atomic_int_get (&max_unused_threads); local_max_idle_time = g_atomic_int_get (&max_idle_time); last_wakeup_thread_serial = local_wakeup_thread_serial; have_relayed_thread_marker = FALSE; } } } while (pool == wakeup_thread_marker); g_atomic_int_add (&unused_threads, -1); return pool; }
/** * g_thread_pool_get_max_idle_time: * * This function will return the maximum @interval that a * thread will wait in the thread pool for new tasks before * being stopped. * * If this function returns 0, threads waiting in the thread * pool for new work are not stopped. * * Return value: the maximum @interval (milliseconds) to wait * for new tasks in the thread pool before stopping the * thread * * Since: 2.10 */ guint g_thread_pool_get_max_idle_time (void) { return g_atomic_int_get (&max_idle_time); }
/** * @brief Checks if a consumer is tied to a stopped producer * * @param consumer The consumer object to get the data from * * @retval true The producer is currently stopped and no further * elements will be returned by @ref bq_consumer_get. * @retval false The producer is still active, if @ref bq_consumer_get * returned NULL, it's because there is currently no * data available. * * @note This function does not lock @ref Track::lock, * instead it uses atomic operations to get the @ref * Track::stopped value. */ gboolean bq_consumer_stopped(RTP_session *consumer) { if ( consumer->track == NULL ) return false; return !!g_atomic_int_get(&consumer->track->stopped); }
void li_throttle_pool_acquire(liThrottlePool *pool) { assert(g_atomic_int_get(&pool->refcount) > 0); g_atomic_int_inc(&pool->refcount); }
/* Transport implementation */ int janus_websockets_init(janus_transport_callbacks *callback, const char *config_path) { if(g_atomic_int_get(&stopping)) { /* Still stopping from before */ return -1; } if(callback == NULL || config_path == NULL) { /* Invalid arguments */ return -1; } /* This is the callback we'll need to invoke to contact the gateway */ gateway = callback; /* Read configuration */ char filename[255]; g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_WEBSOCKETS_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config != NULL) { janus_config_print(config); /* Handle configuration */ janus_config_item *item = janus_config_get_item_drilldown(config, "general", "ws_logging"); if(item && item->value) { ws_log_level = atoi(item->value); if(ws_log_level < 0) ws_log_level = 0; } JANUS_LOG(LOG_VERB, "libwebsockets logging: %d\n", ws_log_level); lws_set_log_level(ws_log_level, NULL); old_wss = NULL; janus_mutex_init(&old_wss_mutex); /* Any ACL for either the Janus or Admin API? */ item = janus_config_get_item_drilldown(config, "general", "ws_acl"); if(item && item->value) { gchar **list = g_strsplit(item->value, ",", -1); gchar *index = list[0]; if(index != NULL) { int i=0; while(index != NULL) { if(strlen(index) > 0) { JANUS_LOG(LOG_INFO, "Adding '%s' to the Janus API allowed list...\n", index); janus_websockets_allow_address(g_strdup(index), FALSE); } i++; index = list[i]; } } g_strfreev(list); list = NULL; } item = janus_config_get_item_drilldown(config, "admin", "admin_ws_acl"); if(item && item->value) { gchar **list = g_strsplit(item->value, ",", -1); gchar *index = list[0]; if(index != NULL) { int i=0; while(index != NULL) { if(strlen(index) > 0) { JANUS_LOG(LOG_INFO, "Adding '%s' to the Admin/monitor allowed list...\n", index); janus_websockets_allow_address(g_strdup(index), TRUE); } i++; index = list[i]; } } g_strfreev(list); list = NULL; } /* Setup the Janus API WebSockets server(s) */ item = janus_config_get_item_drilldown(config, "general", "ws"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "WebSockets server disabled\n"); } else { int wsport = 8188; item = janus_config_get_item_drilldown(config, "general", "ws_port"); if(item && item->value) wsport = atoi(item->value); /* Prepare context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = NULL; info.protocols = wss_protocols; info.extensions = libwebsocket_get_internal_extensions(); info.ssl_cert_filepath = NULL; info.ssl_private_key_filepath = NULL; info.gid = -1; info.uid = -1; info.options = 0; /* Create the WebSocket context */ wss = libwebsocket_create_context(&info); if(wss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsock...\n"); } else { JANUS_LOG(LOG_INFO, "WebSockets server started (port %d)...\n", wsport); } } item = janus_config_get_item_drilldown(config, "general", "wss"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Secure WebSockets server disabled\n"); } else { int wsport = 8989; item = janus_config_get_item_drilldown(config, "general", "wss_port"); if(item && item->value) wsport = atoi(item->value); item = janus_config_get_item_drilldown(config, "certificates", "cert_pem"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n"); } else { char *server_pem = (char *)item->value; char *server_key = (char *)item->value; item = janus_config_get_item_drilldown(config, "certificates", "cert_key"); if(item && item->value) server_key = (char *)item->value; JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key); /* Prepare secure context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = NULL; info.protocols = swss_protocols; info.extensions = libwebsocket_get_internal_extensions(); info.ssl_cert_filepath = server_pem; info.ssl_private_key_filepath = server_key; info.gid = -1; info.uid = -1; info.options = 0; /* Create the secure WebSocket context */ swss = libwebsocket_create_context(&info); if(swss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsock...\n"); } else { JANUS_LOG(LOG_INFO, "Secure WebSockets server started (port %d)...\n", wsport); } } } /* Do the same for the Admin API, if enabled */ item = janus_config_get_item_drilldown(config, "admin", "admin_ws"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Admin WebSockets server disabled\n"); } else { int wsport = 7188; item = janus_config_get_item_drilldown(config, "admin", "admin_ws_port"); if(item && item->value) wsport = atoi(item->value); /* Prepare context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = NULL; info.protocols = admin_wss_protocols; info.extensions = libwebsocket_get_internal_extensions(); info.ssl_cert_filepath = NULL; info.ssl_private_key_filepath = NULL; info.gid = -1; info.uid = -1; info.options = 0; /* Create the WebSocket context */ admin_wss = libwebsocket_create_context(&info); if(admin_wss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsock...\n"); } else { JANUS_LOG(LOG_INFO, "Admin WebSockets server started (port %d)...\n", wsport); } } item = janus_config_get_item_drilldown(config, "admin", "admin_wss"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Secure Admin WebSockets server disabled\n"); } else { int wsport = 7989; item = janus_config_get_item_drilldown(config, "admin", "admin_wss_port"); if(item && item->value) wsport = atoi(item->value); item = janus_config_get_item_drilldown(config, "certificates", "cert_pem"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n"); } else { char *server_pem = (char *)item->value; char *server_key = (char *)item->value; item = janus_config_get_item_drilldown(config, "certificates", "cert_key"); if(item && item->value) server_key = (char *)item->value; JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key); /* Prepare secure context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = NULL; info.protocols = admin_swss_protocols; info.extensions = libwebsocket_get_internal_extensions(); info.ssl_cert_filepath = server_pem; info.ssl_private_key_filepath = server_key; info.gid = -1; info.uid = -1; info.options = 0; /* Create the secure WebSocket context */ admin_swss = libwebsocket_create_context(&info); if(admin_swss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsock...\n"); } else { JANUS_LOG(LOG_INFO, "Secure Admin WebSockets server started (port %d)...\n", wsport); } } } } janus_config_destroy(config); config = NULL; if(!wss && !swss && !admin_wss && !admin_swss) { JANUS_LOG(LOG_FATAL, "No WebSockets server started, giving up...\n"); return -1; /* No point in keeping the plugin loaded */ } wss_janus_api_enabled = wss || swss; wss_admin_api_enabled = admin_wss || admin_swss; GError *error = NULL; /* Start the WebSocket service threads */ if(wss != NULL) { wss_thread = g_thread_try_new("websockets thread", &janus_websockets_thread, wss, &error); if(!wss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } if(swss != NULL) { swss_thread = g_thread_try_new("secure websockets thread", &janus_websockets_thread, swss, &error); if(!swss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Secure WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } if(admin_wss != NULL) { admin_wss_thread = g_thread_try_new("admin websockets thread", &janus_websockets_thread, admin_wss, &error); if(!admin_wss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Admin WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } if(admin_swss != NULL) { admin_swss_thread = g_thread_try_new("secure admin websockets thread", &janus_websockets_thread, admin_swss, &error); if(!admin_swss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Secure Admin WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } /* Done */ g_atomic_int_set(&initialized, 1); JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_WEBSOCKETS_NAME); return 0; }
static void output_loop (gpointer data) { GstPad *pad; GOmxCore *gomx; GOmxPort *out_port; GstOmxBaseFilter *self; GstFlowReturn ret = GST_FLOW_OK; pad = data; self = GST_OMX_BASE_FILTER (gst_pad_get_parent (pad)); gomx = self->gomx; GST_LOG_OBJECT (self, "begin"); /* do not bother if we have been setup to bail out */ if ((ret = g_atomic_int_get (&self->last_pad_push_return)) != GST_FLOW_OK) goto leave; if (!self->ready) { g_error ("not ready"); return; } out_port = self->out_port; if (G_LIKELY (out_port->enabled)) { OMX_BUFFERHEADERTYPE *omx_buffer = NULL; GST_LOG_OBJECT (self, "request buffer"); omx_buffer = g_omx_port_request_buffer (out_port); GST_LOG_OBJECT (self, "omx_buffer: %p", omx_buffer); if (G_UNLIKELY (!omx_buffer)) { GST_WARNING_OBJECT (self, "null buffer: leaving"); ret = GST_FLOW_WRONG_STATE; goto leave; } log_buffer (self, omx_buffer); if (G_LIKELY (omx_buffer->nFilledLen > 0)) { GstBuffer *buf; #if 1 /** @todo remove this check */ if (G_LIKELY (self->in_port->enabled)) { GstCaps *caps = NULL; caps = gst_pad_get_negotiated_caps (self->srcpad); if (!caps) { /** @todo We shouldn't be doing this. */ GST_WARNING_OBJECT (self, "faking settings changed notification"); if (gomx->settings_changed_cb) gomx->settings_changed_cb (gomx); } else { GST_LOG_OBJECT (self, "caps already fixed: %" GST_PTR_FORMAT, caps); gst_caps_unref (caps); } } #endif /* buf is always null when the output buffer pointer isn't shared. */ buf = omx_buffer->pAppPrivate; /** @todo we need to move all the caps handling to one single * place, in the output loop probably. */ if (G_UNLIKELY (omx_buffer->nFlags & 0x80)) { GstCaps *caps = NULL; GstStructure *structure; GValue value = { 0 }; caps = gst_pad_get_negotiated_caps (self->srcpad); caps = gst_caps_make_writable (caps); structure = gst_caps_get_structure (caps, 0); g_value_init (&value, GST_TYPE_BUFFER); buf = gst_buffer_new_and_alloc (omx_buffer->nFilledLen); memcpy (GST_BUFFER_DATA (buf), omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen); gst_value_set_buffer (&value, buf); gst_buffer_unref (buf); gst_structure_set_value (structure, "codec_data", &value); g_value_unset (&value); gst_pad_set_caps (self->srcpad, caps); } else if (buf && !(omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) { GST_BUFFER_SIZE (buf) = omx_buffer->nFilledLen; if (self->use_timestamps) { GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND, OMX_TICKS_PER_SECOND); } omx_buffer->pAppPrivate = NULL; omx_buffer->pBuffer = NULL; ret = push_buffer (self, buf); gst_buffer_unref (buf); } else { /* This is only meant for the first OpenMAX buffers, * which need to be pre-allocated. */ /* Also for the very last one. */ ret = gst_pad_alloc_buffer_and_set_caps (self->srcpad, GST_BUFFER_OFFSET_NONE, omx_buffer->nFilledLen, GST_PAD_CAPS (self->srcpad), &buf); if (G_LIKELY (buf)) { memcpy (GST_BUFFER_DATA (buf), omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen); if (self->use_timestamps) { GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND, OMX_TICKS_PER_SECOND); } if (self->share_output_buffer) { GST_WARNING_OBJECT (self, "couldn't zero-copy"); /* If pAppPrivate is NULL, it means it was a dummy * allocation, free it. */ if (!omx_buffer->pAppPrivate) { g_free (omx_buffer->pBuffer); omx_buffer->pBuffer = NULL; } } ret = push_buffer (self, buf); } else { GST_WARNING_OBJECT (self, "couldn't allocate buffer of size %" G_GUINT32_FORMAT, omx_buffer->nFilledLen); } } } else { GST_WARNING_OBJECT (self, "empty buffer"); } if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) { GST_DEBUG_OBJECT (self, "got eos"); gst_pad_push_event (self->srcpad, gst_event_new_eos ()); ret = GST_FLOW_UNEXPECTED; goto leave; } if (self->share_output_buffer && !omx_buffer->pBuffer && omx_buffer->nOffset == 0) { GstBuffer *buf; GstFlowReturn result; GST_LOG_OBJECT (self, "allocate buffer"); result = gst_pad_alloc_buffer_and_set_caps (self->srcpad, GST_BUFFER_OFFSET_NONE, omx_buffer->nAllocLen, GST_PAD_CAPS (self->srcpad), &buf); if (G_LIKELY (result == GST_FLOW_OK)) { gst_buffer_ref (buf); omx_buffer->pAppPrivate = buf; omx_buffer->pBuffer = GST_BUFFER_DATA (buf); omx_buffer->nAllocLen = GST_BUFFER_SIZE (buf); } else { GST_WARNING_OBJECT (self, "could not pad allocate buffer, using malloc"); omx_buffer->pBuffer = g_malloc (omx_buffer->nAllocLen); } } if (self->share_output_buffer && !omx_buffer->pBuffer) { GST_ERROR_OBJECT (self, "no input buffer to share"); } omx_buffer->nFilledLen = 0; GST_LOG_OBJECT (self, "release_buffer"); g_omx_port_release_buffer (out_port, omx_buffer); } leave: self->last_pad_push_return = ret; if (gomx->omx_error != OMX_ErrorNone) ret = GST_FLOW_ERROR; if (ret != GST_FLOW_OK) { GST_INFO_OBJECT (self, "pause task, reason: %s", gst_flow_get_name (ret)); gst_pad_pause_task (self->srcpad); } GST_LOG_OBJECT (self, "end"); gst_object_unref (self); }
static void maildir_check_mail( XfceMailwatchMaildirMailbox *maildir ) { gchar *path = NULL; struct stat st; DBG( "-->>" ); g_mutex_lock( maildir->mutex ); if ( !maildir->path || !*(maildir->path) ) { goto out; } path = g_build_filename( maildir->path, "new", NULL ); if ( stat( path, &st ) < 0 ) { xfce_mailwatch_log_message( maildir->mailwatch, XFCE_MAILWATCH_MAILBOX( maildir ), XFCE_MAILWATCH_LOG_ERROR, _( "Failed to get status of file %s: %s" ), path, g_strerror( errno ) ); goto out; } if ( !S_ISDIR( st.st_mode ) ) { xfce_mailwatch_log_message( maildir->mailwatch, XFCE_MAILWATCH_MAILBOX( maildir ), XFCE_MAILWATCH_LOG_ERROR, _( "%s is not a directory. Is %s really a valid maildir?" ), path, maildir->path ); goto out; } if ( st.st_mtime > maildir->mtime ) { GDir *dir; GError *error = NULL; dir = g_dir_open( path, 0, &error ); if ( dir ) { int count_new = 0; const gchar *entry; while ( ( entry = g_dir_read_name( dir ) ) ) { count_new++; /* only check every 25 entries */ if( !( count_new % 25 ) ) { if( !g_atomic_int_get( &maildir->running ) ) { g_dir_close( dir ); g_atomic_pointer_set( &maildir->thread, NULL ); return; } } } g_dir_close( dir ); xfce_mailwatch_signal_new_messages( maildir->mailwatch, (XfceMailwatchMailbox *) maildir, count_new ); } else { xfce_mailwatch_log_message( maildir->mailwatch, XFCE_MAILWATCH_MAILBOX( maildir ), XFCE_MAILWATCH_LOG_ERROR, "%s", error->message ); g_error_free( error ); } maildir->mtime = st.st_mtime; } out: g_mutex_unlock( maildir->mutex ); if ( path ) { g_free( path ); } DBG( "<<--" ); }
static GstFlowReturn gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame) { GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); GstFlowReturn ret = GST_FLOW_OK; GST_DEBUG_OBJECT (self, "Handling frame %d", frame->system_frame_number); if (G_UNLIKELY (!g_atomic_int_get (&self->active))) goto flushing; if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2output))) { if (!self->input_state) goto not_negotiated; if (!gst_v4l2_object_set_format (self->v4l2output, self->input_state->caps)) goto not_negotiated; } if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2capture))) { GstBufferPool *pool = GST_BUFFER_POOL (self->v4l2output->pool); GstVideoInfo info; GstVideoCodecState *output_state; GstBuffer *codec_data; GST_DEBUG_OBJECT (self, "Sending header"); codec_data = self->input_state->codec_data; /* We are running in byte-stream mode, so we don't know the headers, but * we need to send something, otherwise the decoder will refuse to * intialize. */ if (codec_data) { gst_buffer_ref (codec_data); } else { codec_data = frame->input_buffer; frame->input_buffer = NULL; } /* Ensure input internal pool is active */ if (!gst_buffer_pool_is_active (pool)) { GstStructure *config = gst_buffer_pool_get_config (pool); gst_buffer_pool_config_set_params (config, self->input_state->caps, self->v4l2output->info.size, 2, 2); /* There is no reason to refuse this config */ if (!gst_buffer_pool_set_config (pool, config)) goto activate_failed; if (!gst_buffer_pool_set_active (pool, TRUE)) goto activate_failed; } GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); ret = gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (self-> v4l2output->pool), &codec_data); GST_VIDEO_DECODER_STREAM_LOCK (decoder); gst_buffer_unref (codec_data); if (!gst_v4l2_object_acquire_format (self->v4l2capture, &info)) goto not_negotiated; output_state = gst_video_decoder_set_output_state (decoder, info.finfo->format, info.width, info.height, self->input_state); /* Copy the rest of the information, there might be more in the future */ output_state->info.interlace_mode = info.interlace_mode; gst_video_codec_state_unref (output_state); if (!gst_video_decoder_negotiate (decoder)) { if (GST_PAD_IS_FLUSHING (decoder->srcpad)) goto flushing; else goto not_negotiated; } /* Ensure our internal pool is activated */ if (!gst_buffer_pool_set_active (GST_BUFFER_POOL (self->v4l2capture->pool), TRUE)) goto activate_failed; } if (g_atomic_int_get (&self->processing) == FALSE) { /* It's possible that the processing thread stopped due to an error */ if (self->output_flow != GST_FLOW_OK && self->output_flow != GST_FLOW_FLUSHING) { GST_DEBUG_OBJECT (self, "Processing loop stopped with error, leaving"); ret = self->output_flow; goto drop; } GST_DEBUG_OBJECT (self, "Starting decoding thread"); /* Start the processing task, when it quits, the task will disable input * processing to unlock input if draining, or prevent potential block */ g_atomic_int_set (&self->processing, TRUE); if (!gst_pad_start_task (decoder->srcpad, (GstTaskFunction) gst_v4l2_video_dec_loop, self, (GDestroyNotify) gst_v4l2_video_dec_loop_stopped)) goto start_task_failed; } if (frame->input_buffer) { GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); ret = gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (self->v4l2output-> pool), &frame->input_buffer); GST_VIDEO_DECODER_STREAM_LOCK (decoder); if (ret == GST_FLOW_FLUSHING) { if (g_atomic_int_get (&self->processing) == FALSE) ret = self->output_flow; goto drop; } else if (ret != GST_FLOW_OK) { goto process_failed; } /* No need to keep input arround */ gst_buffer_replace (&frame->input_buffer, NULL); } gst_video_codec_frame_unref (frame); return ret; /* ERRORS */ not_negotiated: { GST_ERROR_OBJECT (self, "not negotiated"); ret = GST_FLOW_NOT_NEGOTIATED; goto drop; } activate_failed: { GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, (_("Failed to allocate required memory.")), ("Buffer pool activation failed")); ret = GST_FLOW_ERROR; goto drop; } flushing: { ret = GST_FLOW_FLUSHING; goto drop; } start_task_failed: { GST_ELEMENT_ERROR (self, RESOURCE, FAILED, (_("Failed to start decoding thread.")), (NULL)); g_atomic_int_set (&self->processing, FALSE); ret = GST_FLOW_ERROR; goto drop; } process_failed: { GST_ELEMENT_ERROR (self, RESOURCE, FAILED, (_("Failed to process frame.")), ("Maybe be due to not enough memory or failing driver")); ret = GST_FLOW_ERROR; goto drop; } drop: { gst_video_decoder_drop_frame (decoder, frame); return ret; } }
/* Thread to handle incoming messages */ static void *janus_videocall_handler(void *data) { JANUS_LOG(LOG_VERB, "Joining VideoCall handler thread\n"); janus_videocall_message *msg = NULL; int error_code = 0; char error_cause[512]; json_t *root = NULL; while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) { msg = g_async_queue_pop(messages); if(msg == NULL) continue; if(msg == &exit_message) break; if(msg->handle == NULL) { janus_videocall_message_free(msg); continue; } janus_videocall_session *session = (janus_videocall_session *)msg->handle->plugin_handle; if(!session) { JANUS_LOG(LOG_ERR, "No session associated with this handle...\n"); janus_videocall_message_free(msg); continue; } if(session->destroyed) { janus_videocall_message_free(msg); continue; } /* Handle request */ error_code = 0; root = msg->message; if(msg->message == NULL) { JANUS_LOG(LOG_ERR, "No message??\n"); error_code = JANUS_VIDEOCALL_ERROR_NO_MESSAGE; g_snprintf(error_cause, 512, "%s", "No message??"); goto error; } if(!json_is_object(root)) { JANUS_LOG(LOG_ERR, "JSON error: not an object\n"); error_code = JANUS_VIDEOCALL_ERROR_INVALID_JSON; g_snprintf(error_cause, 512, "JSON error: not an object"); goto error; } JANUS_VALIDATE_JSON_OBJECT(root, request_parameters, error_code, error_cause, TRUE, JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT, JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT); if(error_code != 0) goto error; const char *msg_sdp_type = json_string_value(json_object_get(msg->jsep, "type")); const char *msg_sdp = json_string_value(json_object_get(msg->jsep, "sdp")); json_t *request = json_object_get(root, "request"); const char *request_text = json_string_value(request); json_t *result = NULL; char *sdp_type = NULL, *sdp = NULL; if(!strcasecmp(request_text, "list")) { result = json_object(); json_t *list = json_array(); JANUS_LOG(LOG_VERB, "Request for the list of peers\n"); /* Return a list of all available mountpoints */ janus_mutex_lock(&sessions_mutex); GHashTableIter iter; gpointer value; g_hash_table_iter_init(&iter, sessions); while (g_hash_table_iter_next(&iter, NULL, &value)) { janus_videocall_session *user = value; if(user != NULL && user->username != NULL) json_array_append_new(list, json_string(user->username)); } json_object_set_new(result, "list", list); janus_mutex_unlock(&sessions_mutex); } else if(!strcasecmp(request_text, "register")) { /* Map this handle to a username */ if(session->username != NULL) { JANUS_LOG(LOG_ERR, "Already registered (%s)\n", session->username); error_code = JANUS_VIDEOCALL_ERROR_ALREADY_REGISTERED; g_snprintf(error_cause, 512, "Already registered (%s)", session->username); goto error; } JANUS_VALIDATE_JSON_OBJECT(root, username_parameters, error_code, error_cause, TRUE, JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT, JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT); if(error_code != 0) goto error; json_t *username = json_object_get(root, "username"); const char *username_text = json_string_value(username); janus_mutex_lock(&sessions_mutex); if(g_hash_table_lookup(sessions, username_text) != NULL) { janus_mutex_unlock(&sessions_mutex); JANUS_LOG(LOG_ERR, "Username '%s' already taken\n", username_text); error_code = JANUS_VIDEOCALL_ERROR_USERNAME_TAKEN; g_snprintf(error_cause, 512, "Username '%s' already taken", username_text); goto error; } janus_mutex_unlock(&sessions_mutex); session->username = g_strdup(username_text); janus_mutex_lock(&sessions_mutex); g_hash_table_insert(sessions, (gpointer)session->username, session); janus_mutex_unlock(&sessions_mutex); result = json_object(); json_object_set_new(result, "event", json_string("registered")); json_object_set_new(result, "username", json_string(username_text)); /* Also notify event handlers */ if(notify_events && gateway->events_is_enabled()) { json_t *info = json_object(); json_object_set_new(info, "event", json_string("registered")); json_object_set_new(info, "username", json_string(username_text)); gateway->notify_event(&janus_videocall_plugin, session->handle, info); } } else if(!strcasecmp(request_text, "call")) { /* Call another peer */ if(session->username == NULL) { JANUS_LOG(LOG_ERR, "Register a username first\n"); error_code = JANUS_VIDEOCALL_ERROR_REGISTER_FIRST; g_snprintf(error_cause, 512, "Register a username first"); goto error; } if(session->peer != NULL) { JANUS_LOG(LOG_ERR, "Already in a call\n"); error_code = JANUS_VIDEOCALL_ERROR_ALREADY_IN_CALL; g_snprintf(error_cause, 512, "Already in a call"); goto error; } JANUS_VALIDATE_JSON_OBJECT(root, username_parameters, error_code, error_cause, TRUE, JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT, JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT); if(error_code != 0) goto error; json_t *username = json_object_get(root, "username"); const char *username_text = json_string_value(username); if(!strcmp(username_text, session->username)) { JANUS_LOG(LOG_ERR, "You can't call yourself... use the EchoTest for that\n"); error_code = JANUS_VIDEOCALL_ERROR_USE_ECHO_TEST; g_snprintf(error_cause, 512, "You can't call yourself... use the EchoTest for that"); goto error; } janus_mutex_lock(&sessions_mutex); janus_videocall_session *peer = g_hash_table_lookup(sessions, username_text); if(peer == NULL || peer->destroyed) { janus_mutex_unlock(&sessions_mutex); JANUS_LOG(LOG_ERR, "Username '%s' doesn't exist\n", username_text); error_code = JANUS_VIDEOCALL_ERROR_NO_SUCH_USERNAME; g_snprintf(error_cause, 512, "Username '%s' doesn't exist", username_text); goto error; } if(peer->peer != NULL) { janus_mutex_unlock(&sessions_mutex); JANUS_LOG(LOG_VERB, "%s is busy\n", username_text); result = json_object(); json_object_set_new(result, "event", json_string("hangup")); json_object_set_new(result, "username", json_string(session->username)); json_object_set_new(result, "reason", json_string("User busy")); /* Also notify event handlers */ if(notify_events && gateway->events_is_enabled()) { json_t *info = json_object(); json_object_set_new(info, "event", json_string("hangup")); json_object_set_new(info, "reason", json_string("User busy")); gateway->notify_event(&janus_videocall_plugin, session->handle, info); } gateway->close_pc(session->handle); } else { janus_mutex_unlock(&sessions_mutex); /* Any SDP to handle? if not, something's wrong */ if(!msg_sdp) { JANUS_LOG(LOG_ERR, "Missing SDP\n"); error_code = JANUS_VIDEOCALL_ERROR_MISSING_SDP; g_snprintf(error_cause, 512, "Missing SDP"); goto error; } janus_mutex_lock(&sessions_mutex); session->peer = peer; peer->peer = session; session->has_audio = (strstr(msg_sdp, "m=audio") != NULL); session->has_video = (strstr(msg_sdp, "m=video") != NULL); janus_mutex_unlock(&sessions_mutex); JANUS_LOG(LOG_VERB, "%s is calling %s\n", session->username, session->peer->username); JANUS_LOG(LOG_VERB, "This is involving a negotiation (%s) as well:\n%s\n", msg_sdp_type, msg_sdp); /* Send SDP to our peer */ json_t *call = json_object(); json_object_set_new(call, "videocall", json_string("event")); json_t *calling = json_object(); json_object_set_new(calling, "event", json_string("incomingcall")); json_object_set_new(calling, "username", json_string(session->username)); json_object_set_new(call, "result", calling); json_t *jsep = json_pack("{ssss}", "type", msg_sdp_type, "sdp", msg_sdp); g_atomic_int_set(&session->hangingup, 0); int ret = gateway->push_event(peer->handle, &janus_videocall_plugin, NULL, call, jsep); JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret)); g_free(sdp); json_decref(call); json_decref(jsep); /* Send an ack back */ result = json_object(); json_object_set_new(result, "event", json_string("calling")); /* Also notify event handlers */ if(notify_events && gateway->events_is_enabled()) { json_t *info = json_object(); json_object_set_new(info, "event", json_string("calling")); gateway->notify_event(&janus_videocall_plugin, session->handle, info); } } } else if(!strcasecmp(request_text, "accept")) { /* Accept a call from another peer */ if(session->peer == NULL) { JANUS_LOG(LOG_ERR, "No incoming call to accept\n"); error_code = JANUS_VIDEOCALL_ERROR_NO_CALL; g_snprintf(error_cause, 512, "No incoming call to accept"); goto error; } /* Any SDP to handle? if not, something's wrong */ if(!msg_sdp) { JANUS_LOG(LOG_ERR, "Missing SDP\n"); error_code = JANUS_VIDEOCALL_ERROR_MISSING_SDP; g_snprintf(error_cause, 512, "Missing SDP"); goto error; } JANUS_LOG(LOG_VERB, "%s is accepting a call from %s\n", session->username, session->peer->username); JANUS_LOG(LOG_VERB, "This is involving a negotiation (%s) as well:\n%s\n", msg_sdp_type, msg_sdp); session->has_audio = (strstr(msg_sdp, "m=audio") != NULL); session->has_video = (strstr(msg_sdp, "m=video") != NULL); /* Send SDP to our peer */ json_t *jsep = json_pack("{ssss}", "type", msg_sdp_type, "sdp", msg_sdp); json_t *call = json_object(); json_object_set_new(call, "videocall", json_string("event")); json_t *calling = json_object(); json_object_set_new(calling, "event", json_string("accepted")); json_object_set_new(calling, "username", json_string(session->username)); json_object_set_new(call, "result", calling); g_atomic_int_set(&session->hangingup, 0); int ret = gateway->push_event(session->peer->handle, &janus_videocall_plugin, NULL, call, jsep); JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret)); json_decref(call); json_decref(jsep); /* Send an ack back */ result = json_object(); json_object_set_new(result, "event", json_string("accepted")); /* Also notify event handlers */ if(notify_events && gateway->events_is_enabled()) { json_t *info = json_object(); json_object_set_new(info, "event", json_string("accepted")); gateway->notify_event(&janus_videocall_plugin, session->handle, info); } } else if(!strcasecmp(request_text, "set")) { /* Update the local configuration (audio/video mute/unmute, bitrate cap or recording) */ JANUS_VALIDATE_JSON_OBJECT(root, set_parameters, error_code, error_cause, TRUE, JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT, JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT); if(error_code != 0) goto error; json_t *audio = json_object_get(root, "audio"); json_t *video = json_object_get(root, "video"); json_t *bitrate = json_object_get(root, "bitrate"); json_t *record = json_object_get(root, "record"); json_t *recfile = json_object_get(root, "filename"); if(audio) { session->audio_active = json_is_true(audio); JANUS_LOG(LOG_VERB, "Setting audio property: %s\n", session->audio_active ? "true" : "false"); } if(video) { if(!session->video_active && json_is_true(video)) { /* Send a PLI */ JANUS_LOG(LOG_VERB, "Just (re-)enabled video, sending a PLI to recover it\n"); char buf[12]; janus_rtcp_pli((char *)&buf, 12); gateway->relay_rtcp(session->handle, 1, buf, 12); } session->video_active = json_is_true(video); JANUS_LOG(LOG_VERB, "Setting video property: %s\n", session->video_active ? "true" : "false"); } if(bitrate) { session->bitrate = json_integer_value(bitrate); JANUS_LOG(LOG_VERB, "Setting video bitrate: %"SCNu64"\n", session->bitrate); if(session->bitrate > 0) { /* FIXME Generate a new REMB (especially useful for Firefox, which doesn't send any we can cap later) */ char buf[24]; janus_rtcp_remb((char *)&buf, 24, session->bitrate); JANUS_LOG(LOG_VERB, "Sending REMB\n"); gateway->relay_rtcp(session->handle, 1, buf, 24); /* FIXME How should we handle a subsequent "no limit" bitrate? */ } } if(record) { if(msg_sdp) { session->has_audio = (strstr(msg_sdp, "m=audio") != NULL); session->has_video = (strstr(msg_sdp, "m=video") != NULL); } gboolean recording = json_is_true(record); const char *recording_base = json_string_value(recfile); JANUS_LOG(LOG_VERB, "Recording %s (base filename: %s)\n", recording ? "enabled" : "disabled", recording_base ? recording_base : "not provided"); janus_mutex_lock(&session->rec_mutex); if(!recording) { /* Not recording (anymore?) */ if(session->arc) { janus_recorder_close(session->arc); JANUS_LOG(LOG_INFO, "Closed audio recording %s\n", session->arc->filename ? session->arc->filename : "??"); janus_recorder_free(session->arc); } session->arc = NULL; if(session->vrc) { janus_recorder_close(session->vrc); JANUS_LOG(LOG_INFO, "Closed video recording %s\n", session->vrc->filename ? session->vrc->filename : "??"); janus_recorder_free(session->vrc); } session->vrc = NULL; } else { /* We've started recording, send a PLI and go on */ char filename[255]; gint64 now = janus_get_real_time(); if(session->has_audio) { /* FIXME We assume we're recording Opus, here */ memset(filename, 0, 255); if(recording_base) { /* Use the filename and path we have been provided */ g_snprintf(filename, 255, "%s-audio", recording_base); session->arc = janus_recorder_create(NULL, "opus", filename); if(session->arc == NULL) { /* FIXME We should notify the fact the recorder could not be created */ JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this VideoCall user!\n"); } } else { /* Build a filename */ g_snprintf(filename, 255, "videocall-%s-%s-%"SCNi64"-audio", session->username ? session->username : "******", (session->peer && session->peer->username) ? session->peer->username : "******", now); session->arc = janus_recorder_create(NULL, "opus", filename); if(session->arc == NULL) { /* FIXME We should notify the fact the recorder could not be created */ JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this VideoCall user!\n"); } } } if(session->has_video) { /* FIXME We assume we're recording VP8, here */ memset(filename, 0, 255); if(recording_base) { /* Use the filename and path we have been provided */ g_snprintf(filename, 255, "%s-video", recording_base); session->vrc = janus_recorder_create(NULL, "vp8", filename); if(session->vrc == NULL) { /* FIXME We should notify the fact the recorder could not be created */ JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this VideoCall user!\n"); } } else { /* Build a filename */ g_snprintf(filename, 255, "videocall-%s-%s-%"SCNi64"-video", session->username ? session->username : "******", (session->peer && session->peer->username) ? session->peer->username : "******", now); session->vrc = janus_recorder_create(NULL, "vp8", filename); if(session->vrc == NULL) { /* FIXME We should notify the fact the recorder could not be created */ JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this VideoCall user!\n"); } } /* Send a PLI */ JANUS_LOG(LOG_VERB, "Recording video, sending a PLI to kickstart it\n"); char buf[12]; janus_rtcp_pli((char *)&buf, 12); gateway->relay_rtcp(session->handle, 1, buf, 12); } } janus_mutex_unlock(&session->rec_mutex); } /* Also notify event handlers */ if(notify_events && gateway->events_is_enabled()) { json_t *info = json_object(); json_object_set_new(info, "event", json_string("configured")); json_object_set_new(info, "audio_active", session->audio_active ? json_true() : json_false()); json_object_set_new(info, "video_active", session->video_active ? json_true() : json_false()); json_object_set_new(info, "bitrate", json_integer(session->bitrate)); if(session->arc || session->vrc) { json_t *recording = json_object(); if(session->arc && session->arc->filename) json_object_set_new(recording, "audio", json_string(session->arc->filename)); if(session->vrc && session->vrc->filename) json_object_set_new(recording, "video", json_string(session->vrc->filename)); json_object_set_new(info, "recording", recording); } gateway->notify_event(&janus_videocall_plugin, session->handle, info); } /* Send an ack back */ result = json_object(); json_object_set_new(result, "event", json_string("set")); } else if(!strcasecmp(request_text, "hangup")) { /* Hangup an ongoing call or reject an incoming one */ janus_mutex_lock(&sessions_mutex); janus_videocall_session *peer = session->peer; if(peer == NULL) { JANUS_LOG(LOG_WARN, "No call to hangup\n"); } else { JANUS_LOG(LOG_VERB, "%s is hanging up the call with %s\n", session->username, peer->username); session->peer = NULL; peer->peer = NULL; } janus_mutex_unlock(&sessions_mutex); /* Notify the success as an hangup message */ result = json_object(); json_object_set_new(result, "event", json_string("hangup")); json_object_set_new(result, "username", json_string(session->username)); json_object_set_new(result, "reason", json_string("Explicit hangup")); /* Also notify event handlers */ if(notify_events && gateway->events_is_enabled()) { json_t *info = json_object(); json_object_set_new(info, "event", json_string("hangup")); json_object_set_new(info, "reason", json_string("Explicit hangup")); gateway->notify_event(&janus_videocall_plugin, session->handle, info); } gateway->close_pc(session->handle); if(peer != NULL) { /* Send event to our peer too */ json_t *call = json_object(); json_object_set_new(call, "videocall", json_string("event")); json_t *calling = json_object(); json_object_set_new(calling, "event", json_string("hangup")); json_object_set_new(calling, "username", json_string(session->username)); json_object_set_new(calling, "reason", json_string("Remote hangup")); json_object_set_new(call, "result", calling); gateway->close_pc(peer->handle); int ret = gateway->push_event(peer->handle, &janus_videocall_plugin, NULL, call, NULL); JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret)); json_decref(call); /* Also notify event handlers */ if(notify_events && gateway->events_is_enabled()) { json_t *info = json_object(); json_object_set_new(info, "event", json_string("hangup")); json_object_set_new(info, "reason", json_string("Remote hangup")); gateway->notify_event(&janus_videocall_plugin, peer->handle, info); } } } else { JANUS_LOG(LOG_ERR, "Unknown request (%s)\n", request_text); error_code = JANUS_VIDEOCALL_ERROR_INVALID_REQUEST; g_snprintf(error_cause, 512, "Unknown request (%s)", request_text); goto error; } /* Prepare JSON event */ json_t *jsep = sdp ? json_pack("{ssss}", "type", sdp_type, "sdp", sdp) : NULL; json_t *event = json_object(); json_object_set_new(event, "videocall", json_string("event")); if(result != NULL) json_object_set_new(event, "result", result); int ret = gateway->push_event(msg->handle, &janus_videocall_plugin, msg->transaction, event, jsep); JANUS_LOG(LOG_VERB, " >> Pushing event: %d (%s)\n", ret, janus_get_api_error(ret)); g_free(sdp); json_decref(event); if(jsep) json_decref(jsep); janus_videocall_message_free(msg); continue; error: { /* Prepare JSON error event */ json_t *event = json_object(); json_object_set_new(event, "videocall", json_string("event")); json_object_set_new(event, "error_code", json_integer(error_code)); json_object_set_new(event, "error", json_string(error_cause)); int ret = gateway->push_event(msg->handle, &janus_videocall_plugin, msg->transaction, event, NULL); JANUS_LOG(LOG_VERB, " >> Pushing event: %d (%s)\n", ret, janus_get_api_error(ret)); json_decref(event); janus_videocall_message_free(msg); } } JANUS_LOG(LOG_VERB, "Leaving VideoCall handler thread\n"); return NULL; }
static gboolean gst_ac3_parse_check_valid_frame (GstBaseParse * parse, GstBaseParseFrame * frame, guint * framesize, gint * skipsize) { GstAc3Parse *ac3parse = GST_AC3_PARSE (parse); GstBuffer *buf = frame->buffer; GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buf); gint off; gboolean lost_sync, draining, eac, more = FALSE; guint frmsiz, blocks, sid; gint have_blocks = 0; if (G_UNLIKELY (GST_BUFFER_SIZE (buf) < 6)) return FALSE; off = gst_byte_reader_masked_scan_uint32 (&reader, 0xffff0000, 0x0b770000, 0, GST_BUFFER_SIZE (buf)); GST_LOG_OBJECT (parse, "possible sync at buffer offset %d", off); /* didn't find anything that looks like a sync word, skip */ if (off < 0) { *skipsize = GST_BUFFER_SIZE (buf) - 3; return FALSE; } /* possible frame header, but not at offset 0? skip bytes before sync */ if (off > 0) { *skipsize = off; return FALSE; } /* make sure the values in the frame header look sane */ if (!gst_ac3_parse_frame_header (ac3parse, buf, 0, &frmsiz, NULL, NULL, &blocks, &sid, &eac)) { *skipsize = off + 2; return FALSE; } *framesize = frmsiz; if (G_UNLIKELY (g_atomic_int_get (&ac3parse->align) == GST_AC3_PARSE_ALIGN_NONE)) gst_ac3_parse_set_alignment (ac3parse, eac); GST_LOG_OBJECT (parse, "got frame"); lost_sync = GST_BASE_PARSE_LOST_SYNC (parse); draining = GST_BASE_PARSE_DRAINING (parse); if (g_atomic_int_get (&ac3parse->align) == GST_AC3_PARSE_ALIGN_IEC61937) { /* We need 6 audio blocks from each substream, so we keep going forwards * till we have it */ g_assert (blocks > 0); GST_LOG_OBJECT (ac3parse, "Need %d frames before pushing", 6 / blocks); if (sid != 0) { /* We need the first substream to be the one with id 0 */ GST_LOG_OBJECT (ac3parse, "Skipping till we find sid 0"); *skipsize = off + 2; return FALSE; } *framesize = 0; /* Loop till we have 6 blocks per substream */ for (have_blocks = 0; !more && have_blocks < 6; have_blocks += blocks) { /* Loop till we get one frame from each substream */ do { *framesize += frmsiz; if (!gst_byte_reader_skip (&reader, frmsiz) || GST_BUFFER_SIZE (buf) < (*framesize + 6)) { more = TRUE; break; } if (!gst_ac3_parse_frame_header (ac3parse, buf, *framesize, &frmsiz, NULL, NULL, NULL, &sid, &eac)) { *skipsize = off + 2; return FALSE; } } while (sid); } /* We're now at the next frame, so no need to skip if resyncing */ frmsiz = 0; } if (lost_sync && !draining) { guint16 word = 0; GST_DEBUG_OBJECT (ac3parse, "resyncing; checking next frame syncword"); if (more || !gst_byte_reader_skip (&reader, frmsiz) || !gst_byte_reader_get_uint16_be (&reader, &word)) { GST_DEBUG_OBJECT (ac3parse, "... but not sufficient data"); gst_base_parse_set_min_frame_size (parse, *framesize + 6); *skipsize = 0; return FALSE; } else { if (word != 0x0b77) { GST_DEBUG_OBJECT (ac3parse, "0x%x not OK", word); *skipsize = off + 2; return FALSE; } else { /* ok, got sync now, let's assume constant frame size */ gst_base_parse_set_min_frame_size (parse, *framesize); } } } return TRUE; }
static void fastcgi_context_acquire(fastcgi_context *ctx) { assert(g_atomic_int_get(&ctx->refcount) > 0); g_atomic_int_inc(&ctx->refcount); }
static GstFlowReturn gst_ac3_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) { GstAc3Parse *ac3parse = GST_AC3_PARSE (parse); GstBuffer *buf = frame->buffer; guint fsize, rate, chans, blocks, sid; gboolean eac, update_rate = FALSE; if (!gst_ac3_parse_frame_header (ac3parse, buf, 0, &fsize, &rate, &chans, &blocks, &sid, &eac)) goto broken_header; GST_LOG_OBJECT (parse, "size: %u, blocks: %u, rate: %u, chans: %u", fsize, blocks, rate, chans); if (G_UNLIKELY (sid)) { /* dependent frame, no need to (ac)count for or consider further */ GST_LOG_OBJECT (parse, "sid: %d", sid); frame->flags |= GST_BASE_PARSE_FRAME_FLAG_NO_FRAME; /* TODO maybe also mark as DELTA_UNIT, * if that does not surprise baseparse elsewhere */ /* occupies same time space as previous base frame */ if (G_LIKELY (GST_BUFFER_TIMESTAMP (buf) >= GST_BUFFER_DURATION (buf))) GST_BUFFER_TIMESTAMP (buf) -= GST_BUFFER_DURATION (buf); /* only return if we already arranged for caps */ if (G_LIKELY (ac3parse->sample_rate > 0)) return GST_FLOW_OK; } if (G_UNLIKELY (ac3parse->sample_rate != rate || ac3parse->channels != chans || ac3parse->eac != eac)) { GstCaps *caps = gst_caps_new_simple (eac ? "audio/x-eac3" : "audio/x-ac3", "framed", G_TYPE_BOOLEAN, TRUE, "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, chans, NULL); gst_caps_set_simple (caps, "alignment", G_TYPE_STRING, g_atomic_int_get (&ac3parse->align) == GST_AC3_PARSE_ALIGN_IEC61937 ? "iec61937" : "frame", NULL); gst_buffer_set_caps (buf, caps); gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps); gst_caps_unref (caps); ac3parse->sample_rate = rate; ac3parse->channels = chans; ac3parse->eac = eac; update_rate = TRUE; } if (G_UNLIKELY (ac3parse->blocks != blocks)) { ac3parse->blocks = blocks; update_rate = TRUE; } if (G_UNLIKELY (update_rate)) gst_base_parse_set_frame_rate (parse, rate, 256 * blocks, 2, 2); return GST_FLOW_OK; /* ERRORS */ broken_header: { /* this really shouldn't ever happen */ GST_ELEMENT_ERROR (parse, STREAM, DECODE, (NULL), (NULL)); return GST_FLOW_ERROR; } }
pthread_mutex_unlock(thread_pool->pull_mutex); break; } list = list->next; } }while(list == NULL); } returnable_thread = NULL; pthread_mutex_lock(thread_pool->return_mutex); max_threads = g_atomic_int_get(&(thread_pool->max_threads)); if((n_threads = g_atomic_int_get(&(thread_pool->n_threads))) <= max_threads){ ags_thread_pool_pull_running(); }else{ g_atomic_int_inc(&(thread_pool->queued)); while((n_threads = g_atomic_int_get(&(thread_pool->n_threads))) > max_threads){ g_message("n_threads >= max_threads\0"); pthread_cond_wait(thread_pool->return_cond, thread_pool->return_mutex); } g_atomic_int_dec_and_test(&(thread_pool->queued)); ags_thread_pool_pull_running();
static void * audio_thread(void *param) { struct pollfd *fds = NULL; nfds_t nfds = 0; static char buf[16 * 1024]; ppb_message_loop_mark_thread_unsuitable(); nfds = do_rebuild_fds(&fds); pthread_barrier_wait(&stream_list_update_barrier); if (nfds == 0) goto quit; while (1) { if (g_atomic_int_get(&terminate_thread)) goto quit; int res = poll(fds, nfds, 10 * 1000); if (res == -1) { if (errno == EINTR) continue; trace_error("%s, poll, errno=%d\n", __func__, errno); continue; } if (res == 0 || fds == NULL) continue; if (fds[0].revents) drain_wakeup_pipe(fds[0].fd); if (g_atomic_int_get(&rebuild_fds)) { nfds = do_rebuild_fds(&fds); pthread_barrier_wait(&stream_list_update_barrier); if (nfds == 0) goto quit; } for (uintptr_t k = 1; k < nfds; k ++) { unsigned short revents = 0; audio_stream *as = g_hash_table_lookup(stream_by_fd_ht, GINT_TO_POINTER(fds[k].fd)); // check if stream was deleted if (!as) continue; snd_pcm_poll_descriptors_revents(as->pcm, &fds[k], 1, &revents); if (revents & (~(POLLIN | POLLOUT))) { trace_warning("%s, revents have unexpected flags set (%u)\n", __func__, (unsigned int)revents); recover_pcm(as->pcm); } if (revents & (POLLIN | POLLOUT)) { int paused = g_atomic_int_get(&as->paused); snd_pcm_sframes_t frame_count = snd_pcm_avail(as->pcm); if (revents & POLLIN) { // POLLIN const size_t frame_size = 1 * sizeof(int16_t); // mono 16-bit const size_t max_segment_length = MIN(as->sample_frame_count * frame_size, sizeof(buf)); size_t to_process = frame_count * frame_size; while (to_process > 0) { snd_pcm_sframes_t frames_read; const size_t segment_length = MIN(to_process, max_segment_length); frames_read = snd_pcm_readi(as->pcm, buf, segment_length / frame_size); if (frames_read < 0) { trace_warning("%s, snd_pcm_readi error %d\n", __func__, (int)frames_read); recover_pcm(as->pcm); continue; } if (!paused && as->capture_cb) as->capture_cb(buf, frames_read * frame_size, 0, as->cb_user_data); to_process -= frames_read * frame_size; } } else { // POLLOUT const size_t frame_size = 2 * sizeof(int16_t); // stereo 16-bit const size_t max_segment_length = MIN(as->sample_frame_count * frame_size, sizeof(buf)); size_t to_process = frame_count * frame_size; while (to_process > 0) { snd_pcm_sframes_t frames_written; const size_t segment_length = MIN(to_process, max_segment_length); if (paused || !as->playback_cb) memset(buf, 0, segment_length); else as->playback_cb(buf, segment_length, 0, as->cb_user_data); frames_written = snd_pcm_writei(as->pcm, buf, segment_length / frame_size); if (frames_written < 0) { trace_warning("%s, snd_pcm_writei error %d\n", __func__, (int)frames_written); recover_pcm(as->pcm); continue; } to_process -= frames_written * frame_size; } } } } } quit: free(fds); return NULL; }
/** * gst_buffer_pool_set_active: * @pool: a #GstBufferPool * @active: the new active state * * Control the active state of @pool. When the pool is inactive, new calls to * gst_buffer_pool_acquire_buffer() will return with %GST_FLOW_FLUSHING. * * Activating the bufferpool will preallocate all resources in the pool based on * the configuration of the pool. * * Deactivating will free the resources again when there are no outstanding * buffers. When there are outstanding buffers, they will be freed as soon as * they are all returned to the pool. * * Returns: %FALSE when the pool was not configured or when preallocation of the * buffers failed. */ gboolean gst_buffer_pool_set_active (GstBufferPool * pool, gboolean active) { gboolean res = TRUE; GstBufferPoolPrivate *priv; g_return_val_if_fail (GST_IS_BUFFER_POOL (pool), FALSE); GST_LOG_OBJECT (pool, "active %d", active); priv = pool->priv; GST_BUFFER_POOL_LOCK (pool); /* just return if we are already in the right state */ if (priv->active == active) goto was_ok; /* we need to be configured */ if (!priv->configured) goto not_configured; if (active) { if (!do_start (pool)) goto start_failed; /* flush_stop my release buffers, setting to active to avoid running * do_stop while activating the pool */ priv->active = TRUE; /* unset the flushing state now */ do_set_flushing (pool, FALSE); } else { gint outstanding; /* set to flushing first */ do_set_flushing (pool, TRUE); /* when all buffers are in the pool, free them. Else they will be * freed when they are released */ outstanding = g_atomic_int_get (&priv->outstanding); GST_LOG_OBJECT (pool, "outstanding buffers %d", outstanding); if (outstanding == 0) { if (!do_stop (pool)) goto stop_failed; } priv->active = FALSE; } GST_BUFFER_POOL_UNLOCK (pool); return res; was_ok: { GST_DEBUG_OBJECT (pool, "pool was in the right state"); GST_BUFFER_POOL_UNLOCK (pool); return TRUE; } not_configured: { GST_ERROR_OBJECT (pool, "pool was not configured"); GST_BUFFER_POOL_UNLOCK (pool); return FALSE; } start_failed: { GST_ERROR_OBJECT (pool, "start failed"); GST_BUFFER_POOL_UNLOCK (pool); return FALSE; } stop_failed: { GST_WARNING_OBJECT (pool, "stop failed"); GST_BUFFER_POOL_UNLOCK (pool); return FALSE; } }
static audio_stream * alsa_create_stream(audio_stream_direction direction, unsigned int sample_rate, unsigned int sample_frame_count, const char *pcm_name) { audio_stream *as; snd_pcm_hw_params_t *hw_params; snd_pcm_sw_params_t *sw_params; int dir; if (!g_atomic_int_get(&audio_thread_started)) { pthread_barrier_init(&stream_list_update_barrier, NULL, 2); pthread_create(&audio_thread_id, NULL, audio_thread, NULL); g_atomic_int_set(&audio_thread_started, 1); pthread_barrier_wait(&stream_list_update_barrier); } as = calloc(1, sizeof(*as)); if (!as) goto err; as->sample_frame_count = sample_frame_count; g_atomic_int_set(&as->paused, 1); #define CHECK_A(funcname, params) \ do { \ int errcode___ = funcname params; \ if (errcode___ < 0) { \ trace_error("%s, " #funcname ", %s\n", __func__, snd_strerror(errcode___)); \ goto err; \ } \ } while (0) if (direction == STREAM_PLAYBACK) CHECK_A(snd_pcm_open, (&as->pcm, pcm_name, SND_PCM_STREAM_PLAYBACK, 0)); else CHECK_A(snd_pcm_open, (&as->pcm, pcm_name, SND_PCM_STREAM_CAPTURE, 0)); CHECK_A(snd_pcm_hw_params_malloc, (&hw_params)); CHECK_A(snd_pcm_hw_params_any, (as->pcm, hw_params)); CHECK_A(snd_pcm_hw_params_set_access, (as->pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)); CHECK_A(snd_pcm_hw_params_set_format, (as->pcm, hw_params, SND_PCM_FORMAT_S16_LE)); dir = 0; unsigned int rate = sample_rate; CHECK_A(snd_pcm_hw_params_set_rate_near, (as->pcm, hw_params, &rate, &dir)); const int channel_count = (direction == STREAM_PLAYBACK) ? 2 : 1; CHECK_A(snd_pcm_hw_params_set_channels, (as->pcm, hw_params, channel_count)); unsigned int period_time = (long long)sample_frame_count * 1000 * 1000 / sample_rate; period_time = CLAMP(period_time, 1000 * (unsigned int)config.audio_buffer_min_ms, 1000 * (unsigned int)config.audio_buffer_max_ms); dir = 1; CHECK_A(snd_pcm_hw_params_set_period_time_near, (as->pcm, hw_params, &period_time, &dir)); unsigned int buffer_time = 4 * period_time; dir = 1; CHECK_A(snd_pcm_hw_params_set_buffer_time_near, (as->pcm, hw_params, &buffer_time, &dir)); dir = 0; CHECK_A(snd_pcm_hw_params_get_buffer_time, (hw_params, &buffer_time, &dir)); CHECK_A(snd_pcm_hw_params, (as->pcm, hw_params)); snd_pcm_hw_params_free(hw_params); CHECK_A(snd_pcm_sw_params_malloc, (&sw_params)); CHECK_A(snd_pcm_sw_params_current, (as->pcm, sw_params)); CHECK_A(snd_pcm_sw_params, (as->pcm, sw_params)); CHECK_A(snd_pcm_prepare, (as->pcm)); snd_pcm_sw_params_free(sw_params); CHECK_A(snd_pcm_prepare, (as->pcm)); if (direction == STREAM_CAPTURE) CHECK_A(snd_pcm_start, (as->pcm)); as->nfds = snd_pcm_poll_descriptors_count(as->pcm); as->fds = calloc(as->nfds, sizeof(struct pollfd)); if (!as->fds) { trace_error("%s, memory allocation failure\n", __func__); goto err; } snd_pcm_poll_descriptors(as->pcm, as->fds, as->nfds); g_hash_table_insert(active_streams_ht, as, GINT_TO_POINTER(1)); for (uintptr_t k = 0; k < as->nfds; k ++) g_hash_table_insert(stream_by_fd_ht, GINT_TO_POINTER(as->fds[k].fd), as); wakeup_audio_thread(); #undef CHECK_A return as; err: free(as); return NULL; }
/** * qmi_utils_get_traces_enabled: * * Checks whether QMI message traces are currently enabled. * * Returns: %TRUE if traces are enabled, %FALSE otherwise. */ gboolean qmi_utils_get_traces_enabled (void) { return (gboolean) g_atomic_int_get (&__traces_enabled); }
/* Transport implementation */ int janus_pfunix_init(janus_transport_callbacks *callback, const char *config_path) { if(g_atomic_int_get(&stopping)) { /* Still stopping from before */ return -1; } if(callback == NULL || config_path == NULL) { /* Invalid arguments */ return -1; } /* This is the callback we'll need to invoke to contact the gateway */ gateway = callback; /* Read configuration */ char filename[255]; g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_PFUNIX_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config != NULL) { /* Handle configuration */ janus_config_print(config); janus_config_item *item = janus_config_get_item_drilldown(config, "general", "json"); if(item && item->value) { /* Check how we need to format/serialize the JSON output */ if(!strcasecmp(item->value, "indented")) { /* Default: indented, we use three spaces for that */ json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(item->value, "plain")) { /* Not indented and no new lines, but still readable */ json_format = JSON_INDENT(0) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(item->value, "compact")) { /* Compact, so no spaces between separators */ json_format = JSON_COMPACT | JSON_PRESERVE_ORDER; } else { JANUS_LOG(LOG_WARN, "Unsupported JSON format option '%s', using default (indented)\n", item->value); json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } } /* First of all, initialize the socketpair for writeable notifications */ if(socketpair(PF_LOCAL, SOCK_STREAM, 0, write_fd) < 0) { JANUS_LOG(LOG_FATAL, "Error creating socket pair for writeable events: %d, %s\n", errno, strerror(errno)); return -1; } /* Setup the Janus API Unix Sockets server(s) */ item = janus_config_get_item_drilldown(config, "general", "enabled"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Unix Sockets server disabled (Janus API)\n"); } else { item = janus_config_get_item_drilldown(config, "general", "path"); char *pfname = (char *)(item && item->value ? item->value : "/tmp/ux-janusapi"); item = janus_config_get_item_drilldown(config, "general", "type"); const char *type = item && item->value ? item->value : "SOCK_SEQPACKET"; dgram = FALSE; if(!strcasecmp(type, "SOCK_SEQPACKET")) { dgram = FALSE; } else if(!strcasecmp(type, "SOCK_DGRAM")) { dgram = TRUE; } else { JANUS_LOG(LOG_WARN, "Unknown type %s, assuming SOCK_SEQPACKET\n", type); type = "SOCK_SEQPACKET"; } JANUS_LOG(LOG_INFO, "Configuring %s Unix Sockets server (Janus API)\n", type); pfd = janus_pfunix_create_socket(pfname, dgram); } /* Do the same for the Admin API, if enabled */ item = janus_config_get_item_drilldown(config, "admin", "admin_enabled"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Unix Sockets server disabled (Admin API)\n"); } else { item = janus_config_get_item_drilldown(config, "admin", "admin_path"); char *pfname = (char *)(item && item->value ? item->value : "/tmp/ux-janusadmin"); item = janus_config_get_item_drilldown(config, "admin", "admin_type"); const char *type = item && item->value ? item->value : "SOCK_SEQPACKET"; if(!strcasecmp(type, "SOCK_SEQPACKET")) { admin_dgram = FALSE; } else if(!strcasecmp(type, "SOCK_DGRAM")) { admin_dgram = TRUE; } else { JANUS_LOG(LOG_WARN, "Unknown type %s, assuming SOCK_SEQPACKET\n", type); type = "SOCK_SEQPACKET"; } JANUS_LOG(LOG_INFO, "Configuring %s Unix Sockets server (Admin API)\n", type); admin_pfd = janus_pfunix_create_socket(pfname, admin_dgram); } } janus_config_destroy(config); config = NULL; if(pfd < 0 && admin_pfd < 0) { JANUS_LOG(LOG_FATAL, "No Unix Sockets server started, giving up...\n"); return -1; /* No point in keeping the plugin loaded */ } /* Create a couple of hashtables for all clients */ clients = g_hash_table_new(NULL, NULL); clients_by_fd = g_hash_table_new(NULL, NULL); clients_by_path = g_hash_table_new(g_str_hash, g_str_equal); janus_mutex_init(&clients_mutex); /* Start the Unix Sockets service thread */ GError *error = NULL; pfunix_thread = g_thread_try_new("pfunix thread", &janus_pfunix_thread, NULL, &error); if(!pfunix_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Unix Sockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } /* Done */ g_atomic_int_set(&initialized, 1); JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_PFUNIX_NAME); return 0; }