/****************************************************************************** * 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; }
/****************************************************************************** * gst_ticircbuffer_get_data ******************************************************************************/ GstBuffer* gst_ticircbuffer_get_data(GstTICircBuffer *circBuf) { Buffer_Handle hCircBufWindow; Buffer_Attrs bAttrs; GstBuffer *result; Int32 bufSize; if (circBuf == NULL) { return NULL; } /* Reset our mutex condition so calling wait_on_consumer will block */ Rendezvous_reset(circBuf->waitOnProducer); /* Reset the read pointer to the beginning of the buffer when we're * approaching the buffer's end (see function definition for reset * conditions). */ gst_ticircbuffer_reset_read_pointer(circBuf); /* Don't return any data util we have a full window available */ while (!circBuf->drain && !gst_ticircbuffer_window_available(circBuf)) { GST_LOG("blocking output until a full window is available\n"); gst_ticircbuffer_wait_on_producer(circBuf); GST_LOG("unblocking output\n"); gst_ticircbuffer_reset_read_pointer(circBuf); /* Reset our mutex condition so calling wait_on_consumer will block */ Rendezvous_reset(circBuf->waitOnProducer); } /* Set the size of the buffer to be no larger than the window size. Some * audio codecs have an issue when you pass a buffer larger than 64K. * We need to pass it smaller buffer sizes though, as the EOS is detected * when we return a 0 size buffer. */ bufSize = gst_ticircbuffer_data_available(circBuf); if (bufSize > circBuf->windowSize) { bufSize = circBuf->windowSize; } /* Return a reference buffer that points to the area of the circular * buffer we want to decode. */ Buffer_getAttrs(circBuf->hBuf, &bAttrs); bAttrs.reference = TRUE; hCircBufWindow = Buffer_create(bufSize, &bAttrs); Buffer_setUserPtr(hCircBufWindow, circBuf->readPtr); Buffer_setNumBytesUsed(hCircBufWindow, bufSize); GST_LOG("returning data at offset %u\n", circBuf->readPtr - Buffer_getUserPtr(circBuf->hBuf)); result = (GstBuffer*)(gst_tidmaibuffertransport_new(hCircBufWindow, NULL)); GST_BUFFER_TIMESTAMP(result) = circBuf->dataTimeStamp; GST_BUFFER_DURATION(result) = GST_CLOCK_TIME_NONE; return result; }
/****************************************************************************** * gst_tiaudenc1_encode_thread * Call the audio codec to process a full input buffer ******************************************************************************/ static void* gst_tiaudenc1_encode_thread(void *arg) { GstTIAudenc1 *audenc1 = GST_TIAUDENC1(gst_object_ref(arg)); void *threadRet = GstTIThreadSuccess; Buffer_Handle hDstBuf; Int32 encDataConsumed; GstBuffer *encDataWindow = NULL; GstClockTime encDataTime; Buffer_Handle hEncDataWindow; GstBuffer *outBuf; GstClockTime sampleDuration; guint sampleRate; guint numSamples; Int bufIdx; Int ret; GST_LOG("starting audenc encode thread\n"); /* Initialize codec engine */ ret = gst_tiaudenc1_codec_start(audenc1); /* Notify main thread that it is ok to continue initialization */ Rendezvous_meet(audenc1->waitOnEncodeThread); Rendezvous_reset(audenc1->waitOnEncodeThread); if (ret == FALSE) { GST_ELEMENT_ERROR(audenc1, RESOURCE, FAILED, ("Failed to start codec\n"), (NULL)); goto thread_exit; } while (TRUE) { /* Obtain an raw data frame */ encDataWindow = gst_ticircbuffer_get_data(audenc1->circBuf); encDataTime = GST_BUFFER_TIMESTAMP(encDataWindow); hEncDataWindow = GST_TIDMAIBUFFERTRANSPORT_DMAIBUF(encDataWindow); /* Check if there is enough encoded data to be sent to the codec. * The last frame of data may not be sufficient to meet the codec * requirements for the amount of input data. If so just throw * away the last bit of data rather than filling with bogus * data. */ if (GST_BUFFER_SIZE(encDataWindow) < Aenc1_getInBufSize(audenc1->hAe)) { GST_LOG("Not enough audio data remains\n"); if (!audenc1->drainingEOS) { goto thread_failure; } goto thread_exit; } /* Obtain a free output buffer for the encoded data */ if (!(hDstBuf = gst_tidmaibuftab_get_buf(audenc1->hOutBufTab))) { GST_ELEMENT_ERROR(audenc1, RESOURCE, READ, ("Failed to get a free contiguous buffer from BufTab\n"), (NULL)); goto thread_exit; } /* Invoke the audio encoder */ GST_LOG("Invoking the audio encoder at 0x%08lx with %u bytes\n", (unsigned long)Buffer_getUserPtr(hEncDataWindow), GST_BUFFER_SIZE(encDataWindow)); ret = Aenc1_process(audenc1->hAe, hEncDataWindow, hDstBuf); encDataConsumed = Buffer_getNumBytesUsed(hEncDataWindow); if (ret < 0) { GST_ELEMENT_ERROR(audenc1, STREAM, ENCODE, ("Failed to encode audio buffer\n"), (NULL)); goto thread_failure; } /* If no encoded data was used we cannot find the next frame */ if (ret == Dmai_EBITERROR && encDataConsumed == 0) { GST_ELEMENT_ERROR(audenc1, STREAM, ENCODE, ("Fatal bit error\n"), (NULL)); goto thread_failure; } if (ret > 0) { GST_LOG("Aenc1_process returned success code %d\n", ret); } sampleRate = audenc1->samplefreq; numSamples = encDataConsumed / (2 * audenc1->channels) ; sampleDuration = GST_FRAMES_TO_CLOCK_TIME(numSamples, sampleRate); /* Release the reference buffer, and tell the circular buffer how much * data was consumed. */ ret = gst_ticircbuffer_data_consumed(audenc1->circBuf, encDataWindow, encDataConsumed); encDataWindow = NULL; if (!ret) { goto thread_failure; } /* Set the source pad capabilities based on the encoded frame * properties. */ gst_tiaudenc1_set_source_caps(audenc1); /* Create a DMAI transport buffer object to carry a DMAI buffer to * the source pad. The transport buffer knows how to release the * buffer for re-use in this element when the source pad calls * gst_buffer_unref(). */ outBuf = gst_tidmaibuffertransport_new(hDstBuf, audenc1->hOutBufTab, NULL, NULL); gst_buffer_set_data(outBuf, GST_BUFFER_DATA(outBuf), Buffer_getNumBytesUsed(hDstBuf)); gst_buffer_set_caps(outBuf, GST_PAD_CAPS(audenc1->srcpad)); /* Set timestamp on output buffer */ if (audenc1->genTimeStamps) { GST_BUFFER_DURATION(outBuf) = sampleDuration; GST_BUFFER_TIMESTAMP(outBuf) = encDataTime; } else { GST_BUFFER_TIMESTAMP(outBuf) = GST_CLOCK_TIME_NONE; } /* Tell circular buffer how much time we consumed */ gst_ticircbuffer_time_consumed(audenc1->circBuf, sampleDuration); /* Push the transport buffer to the source pad */ GST_LOG("pushing buffer to source pad with timestamp : %" GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP(outBuf)), GST_TIME_ARGS (GST_BUFFER_DURATION(outBuf))); if (gst_pad_push(audenc1->srcpad, outBuf) != GST_FLOW_OK) { GST_DEBUG("push to source pad failed\n"); goto thread_failure; } /* Release buffers no longer in use by the codec */ Buffer_freeUseMask(hDstBuf, gst_tidmaibuffer_CODEC_FREE); } thread_failure: gst_tithread_set_status(audenc1, TIThread_CODEC_ABORTED); gst_ticircbuffer_consumer_aborted(audenc1->circBuf); threadRet = GstTIThreadFailure; thread_exit: /* Re-claim any buffers owned by the codec */ bufIdx = BufTab_getNumBufs(GST_TIDMAIBUFTAB_BUFTAB(audenc1->hOutBufTab)); while (bufIdx-- > 0) { Buffer_Handle hBuf = BufTab_getBuf( GST_TIDMAIBUFTAB_BUFTAB(audenc1->hOutBufTab), bufIdx); Buffer_freeUseMask(hBuf, gst_tidmaibuffer_CODEC_FREE); } /* Release the last buffer we retrieved from the circular buffer */ if (encDataWindow) { gst_ticircbuffer_data_consumed(audenc1->circBuf, encDataWindow, 0); } /* We have to wait to shut down this thread until we can guarantee that * no more input buffers will be queued into the circular buffer * (we're about to delete it). */ Rendezvous_meet(audenc1->waitOnEncodeThread); Rendezvous_reset(audenc1->waitOnEncodeThread); /* Notify main thread that we are done draining before we shutdown the * codec, or we will hang. We proceed in this order so the EOS event gets * propagated downstream before we attempt to shut down the codec. The * codec-shutdown process will block until all BufTab buffers have been * released, and downstream-elements may hang on to buffers until * they get the EOS. */ Rendezvous_force(audenc1->waitOnEncodeDrain); /* Initialize codec engine */ if (gst_tiaudenc1_codec_stop(audenc1) < 0) { GST_ERROR("failed to stop codec\n"); GST_ELEMENT_ERROR(audenc1, RESOURCE, FAILED, ("Failed to stop codec\n"), (NULL)); } gst_object_unref(audenc1); GST_LOG("exit audio encode_thread (%d)\n", (int)threadRet); return threadRet; }