gst_nokia_speech/gstframedaudioenc.c
changeset 16 8e837d1bf446
equal deleted inserted replaced
15:4b0c6ed43234 16:8e837d1bf446
       
     1 /* GStreamer Framed Audio Encoder
       
     2  * Copyright 2009 Collabora Ltd,
       
     3  * Copyright 2009 Nokia Corporation
       
     4  *  @author: Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>.
       
     5  *
       
     6  * This library is free software; you can redistribute it and/or
       
     7  * modify it under the terms of the GNU Library General Public
       
     8  * License as published by the Free Software Foundation; either
       
     9  * version 2 of the License, or (at your option) any later version.
       
    10  *
       
    11  * This library is distributed in the hope that it will be useful,
       
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    14  * Library General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU Library General Public
       
    17  * License along with this library; if not, write to the
       
    18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    19  * Boston, MA 02111-1307, USA.
       
    20  */
       
    21 
       
    22 #ifdef HAVE_CONFIG_H
       
    23 #include "config.h"
       
    24 #endif
       
    25 #include <gst/gst.h>
       
    26 #include <gst/audio/audio.h>
       
    27 #include <string.h>
       
    28 
       
    29 #include "gstframedaudioenc.h"
       
    30 
       
    31 /* generic part */
       
    32 #ifndef RAW_FRAME_SIZE
       
    33 
       
    34 /* this will reference caller's debug category;
       
    35  * there is a copy of this per plugin lib (= debug category) */
       
    36 GST_DEBUG_CATEGORY_STATIC (framedaudioenc_debug);
       
    37 #define GST_CAT_DEFAULT framedaudioenc_debug
       
    38 
       
    39 void
       
    40 gst_framed_audio_enc_reset (GstFramedAudioEnc * enc)
       
    41 {
       
    42   gst_adapter_clear (enc->adapter);
       
    43   enc->next_ts = GST_CLOCK_TIME_NONE;
       
    44 }
       
    45 
       
    46 /* installs @enc as element private for @element's pad,
       
    47  * and possibly some event and query handler.
       
    48  * if these need overriding, chain up to them
       
    49  * chain and setcaps still need to be set by @element */
       
    50 void
       
    51 gst_framed_audio_enc_init (GstFramedAudioEnc * enc, GstElement * element,
       
    52     GstDebugCategory * cat)
       
    53 {
       
    54   enc->element = element;
       
    55 #ifndef GST_DISABLE_GST_DEBUG
       
    56   framedaudioenc_debug = cat;
       
    57 #endif
       
    58 
       
    59   enc->adapter = gst_adapter_new ();
       
    60 
       
    61   /* hook some */
       
    62   enc->sinkpad = gst_element_get_pad (enc->element, "sink");
       
    63   g_assert (enc->sinkpad);
       
    64   gst_pad_set_element_private (enc->sinkpad, enc);
       
    65 
       
    66   /* watch downstream events */
       
    67   gst_pad_set_event_function (enc->sinkpad,
       
    68       GST_DEBUG_FUNCPTR (gst_framed_audio_enc_sink_event));
       
    69 
       
    70   gst_framed_audio_enc_reset (enc);
       
    71 }
       
    72 
       
    73 void
       
    74 gst_framed_audio_enc_finalize (GstFramedAudioEnc * enc)
       
    75 {
       
    76   gst_object_unref (enc->adapter);
       
    77 
       
    78   gst_pad_set_element_private (enc->sinkpad, NULL);
       
    79   gst_object_unref (enc->sinkpad);
       
    80 }
       
    81 
       
    82 GstPad *
       
    83 gst_framed_audio_enc_request_new_pad (GstFramedAudioEnc * enc,
       
    84     GstPadTemplate * templ, const gchar * req_name, GstPad ** pad_p)
       
    85 {
       
    86   GstElement *element;
       
    87   GstPad *newpad;
       
    88   GstElementClass *klass;
       
    89   GstCaps *caps;
       
    90 
       
    91   g_return_val_if_fail (templ != NULL, NULL);
       
    92 
       
    93   element = enc->element;
       
    94   klass = GST_ELEMENT_GET_CLASS (element);
       
    95 
       
    96   if (templ != gst_element_class_get_pad_template (klass, "cn"))
       
    97     goto wrong_template;
       
    98 
       
    99   GST_DEBUG_OBJECT (enc->element, "adding cn pad");
       
   100   newpad = gst_pad_new_from_template (templ, "cn");
       
   101   /* set template caps */
       
   102   caps = gst_caps_copy (gst_pad_get_pad_template_caps (newpad));
       
   103   gst_pad_set_caps (newpad, caps);
       
   104   gst_caps_unref (caps);
       
   105   gst_pad_use_fixed_caps (newpad);
       
   106   gst_pad_set_active (newpad, TRUE);
       
   107   /* only 1 pad by name can be added */
       
   108   if (gst_element_add_pad (element, newpad)) {
       
   109     GST_OBJECT_LOCK (element);
       
   110     gst_object_replace ((GstObject **) pad_p, GST_OBJECT_CAST (newpad));
       
   111     GST_OBJECT_UNLOCK (element);
       
   112     GST_DEBUG_OBJECT (enc->element, "cn pad added");
       
   113   } else {
       
   114     gst_object_unref (newpad);
       
   115     goto already_requested;
       
   116   }
       
   117 
       
   118   return newpad;
       
   119 
       
   120   /* ERRORS */
       
   121 wrong_template:
       
   122   {
       
   123     GST_ERROR_OBJECT (element, "not our template!");
       
   124     return NULL;
       
   125   }
       
   126 already_requested:
       
   127   {
       
   128     GST_ERROR_OBJECT (element, "only 1 instance of a pad can be requested");
       
   129     return NULL;
       
   130   }
       
   131 }
       
   132 
       
   133 void
       
   134 gst_framed_audio_enc_release_pad (GstFramedAudioEnc * enc, GstPad * pad,
       
   135     GstPad ** pad_p, void (disable_cn) (GstElement *))
       
   136 {
       
   137   GstElement *element = enc->element;
       
   138 
       
   139   GST_DEBUG_OBJECT (enc->element, "releasing cn pad");
       
   140 
       
   141   GST_OBJECT_LOCK (element);
       
   142   if (pad != *pad_p)
       
   143     goto wrong_pad;
       
   144   GST_OBJECT_UNLOCK (element);
       
   145 
       
   146   /* reconfigure encoder */
       
   147   disable_cn (element);
       
   148 
       
   149   if (gst_element_remove_pad (element, pad)) {
       
   150     GST_OBJECT_LOCK (element);
       
   151     gst_object_replace ((GstObject **) pad_p, NULL);
       
   152     GST_OBJECT_UNLOCK (element);
       
   153     GST_DEBUG_OBJECT (enc->element, "cn pad released");
       
   154   }
       
   155 
       
   156   /* ERRORS */
       
   157 wrong_pad:
       
   158   {
       
   159     GST_OBJECT_UNLOCK (element);
       
   160     GST_ERROR_OBJECT (element, "pad not requested; can not be released!");
       
   161     return;
       
   162   }
       
   163 }
       
   164 
       
   165 gboolean
       
   166 gst_framed_audio_enc_sink_event (GstPad * pad, GstEvent * event)
       
   167 {
       
   168   GstFramedAudioEnc *enc;
       
   169 
       
   170   enc = gst_pad_get_element_private (pad);
       
   171   g_return_val_if_fail (enc, FALSE);
       
   172 
       
   173   GST_LOG_OBJECT (enc->element, "received %s", GST_EVENT_TYPE_NAME (event));
       
   174 
       
   175   switch (GST_EVENT_TYPE (event)) {
       
   176     case GST_EVENT_FLUSH_STOP:
       
   177       /* fall-through */
       
   178     case GST_EVENT_EOS:
       
   179       gst_adapter_clear (enc->adapter);
       
   180       enc->next_ts = GST_CLOCK_TIME_NONE;
       
   181       break;
       
   182     default:
       
   183       break;
       
   184   }
       
   185 
       
   186   return gst_pad_event_default (pad, event);
       
   187 }
       
   188 
       
   189 #else
       
   190 /* included part */
       
   191 
       
   192 /* parameters:
       
   193      RAW_FRAME_SIZE
       
   194      CODEC_FRAME_SIZE
       
   195      FRAME_DURATION
       
   196      AUDIO_SAMPLE_RATE (optional)
       
   197    callback:
       
   198      codec_get_data(enc, in, out, dtx)
       
   199 
       
   200    If one does not mind a few cycles, include'ing can also be replaced by
       
   201    a regular include & call, at the expense of some additional parameters
       
   202    passed some way or another.
       
   203 */
       
   204 
       
   205 #ifndef AUDIO_SAMPLE_RATE
       
   206 #define AUDIO_SAMPLE_RATE   (8000)
       
   207 #endif
       
   208 
       
   209 /* quite some conditional stuff;
       
   210  * the (ugly?) cost of trying to stay inner loop optimal */
       
   211 
       
   212 static GstFlowReturn
       
   213 gst_framed_audio_enc_chain (GstFramedAudioEnc * enc, GstBuffer * buf,
       
   214     GstPad * srcpad, GstPad ** _cnpad)
       
   215 {
       
   216   GstFlowReturn ret = GST_FLOW_OK;
       
   217   GstBuffer *obuf = NULL;
       
   218 #ifdef CN_PAD
       
   219   GstBuffer *cnbuf = NULL;
       
   220   GstPad *cnpad = NULL;
       
   221 #endif
       
   222   gboolean discont = FALSE;
       
   223   const guint8 *data;
       
   224   guint8 *odata;
       
   225   gint av, flush, osize;
       
   226 
       
   227 #ifdef CN_PAD
       
   228   GST_OBJECT_LOCK (enc->element);
       
   229   if (_cnpad)
       
   230     cnpad = *_cnpad;
       
   231   if (cnpad)
       
   232     gst_object_ref (cnpad);
       
   233   GST_OBJECT_UNLOCK (enc->element);
       
   234 #endif
       
   235 
       
   236   if (G_LIKELY (buf)) {
       
   237     GST_LOG_OBJECT (enc->element, "input buffer of size %d with ts: %"
       
   238         GST_TIME_FORMAT, GST_BUFFER_SIZE (buf),
       
   239         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
       
   240     discont = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT);
       
   241 
       
   242     /* reposition to the new buffer's timestamp,
       
   243      * while correcting for some minor left-over */
       
   244     if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
       
   245       if (GST_CLOCK_TIME_IS_VALID (enc->next_ts)) {
       
   246         GstClockTimeDiff diff, limit;
       
   247         GstClockTime tleft;
       
   248 
       
   249         tleft = GST_FRAMES_TO_CLOCK_TIME
       
   250             (gst_adapter_available (enc->adapter) / 2, AUDIO_SAMPLE_RATE);
       
   251         diff =
       
   252             GST_CLOCK_DIFF (enc->next_ts + tleft, GST_BUFFER_TIMESTAMP (buf));
       
   253         limit = GST_SECOND / AUDIO_SAMPLE_RATE / 2;
       
   254         /* try for a perfect stream if possible, do not act on rounding errors */
       
   255         if (diff > limit || diff < -limit) {
       
   256           enc->next_ts = GST_BUFFER_TIMESTAMP (buf);
       
   257           if (enc->next_ts > tleft)
       
   258             enc->next_ts -= tleft;
       
   259           GST_LOG_OBJECT (enc->element, "marking discont based on timestamps");
       
   260           discont = TRUE;
       
   261         }
       
   262       } else
       
   263         enc->next_ts = GST_BUFFER_TIMESTAMP (buf);
       
   264     }
       
   265 
       
   266     gst_adapter_push (enc->adapter, buf);
       
   267     buf = NULL;
       
   268   }
       
   269 
       
   270   av = gst_adapter_available (enc->adapter);
       
   271   if (G_UNLIKELY (av < RAW_FRAME_SIZE))
       
   272     goto done;
       
   273 
       
   274   data = gst_adapter_peek (enc->adapter, av);
       
   275   obuf = gst_buffer_new_and_alloc (av / RAW_FRAME_SIZE * CODEC_FRAME_SIZE);
       
   276   odata = GST_BUFFER_DATA (obuf);
       
   277   osize = 0;
       
   278   flush = 0;
       
   279 
       
   280   while (TRUE) {
       
   281     gint esize;
       
   282 #ifdef CN_PAD
       
   283     GstDtxDecision dtx;
       
   284 
       
   285     /* safe default to start with, should get set */
       
   286     dtx = GST_DTX_DECISION_VOICE;
       
   287     esize = codec_get_data (enc->element, data + flush, odata, &dtx);
       
   288 #else
       
   289     esize = codec_get_data (enc->element, data + flush, odata, NULL);
       
   290 #endif
       
   291 
       
   292     if (G_UNLIKELY (esize < 0))
       
   293       goto encode_failed;
       
   294 
       
   295 #ifdef CN_PAD
       
   296     /* cn in a separate stream */
       
   297     switch (dtx) {
       
   298       case GST_DTX_DECISION_VOICE:
       
   299 #endif
       
   300         flush += RAW_FRAME_SIZE;
       
   301         av -= RAW_FRAME_SIZE;
       
   302 
       
   303         odata += esize;
       
   304         osize += esize;
       
   305 #ifdef CN_PAD
       
   306         break;
       
   307       case GST_DTX_DECISION_SID_UPDATE:
       
   308         GST_LOG_OBJECT (enc->element, "dtx: SID_UPDATE %d", esize);
       
   309         /* if already data before, need to put SID data separately */
       
   310         if (G_UNLIKELY (osize)) {
       
   311           cnbuf = gst_buffer_new_and_alloc (esize);
       
   312           memcpy (GST_BUFFER_DATA (cnbuf), data + osize, esize);
       
   313         } else {
       
   314           cnbuf = obuf;
       
   315           obuf = NULL;
       
   316         }
       
   317         /* and send one or both */
       
   318         goto send;
       
   319         break;
       
   320       case GST_DTX_DECISION_SID_NONE:
       
   321         GST_LOG_OBJECT (enc->element, "dtx: SID_NONE %d", esize);
       
   322         /* maybe send preceding voice, if any */
       
   323         goto send;
       
   324         break;
       
   325     }
       
   326 #endif
       
   327 
       
   328 #ifdef CODEC_FRAME_VARIABLE
       
   329     /* flush output after insufficient data */
       
   330     if (av >= RAW_FRAME_SIZE)
       
   331       continue;
       
   332 #else
       
   333     /* ... or some reduced (e.g. silence) frame */
       
   334     if (esize >= CODEC_FRAME_SIZE && av >= RAW_FRAME_SIZE)
       
   335       continue;
       
   336 #endif
       
   337 
       
   338 #ifdef CN_PAD
       
   339   send:
       
   340 #endif
       
   341     /* maybe a silent discarded frame */
       
   342     if (G_LIKELY (osize)) {
       
   343       GST_BUFFER_SIZE (obuf) = osize;
       
   344       GST_BUFFER_DURATION (obuf)
       
   345           = FRAME_DURATION * (flush / RAW_FRAME_SIZE);
       
   346       GST_BUFFER_TIMESTAMP (obuf) = enc->next_ts;
       
   347       if (G_UNLIKELY (discont)) {
       
   348         GST_BUFFER_FLAG_SET (obuf, GST_BUFFER_FLAG_DISCONT);
       
   349         discont = FALSE;
       
   350       }
       
   351 
       
   352       GST_LOG_OBJECT (enc->element,
       
   353           "pushing buffer of size %d with ts: %" GST_TIME_FORMAT,
       
   354           GST_BUFFER_SIZE (obuf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (obuf)));
       
   355       gst_buffer_set_caps (obuf, GST_PAD_CAPS (srcpad));
       
   356       ret = gst_pad_push (srcpad, obuf);
       
   357       obuf = NULL;
       
   358     } else {
       
   359       ret = GST_FLOW_OK;
       
   360     }
       
   361 
       
   362 #ifdef CN_PAD
       
   363     /* check for stuff to send on cn pad */
       
   364     if (cnbuf && cnpad) {
       
   365       /* only at most 1 SID update per buffer */
       
   366       GST_BUFFER_SIZE (cnbuf) = esize;
       
   367       GST_BUFFER_DURATION (cnbuf) = FRAME_DURATION;
       
   368       GST_BUFFER_TIMESTAMP (cnbuf) = enc->next_ts;
       
   369 
       
   370       GST_LOG_OBJECT (enc->element,
       
   371           "pushing cn buffer of size %d with ts: %" GST_TIME_FORMAT,
       
   372           GST_BUFFER_SIZE (cnbuf),
       
   373           GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (cnbuf)));
       
   374       gst_buffer_set_caps (cnbuf, GST_PAD_CAPS (cnpad));
       
   375       if (G_LIKELY (ret == GST_FLOW_OK)) {
       
   376         ret = gst_pad_push (cnpad, cnbuf);
       
   377         /* cn pad may not be linked */
       
   378         if (G_UNLIKELY (ret == GST_FLOW_NOT_LINKED))
       
   379           ret = GST_FLOW_OK;
       
   380       } else
       
   381         gst_pad_push (cnpad, cnbuf);
       
   382       cnbuf = NULL;
       
   383     } else if (G_UNLIKELY (cnbuf)) {
       
   384       /* should not occur */
       
   385       gst_buffer_unref (cnbuf);
       
   386       cnbuf = NULL;
       
   387     }
       
   388 
       
   389     if (dtx != GST_DTX_DECISION_VOICE) {
       
   390       /* still need to count non-voice encoded frame */
       
   391       flush += RAW_FRAME_SIZE;
       
   392       av -= RAW_FRAME_SIZE;
       
   393     }
       
   394 #endif /* CN_PAD */
       
   395 
       
   396     /* remove used part */
       
   397     gst_adapter_flush (enc->adapter, flush);
       
   398     if (GST_CLOCK_TIME_IS_VALID (enc->next_ts))
       
   399       enc->next_ts += FRAME_DURATION * (flush / RAW_FRAME_SIZE);
       
   400 
       
   401     /* end if insufficient left or error */
       
   402     if (av < RAW_FRAME_SIZE || ret != GST_FLOW_OK)
       
   403       break;
       
   404 
       
   405     /* allocate new buffer */
       
   406     if (!obuf) {
       
   407       obuf = gst_buffer_new_and_alloc (av / RAW_FRAME_SIZE * CODEC_FRAME_SIZE);
       
   408       odata = GST_BUFFER_DATA (obuf);
       
   409       osize = 0;
       
   410     }
       
   411     /* and prepare to consume again */
       
   412     data = gst_adapter_peek (enc->adapter, av);
       
   413     flush = 0;
       
   414   }
       
   415 
       
   416   if (!av) {
       
   417     enc->next_ts = GST_CLOCK_TIME_NONE;
       
   418   }
       
   419 
       
   420 done:
       
   421 #ifdef CN_PAD
       
   422   GST_OBJECT_LOCK (enc->element);
       
   423   if (cnpad)
       
   424     gst_object_unref (cnpad);
       
   425   GST_OBJECT_UNLOCK (enc->element);
       
   426 #endif
       
   427 
       
   428   return ret;
       
   429 
       
   430   /* ERRORS */
       
   431 encode_failed:
       
   432   {
       
   433     GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL), (NULL));
       
   434     ret = GST_FLOW_ERROR;
       
   435     gst_buffer_unref (obuf);
       
   436     goto done;
       
   437   }
       
   438 }
       
   439 #endif