Hello,

I'm currently trying to port rockbox to the Nokia N900. The rockbox 
application backend is using SDL for audio and I want to migrate
it to gstreamer.

I tried all kind of ways to push the raw audio via an "appsrc" to "playbin2".
The result is always an "Internal data flow" error.

Please see the attached code and the function call log.
The magic happens in pcm_play_dma_init(), found_source() and feed_data().

Any idea what could be wrong? The timestamps on the GstBuffer?

Can I push arbitrary data size into the appsrc or must it be
in sample size chunks?

The code is quite hacky at the moment as I want to get
it up and running and then beautify it. Sorry:)

Thanks in advance,
Thomas
[TOMJ 1292539796] called pcm_play_dma_init
TOMJ: N900 audio thread up
[TOMJ 1292539796] called pcm_play_lock
[TOMJ 1292539796] called pcm_play_unlock
[TOMJ 1292539796] called pcm_set_mixer_volume
[TOMJ 1292539796] called pcm_set_mixer_volume
[TOMJ 1292539796] called pcm_set_mixer_volume
[TOMJ 1292539796] called pcm_set_mixer_volume
[TOMJ 1292539796] called pcm_postinit
[TOMJ 1292539800] called pcm_play_lock
[TOMJ 1292539800] called pcm_play_unlock
[TOMJ 1292539800] called pcm_play_lock
[TOMJ 1292539800] called pcm_play_unlock
[TOMJ 1292539800] called pcm_play_lock
[TOMJ 1292539800] called pcm_play_unlock
[TOMJ 1292539800] called pcm_set_mixer_volume
[TOMJ 1292539800] called pcm_play_lock
[TOMJ 1292539800] called pcm_play_unlock
[TOMJ 1292539800] called pcm_play_lock
[TOMJ 1292539800] called pcm_dma_apply_settings
[TOMJ 1292539800] called pcm_dma_start: addr: 10606784, size: 34304
got BUS message state-changed
Element playsink0 changed state from NULL to READY.
got BUS message state-changed
Element playbin20 changed state from NULL to READY.
got BUS message state-changed
Element uridecodebin0 changed state from NULL to READY.
got appsrc 0xc6e080
got BUS message state-changed
Element typefind changed state from NULL to READY.
got BUS message state-changed
Element decodebin20 changed state from NULL to READY.
got BUS message state-changed
Element typefind changed state from READY to PAUSED.
got BUS message state-changed
Element source changed state from NULL to READY.
got BUS message stream-status
got BUS message state-changed
Element source changed state from READY to PAUSED.
[TOMJ 1292539800] called pcm_play_unlock
got BUS message stream-status
TOMJ: feed_data: len 34304
feed gst_buffer 0xc7c410, pcm_data: 0xa1d8c0, len 34304
push-buffer result: 0
got BUS message state-changed
Element playbin2inputselector0 changed state from NULL to READY.
got BUS message state-changed
Element playbin2inputselector0 changed state from READY to PAUSED.
got BUS message state-changed
Element audiotee changed state from NULL to READY.
got BUS message state-changed
Element audiotee changed state from READY to PAUSED.
got BUS message state-changed
Element aresample changed state from NULL to READY.
got BUS message state-changed
Element aconv changed state from NULL to READY.
got BUS message state-changed
Element abin changed state from NULL to READY.
got BUS message state-changed
Element aresample changed state from READY to PAUSED.
got BUS message state-changed
Element aconv changed state from READY to PAUSED.
got BUS message state-changed
Element decodebin20 changed state from READY to PAUSED.
got BUS message state-changed
Element uridecodebin0 changed state from READY to PAUSED.
got BUS message error
Received error: Src: source, msg: Internal data flow error.
TOMJ: N900 audio thread down
/***************************************************************************
 *             __________               __   ___.                  
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___  
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /  
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <   
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \  
 *                     \/            \/     \/    \/            \/ 
 * $Id$
 *
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include "autoconf.h"

#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include "config.h"
#include "debug.h"
#include "sound.h"
#include "audiohw.h"
#include "system.h"

#include <SDL.h>
#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
#include <linux/types.h>

#ifdef HAVE_RECORDING
#include "audiohw.h"
#ifdef HAVE_SPDIF_IN
#include "spdif.h"
#endif
#endif

#include "pcm.h"
#include "pcm_sampr.h"

/*#define LOGF_ENABLE*/
#include "logf.h"

#ifdef DEBUG
#include <stdio.h>
extern bool debug_audio;
#endif

static int sim_volume = 0;

#if CONFIG_CODEC == SWCODEC

