/******************************************************************************
 * 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;
}
示例#3
0
/******************************************************************************
 * 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;
}