/****************************************************************************** * gst_tiaudenc1_chain * This is the main processing routine. This function receives a buffer * from the sink pad, processes it, and pushes the result to the source * pad. ******************************************************************************/ static GstFlowReturn gst_tiaudenc1_chain(GstPad * pad, GstBuffer * buf) { GstTIAudenc1 *audenc1 = GST_TIAUDENC1(GST_OBJECT_PARENT(pad)); GstFlowReturn flow = GST_FLOW_OK; gboolean checkResult; /* If the encode thread aborted, signal it to let it know it's ok to * shut down, and communicate the failure to the pipeline. */ if (gst_tithread_check_status(audenc1, TIThread_CODEC_ABORTED, checkResult)) { flow = GST_FLOW_UNEXPECTED; goto exit; } /* If our engine handle is currently NULL, then either this is our first * buffer or the upstream element has re-negotiated our capabilities which * resulted in our engine being closed. In either case, we need to * initialize (or re-initialize) our audio encoder to handle the new * stream. */ if (audenc1->hEngine == NULL) { if (!gst_tiaudenc1_init_audio(audenc1)) { GST_ELEMENT_ERROR(audenc1, RESOURCE, FAILED, ("Unable to initialize audio\n"), (NULL)); flow = GST_FLOW_UNEXPECTED; goto exit; } GST_TICIRCBUFFER_TIMESTAMP(audenc1->circBuf) = GST_CLOCK_TIME_IS_VALID(GST_BUFFER_TIMESTAMP(buf)) ? GST_BUFFER_TIMESTAMP(buf) : 0ULL; } /* Queue up the encoded data stream into a circular buffer */ if (!gst_ticircbuffer_queue_data(audenc1->circBuf, buf)) { GST_ELEMENT_ERROR(audenc1, RESOURCE, WRITE, ("Failed to queue input buffer into circular buffer\n"), (NULL)); flow = GST_FLOW_UNEXPECTED; goto exit; } exit: gst_buffer_unref(buf); return flow; }
/****************************************************************************** * gst_ticircbuffer_queue_data * Append received encoded data to end of circular buffer ******************************************************************************/ gboolean gst_ticircbuffer_queue_data(GstTICircBuffer *circBuf, GstBuffer *buf) { gboolean result = TRUE; Int32 writeSpace; /* If the circular buffer doesn't exist, do nothing */ if (circBuf == NULL) { goto exit_fail; } /* Reset our mutex condition so a call to wait_on_consumer will block */ Rendezvous_reset(circBuf->waitOnConsumer); /* If the consumer aborted, abort the buffer queuing. We don't want to * queue buffers that no one will read. */ if (circBuf->consumerAborted) { goto exit_fail; } /* If we run out of space, we need to move the data from the last buffer * window to the first window and continue queuing new data in the second * window. If the consumer isn't done with the first window yet, we need * to block until it is. */ while ((writeSpace = gst_ticircbuffer_write_space(circBuf)) < GST_BUFFER_SIZE(buf)) { /* If the write pointer is ahead of the read pointer, check to see if * the first window is free. If it is, we may be able to shift the * data without blocking. */ if (circBuf->contiguousData && gst_ticircbuffer_first_window_free(circBuf)) { if (gst_ticircbuffer_shift_data(circBuf)) { continue; } } /* If there is some space available in the circular buffer, process as * much of the input buffer as we can before we block. The ability to * do this is critical for both encode and decode operations. For * encode, this guarantees that we will always provide a full window * to encode and never starve the codec. For decode, this guarantees * the write pointer will always reach the last window of the circular * buffer before blocking, which is critical for the write pointer to * be reset properly. */ if (writeSpace > 0) { GstBuffer* subBuf; gboolean tmpResult; GST_LOG("splitting input buffer of size %u into two pieces of " "sizes %lu and %lu\n", GST_BUFFER_SIZE(buf), writeSpace, GST_BUFFER_SIZE(buf) - writeSpace); subBuf = gst_buffer_create_sub(buf, 0, writeSpace); tmpResult = gst_ticircbuffer_queue_data(circBuf, subBuf); gst_buffer_unref(subBuf); if (!tmpResult) { goto exit_fail; } subBuf = gst_buffer_create_sub(buf, writeSpace, GST_BUFFER_SIZE(buf) - writeSpace); tmpResult = gst_ticircbuffer_queue_data(circBuf, subBuf); gst_buffer_unref(subBuf); if (!tmpResult) { goto exit_fail; } goto exit; } /* Block until either the first window is free, or there is enough * free space available to put our buffer. */ GST_LOG("blocking input until processing thread catches up\n"); gst_ticircbuffer_wait_on_consumer(circBuf, GST_BUFFER_SIZE(buf)); GST_LOG("unblocking input\n"); /* Reset our mutex condition so calling wait_on_consumer will block */ Rendezvous_reset(circBuf->waitOnConsumer); /* If the consumer aborted, abort the buffer queuing. We don't want to * queue buffers that no one will read. */ if (circBuf->consumerAborted) { goto exit_fail; } gst_ticircbuffer_shift_data(circBuf); } /* Log the buffer timestamp if available */ if (GST_CLOCK_TIME_IS_VALID(GST_BUFFER_TIMESTAMP(buf))) { GST_LOG("buffer received: timestamp: %llu, duration: " "%llu\n", GST_BUFFER_TIMESTAMP(buf), GST_BUFFER_DURATION(buf)); } else { GST_LOG("buffer received: no timestamp available\n"); } /* Copy the buffer using user defined function */ if (circBuf->userCopy) { GST_LOG("copying input buffer using user provided copy fxn\n"); if (circBuf->userCopy(circBuf->writePtr, buf, circBuf->userCopyData) < 0) { GST_ERROR("failed to copy input buffer.\n"); goto exit_fail; } } else { memcpy(circBuf->writePtr, GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf)); } circBuf->writePtr += GST_BUFFER_SIZE(buf); /* Copy new data to the end of the buffer */ GST_LOG("queued %u bytes of data\n", GST_BUFFER_SIZE(buf)); /* Output the buffer status to stdout if buffer debug is enabled */ if (circBuf->displayBuffer) { gst_ticircbuffer_display(circBuf); } /* If the upstream elements are providing time information, update the * duration of time stored by the encoded data. */ if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_DURATION(buf))) { circBuf->dataDuration = GST_CLOCK_TIME_NONE; } else if (GST_CLOCK_TIME_IS_VALID(circBuf->dataDuration)) { circBuf->dataDuration += GST_BUFFER_DURATION(buf); } /* If our buffer got low, some consuming threads may have blocked waiting * for more data. If there is at least a window and our specified read * ahead available in the buffer, unblock any threads. */ if (gst_ticircbuffer_data_size(circBuf) >= circBuf->windowSize + circBuf->readAheadSize) { gst_ticircbuffer_broadcast_producer(circBuf); } goto exit; exit_fail: result = FALSE; exit: return result; }