static gboolean gst_vorbis_enc_sink_setcaps (GstPad * pad, GstCaps * caps) { GstVorbisEnc *vorbisenc; GstStructure *structure; vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad)); vorbisenc->setup = FALSE; structure = gst_caps_get_structure (caps, 0); gst_structure_get_int (structure, "channels", &vorbisenc->channels); gst_structure_get_int (structure, "rate", &vorbisenc->frequency); gst_vorbis_enc_setup (vorbisenc); if (vorbisenc->setup) return TRUE; return FALSE; }
static gboolean gst_vorbis_enc_set_format (GstAudioEncoder * enc, GstAudioInfo * info) { GstVorbisEnc *vorbisenc; vorbisenc = GST_VORBISENC (enc); vorbisenc->channels = GST_AUDIO_INFO_CHANNELS (info); vorbisenc->frequency = GST_AUDIO_INFO_RATE (info); /* if re-configured, we were drained and cleared already */ if (!gst_vorbis_enc_setup (vorbisenc)) return FALSE; /* feedback to base class */ gst_audio_encoder_set_latency (enc, gst_vorbis_enc_get_latency (vorbisenc), gst_vorbis_enc_get_latency (vorbisenc)); return TRUE; }
static GstFlowReturn gst_vorbis_enc_handle_frame (GstAudioEncoder * enc, GstBuffer * buffer) { GstVorbisEnc *vorbisenc; GstFlowReturn ret = GST_FLOW_OK; GstMapInfo map; gfloat *ptr; gulong size; gulong i, j; float **vorbis_buffer; GstBuffer *buf1, *buf2, *buf3; vorbisenc = GST_VORBISENC (enc); if (G_UNLIKELY (!vorbisenc->setup)) { if (buffer) { GST_DEBUG_OBJECT (vorbisenc, "forcing setup"); /* should not fail, as setup before same way */ if (!gst_vorbis_enc_setup (vorbisenc)) return GST_FLOW_ERROR; } else { /* end draining */ GST_LOG_OBJECT (vorbisenc, "already drained"); return GST_FLOW_OK; } } if (!vorbisenc->header_sent) { /* Vorbis streams begin with three headers; the initial header (with most of the codec setup parameters) which is mandated by the Ogg bitstream spec. The second header holds any comment fields. The third header holds the bitstream codebook. We merely need to make the headers, then pass them to libvorbis one at a time; libvorbis handles the additional Ogg bitstream constraints */ ogg_packet header; ogg_packet header_comm; ogg_packet header_code; GstCaps *caps; GList *headers; GST_DEBUG_OBJECT (vorbisenc, "creating and sending header packets"); gst_vorbis_enc_set_metadata (vorbisenc); vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header, &header_comm, &header_code); vorbis_comment_clear (&vorbisenc->vc); /* create header buffers */ buf1 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header); buf2 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header_comm); buf3 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header_code); /* mark and put on caps */ caps = gst_caps_new_simple ("audio/x-vorbis", "rate", G_TYPE_INT, vorbisenc->frequency, "channels", G_TYPE_INT, vorbisenc->channels, NULL); caps = _gst_caps_set_buffer_array (caps, "streamheader", buf1, buf2, buf3, NULL); /* negotiate with these caps */ GST_DEBUG_OBJECT (vorbisenc, "here are the caps: %" GST_PTR_FORMAT, caps); gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (vorbisenc), caps); gst_caps_unref (caps); /* store buffers for later pre_push sending */ headers = NULL; GST_DEBUG_OBJECT (vorbisenc, "storing header buffers"); headers = g_list_prepend (headers, buf3); headers = g_list_prepend (headers, buf2); headers = g_list_prepend (headers, buf1); gst_audio_encoder_set_headers (enc, headers); vorbisenc->header_sent = TRUE; } if (!buffer) return gst_vorbis_enc_clear (vorbisenc); gst_buffer_map (buffer, &map, GST_MAP_WRITE); /* data to encode */ size = map.size / (vorbisenc->channels * sizeof (float)); ptr = (gfloat *) map.data; /* expose the buffer to submit data */ vorbis_buffer = vorbis_analysis_buffer (&vorbisenc->vd, size); /* deinterleave samples, write the buffer data */ if (vorbisenc->channels < 2 || vorbisenc->channels > 8) { for (i = 0; i < size; i++) { for (j = 0; j < vorbisenc->channels; j++) { vorbis_buffer[j][i] = *ptr++; } } } else { gint i, j; /* Reorder */ for (i = 0; i < size; i++) { for (j = 0; j < vorbisenc->channels; j++) { vorbis_buffer[gst_vorbis_reorder_map[vorbisenc->channels - 1][j]][i] = ptr[j]; } ptr += vorbisenc->channels; } } /* tell the library how much we actually submitted */ vorbis_analysis_wrote (&vorbisenc->vd, size); gst_buffer_unmap (buffer, &map); GST_LOG_OBJECT (vorbisenc, "wrote %lu samples to vorbis", size); ret = gst_vorbis_enc_output_buffers (vorbisenc); return ret; }
static GstFlowReturn gst_vorbis_enc_handle_frame (GstAudioEncoder * enc, GstBuffer * buffer) { GstVorbisEnc *vorbisenc; GstFlowReturn ret = GST_FLOW_OK; gfloat *data; gulong size; gulong i, j; float **vorbis_buffer; GstBuffer *buf1, *buf2, *buf3; vorbisenc = GST_VORBISENC (enc); if (G_UNLIKELY (!vorbisenc->setup)) { if (buffer) { GST_DEBUG_OBJECT (vorbisenc, "forcing setup"); /* should not fail, as setup before same way */ if (!gst_vorbis_enc_setup (vorbisenc)) return GST_FLOW_ERROR; } else { /* end draining */ GST_LOG_OBJECT (vorbisenc, "already drained"); return GST_FLOW_OK; } } if (!vorbisenc->header_sent) { /* Vorbis streams begin with three headers; the initial header (with most of the codec setup parameters) which is mandated by the Ogg bitstream spec. The second header holds any comment fields. The third header holds the bitstream codebook. We merely need to make the headers, then pass them to libvorbis one at a time; libvorbis handles the additional Ogg bitstream constraints */ ogg_packet header; ogg_packet header_comm; ogg_packet header_code; GstCaps *caps; GST_DEBUG_OBJECT (vorbisenc, "creating and sending header packets"); gst_vorbis_enc_set_metadata (vorbisenc); vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header, &header_comm, &header_code); vorbis_comment_clear (&vorbisenc->vc); /* create header buffers */ buf1 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header); buf2 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header_comm); buf3 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header_code); /* mark and put on caps */ caps = gst_caps_new_simple ("audio/x-vorbis", NULL); caps = _gst_caps_set_buffer_array (caps, "streamheader", buf1, buf2, buf3, NULL); /* negotiate with these caps */ GST_DEBUG_OBJECT (vorbisenc, "here are the caps: %" GST_PTR_FORMAT, caps); gst_buffer_set_caps (buf1, caps); gst_buffer_set_caps (buf2, caps); gst_buffer_set_caps (buf3, caps); gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (vorbisenc), caps); gst_caps_unref (caps); /* store buffers for later pre_push sending */ g_slist_foreach (vorbisenc->headers, (GFunc) gst_buffer_unref, NULL); vorbisenc->headers = NULL; GST_DEBUG_OBJECT (vorbisenc, "storing header buffers"); vorbisenc->headers = g_slist_prepend (vorbisenc->headers, buf3); vorbisenc->headers = g_slist_prepend (vorbisenc->headers, buf2); vorbisenc->headers = g_slist_prepend (vorbisenc->headers, buf1); vorbisenc->header_sent = TRUE; } if (!buffer) return gst_vorbis_enc_clear (vorbisenc); /* data to encode */ data = (gfloat *) GST_BUFFER_DATA (buffer); size = GST_BUFFER_SIZE (buffer) / (vorbisenc->channels * sizeof (float)); /* expose the buffer to submit data */ vorbis_buffer = vorbis_analysis_buffer (&vorbisenc->vd, size); /* deinterleave samples, write the buffer data */ for (i = 0; i < size; i++) { for (j = 0; j < vorbisenc->channels; j++) { vorbis_buffer[j][i] = *data++; } } /* tell the library how much we actually submitted */ vorbis_analysis_wrote (&vorbisenc->vd, size); GST_LOG_OBJECT (vorbisenc, "wrote %lu samples to vorbis", size); vorbisenc->samples_in += size; ret = gst_vorbis_enc_output_buffers (vorbisenc); return ret; }
static GstFlowReturn gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer) { GstVorbisEnc *vorbisenc; GstFlowReturn ret = GST_FLOW_OK; gfloat *data; gulong size; gulong i, j; float **vorbis_buffer; GstBuffer *buf1, *buf2, *buf3; gboolean first = FALSE; vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad)); if (!vorbisenc->setup) goto not_setup; if (!vorbisenc->header_sent) { /* Vorbis streams begin with three headers; the initial header (with most of the codec setup parameters) which is mandated by the Ogg bitstream spec. The second header holds any comment fields. The third header holds the bitstream codebook. We merely need to make the headers, then pass them to libvorbis one at a time; libvorbis handles the additional Ogg bitstream constraints */ ogg_packet header; ogg_packet header_comm; ogg_packet header_code; GstCaps *caps; /* first, make sure header buffers get timestamp == 0 */ vorbisenc->next_ts = 0; vorbisenc->granulepos_offset = 0; vorbisenc->subgranule_offset = 0; GST_DEBUG_OBJECT (vorbisenc, "creating and sending header packets"); gst_vorbis_enc_set_metadata (vorbisenc); vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header, &header_comm, &header_code); vorbis_comment_clear (&vorbisenc->vc); /* create header buffers */ buf1 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header); buf2 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header_comm); buf3 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header_code); /* mark and put on caps */ vorbisenc->srccaps = gst_caps_new_simple ("audio/x-vorbis", NULL); caps = vorbisenc->srccaps; caps = gst_vorbis_enc_set_header_on_caps (caps, buf1, buf2, buf3); /* negotiate with these caps */ GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps); gst_pad_set_caps (vorbisenc->srcpad, caps); gst_buffer_set_caps (buf1, caps); gst_buffer_set_caps (buf2, caps); gst_buffer_set_caps (buf3, caps); /* push out buffers */ /* push_buffer takes the reference even for failure */ if ((ret = gst_vorbis_enc_push_buffer (vorbisenc, buf1)) != GST_FLOW_OK) goto failed_header_push; if ((ret = gst_vorbis_enc_push_buffer (vorbisenc, buf2)) != GST_FLOW_OK) { buf2 = NULL; goto failed_header_push; } if ((ret = gst_vorbis_enc_push_buffer (vorbisenc, buf3)) != GST_FLOW_OK) { buf3 = NULL; goto failed_header_push; } /* now adjust starting granulepos accordingly if the buffer's timestamp is nonzero */ vorbisenc->next_ts = GST_BUFFER_TIMESTAMP (buffer); vorbisenc->expected_ts = GST_BUFFER_TIMESTAMP (buffer); vorbisenc->granulepos_offset = gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buffer), vorbisenc->frequency, GST_SECOND); vorbisenc->subgranule_offset = 0; vorbisenc->subgranule_offset = vorbisenc->next_ts - granulepos_to_timestamp_offset (vorbisenc, 0); vorbisenc->header_sent = TRUE; first = TRUE; } if (vorbisenc->expected_ts != GST_CLOCK_TIME_NONE && GST_BUFFER_TIMESTAMP (buffer) < vorbisenc->expected_ts) { GST_WARNING_OBJECT (vorbisenc, "Buffer is older than previous " "timestamp + duration (%" GST_TIME_FORMAT "< %" GST_TIME_FORMAT "), cannot handle. Dropping buffer.", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), GST_TIME_ARGS (vorbisenc->expected_ts)); gst_buffer_unref (buffer); return GST_FLOW_OK; } if (gst_vorbis_enc_buffer_check_discontinuous (vorbisenc, buffer) && !first) { GST_WARNING_OBJECT (vorbisenc, "Buffer is discontinuous, flushing encoder " "and restarting (Discont from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT ")", GST_TIME_ARGS (vorbisenc->next_ts), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer))); /* Re-initialise encoder (there's unfortunately no API to flush it) */ if ((ret = gst_vorbis_enc_clear (vorbisenc)) != GST_FLOW_OK) return ret; if (!gst_vorbis_enc_setup (vorbisenc)) return GST_FLOW_ERROR; /* Should be impossible, we can only get here if we successfully initialised earlier */ /* Now, set our granulepos offset appropriately. */ vorbisenc->next_ts = GST_BUFFER_TIMESTAMP (buffer); /* We need to round to the nearest whole number of samples, not just do * a truncating division here */ vorbisenc->granulepos_offset = gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buffer) + GST_SECOND / vorbisenc->frequency / 2 - vorbisenc->subgranule_offset, vorbisenc->frequency, GST_SECOND); vorbisenc->header_sent = TRUE; /* And our next output buffer must have DISCONT set on it */ vorbisenc->next_discont = TRUE; } /* Sending zero samples to libvorbis marks EOS, so we mustn't do that */ if (GST_BUFFER_SIZE (buffer) == 0) { gst_buffer_unref (buffer); return GST_FLOW_OK; } /* data to encode */ data = (gfloat *) GST_BUFFER_DATA (buffer); size = GST_BUFFER_SIZE (buffer) / (vorbisenc->channels * sizeof (float)); /* expose the buffer to submit data */ vorbis_buffer = vorbis_analysis_buffer (&vorbisenc->vd, size); /* deinterleave samples, write the buffer data */ for (i = 0; i < size; i++) { for (j = 0; j < vorbisenc->channels; j++) { vorbis_buffer[j][i] = *data++; } } /* tell the library how much we actually submitted */ vorbis_analysis_wrote (&vorbisenc->vd, size); vorbisenc->samples_in += size; gst_buffer_unref (buffer); ret = gst_vorbis_enc_output_buffers (vorbisenc); return ret; /* error cases */ not_setup: { gst_buffer_unref (buffer); GST_ELEMENT_ERROR (vorbisenc, CORE, NEGOTIATION, (NULL), ("encoder not initialized (input is not audio?)")); return GST_FLOW_UNEXPECTED; } failed_header_push: { GST_WARNING_OBJECT (vorbisenc, "Failed to push headers"); /* buf1 is always already unreffed */ if (buf2) gst_buffer_unref (buf2); if (buf3) gst_buffer_unref (buf3); gst_buffer_unref (buffer); return ret; } }