GstCaps *audio_caps = NULL;
SDL_Thread *audio_thread = NULL;
GstElement *playbin = NULL;
GstElement *appsrc = NULL;
GMainLoop *pcm_loop = NULL;
GstBus *bus = NULL;

static __u8* pcm_data = NULL;
static size_t pcm_data_size = 0;

void pcm_play_lock(void)
{
    printf("[TOMJ %d] called pcm_play_lock\n", time(NULL));
}

void pcm_play_unlock(void)
{
    printf("[TOMJ %d] called pcm_play_unlock\n", time(NULL));
}

void pcm_dma_apply_settings(void)
{
    printf("[TOMJ %d] called pcm_dma_apply_settings\n", time(NULL));

/*
    pcm_play_lock();
    pcm_dma_apply_settings_nolock();
    pcm_play_unlock();
*/
}

void pcm_play_dma_start(const void *addr, size_t size)
{
    printf("[TOMJ %d] called pcm_dma_start: addr: %u, size: %d\n", time(NULL), addr, size);

    pcm_data = (__u8 *) addr;
    pcm_data_size = size;

    /* Tell gstreamer to start playing */
    gst_element_set_state (playbin, GST_STATE_PLAYING);
}

void pcm_play_dma_stop(void)
{
    printf("[TOMJ %d] called pcm_dma_stop\n", time(NULL));

    /* Tell gstreamer to pause */
    gst_element_set_state (playbin, GST_STATE_NULL);
}

void pcm_play_dma_pause(bool pause)
{
    printf("[TOMJ %d] called pcm_dma_pause: %d\n", time(NULL), pause);

    if (pause)
        gst_element_set_state (playbin, GST_STATE_NULL);
    else
        gst_element_set_state (playbin, GST_STATE_PLAYING);
}

size_t pcm_get_bytes_waiting(void)
{
    // printf("[TOMJ %d] called pcm_get_bytes_waiting: %d\n", time(NULL), pcm_data_size);
    return pcm_data_size;
}

static void feed_data(GstElement * appsrc, guint size, void *unused)
{
    printf("TOMJ: feed_data: len %d\n", pcm_data_size);

    /* Audio card wants more? Get some more then. */
    if (pcm_data_size == 0)
        pcm_play_get_more_callback((void **)&pcm_data, &pcm_data_size);

    if (pcm_data_size != 0)
    {
        GstBuffer *buffer = gst_buffer_new ();
        GstFlowReturn ret;

        GST_BUFFER_DATA (buffer) = pcm_data;
        GST_BUFFER_SIZE (buffer) = pcm_data_size;

        printf ("feed gst_buffer %p, pcm_data: %p, len %d\n", buffer, pcm_data, pcm_data_size);
        g_signal_emit_by_name (appsrc, "push-buffer", buffer, &ret);
        // gst_buffer_unref (buffer);

        printf("push-buffer result: %d\n", ret);

        // We fed everything into the buffer
        pcm_data_size = 0;
    } else
    {
        printf("feed_data: No Data.\n");
    }
}

const void * pcm_play_dma_get_peak_buffer(int *count)
{
    // printf("[TOMJ %d] called pcm_play_dma_get_peak_buffer: %d\n", time(NULL), pcm_data_size / 4);

    uintptr_t addr = (uintptr_t)pcm_data;
    *count = pcm_data_size / 4;
    return (void *)((addr + 2) & ~3);
}

/* this callback is called when playbin2 has constructed a source object to read
 * from. Since we provided the appsrc:// uri to playbin2, this will be the
 * appsrc that we must handle. We set up some signals to start and stop pushing
 * data into appsrc */
static void
found_source (GObject * object, GObject * orig, GParamSpec * pspec)
{
  /* get a handle to the appsrc */
  g_object_get (orig, pspec->name, &appsrc, NULL);

  printf ("got appsrc %p\n", appsrc);

  /* we can set the length in appsrc. This allows some elements to estimate the
   * total duration of the stream. It's a good idea to set the property when you
   * can but it's not required. */
  // FIXME: g_object_set (appsrc, "size", (gint64) app->length, NULL);

  // Block push-buffer until there is enough room
  g_object_set (appsrc, "block", TRUE, NULL);

  g_object_set(appsrc, "format", GST_FORMAT_BYTES, NULL);

  audio_caps = gst_caps_new_simple("audio/x-raw-int", "width", G_TYPE_INT, (gint)16, "depth", G_TYPE_INT, (gint)16, "channels" ,G_TYPE_INT, (gint)2,
                             "signed",G_TYPE_INT,1,
                             "rate",G_TYPE_INT,44100,"endianness",G_TYPE_INT,(gint)1234,NULL);

  g_object_set (appsrc, "caps", audio_caps, NULL);

  // Configure appsrc
  gst_app_src_set_stream_type(GST_APP_SRC(appsrc),
    GST_APP_STREAM_TYPE_STREAM);

  /* configure the appsrc, we will push data into the appsrc from the
   * mainloop. */
  g_signal_connect (appsrc, "need-data", G_CALLBACK (feed_data), NULL);
}

static gboolean
bus_message (GstBus * bus, GstMessage * message, void *unused)
{
  printf ("got BUS message %s\n",
      gst_message_type_get_name (GST_MESSAGE_TYPE (message)));

  switch (GST_MESSAGE_TYPE (message)) {
    case GST_MESSAGE_ERROR:
    {
      GError *err;
      gchar *debug;
      gst_message_parse_error (message, &err, &debug);

      printf("Received error: Src: %s, msg: %s\n", GST_MESSAGE_SRC_NAME(message), err->message);

      g_error_free (err);
      g_free (debug);
    }

      g_main_loop_quit (pcm_loop);
      break;
    case GST_MESSAGE_EOS:
      g_main_loop_quit (pcm_loop);
      break;
  case GST_MESSAGE_STATE_CHANGED:
  {
    GstState old_state, new_state;

    gst_message_parse_state_changed (message, &old_state, &new_state, NULL);
    printf ("Element %s changed state from %s to %s.\n",
        GST_MESSAGE_SRC_NAME(message),
        gst_element_state_get_name (old_state),
        gst_element_state_get_name (new_state));
    break;
    }
    default:
      break;
  }
  return TRUE;
}

int n900_audio_thread (void *unused)
{
    printf("TOMJ: N900 audio thread up\n");
    /* this mainloop is stopped when we receive an error or EOS */
    g_main_loop_run (pcm_loop);

    printf("TOMJ: N900 audio thread down\n");

    return 0;
}

void pcm_play_dma_init(void)
{
    printf("[TOMJ %d] called pcm_play_dma_init\n", time(NULL));

    // TODO: g_thread_init(NULL) crashes here

    int argc = 0;
    char *argv = { NULL };
    gst_init (&argc, &argv);

    pcm_loop = g_main_loop_new (NULL, false);

    playbin = gst_element_factory_make ("playbin2", NULL);
    g_assert (playbin);

    bus = gst_pipeline_get_bus (GST_PIPELINE (playbin));

    /* add watch for messages */
    gst_bus_add_watch (bus, (GstBusFunc) bus_message, NULL);

  /* set to read from appsrc */
  g_object_set (playbin, "uri", "appsrc://", NULL);
  g_object_set (playbin, "flags", (1 << 1));    // AUDIO flag
  
  /* get notification when the source is created so that we get a handle to it
   * and can configure it */
  g_signal_connect (playbin, "deep-notify::source",
      (GCallback) found_source, NULL);

   // Start processing the loop
   audio_thread = SDL_CreateThread(n900_audio_thread, NULL);
}

void pcm_postinit(void)
{
    printf("[TOMJ %d] called pcm_postinit\n", time(NULL));
}

void pcm_set_mixer_volume(int volume)
{
    printf("[TOMJ %d] called pcm_set_mixer_volume\n", time(NULL));

    sim_volume = volume;
}


#ifdef HAVE_RECORDING
void pcm_rec_lock(void)
{
}

void pcm_rec_unlock(void)
{
}

void pcm_rec_dma_init(void)
{
}

void pcm_rec_dma_close(void)
{
}

void pcm_rec_dma_start(void *start, size_t size)
{
    (void)start;
    (void)size;
}

void pcm_rec_dma_stop(void)
{
}

const void * pcm_rec_dma_get_peak_buffer(void)
{
    return NULL;
}

void audiohw_set_recvol(int left, int right, int type)
{
    (void)left;
    (void)right;
    (void)type;
}

#ifdef HAVE_SPDIF_IN
unsigned long spdif_measure_frequency(void)
{
    return 0;
}
#endif

#endif /* HAVE_RECORDING */

#endif /* CONFIG_CODEC == SWCODEC */
------------------------------------------------------------------------------
Lotusphere 2011
Register now for Lotusphere 2011 and learn how
to connect the dots, take your collaborative environment
to the next level, and enter the era of Social Business.
http://p.sf.net/sfu/lotusphere-d2d
_______________________________________________
Gstreamer-embedded mailing list
Gstreamer-embedded@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/gstreamer-embedded

Reply via email to