static GstBuffer * mpegpsmux_queue_buffer_for_stream (MpegPsMux * mux, MpegPsPadData * ps_data) { GstCollectData *c_data = (GstCollectData *) ps_data; GstBuffer *buf; g_assert (ps_data->queued.buf == NULL); buf = gst_collect_pads_peek (mux->collect, c_data); if (buf == NULL) return NULL; ps_data->queued.buf = buf; /* do any raw -> byte-stream format conversions (e.g. for H.264, AAC) */ if (ps_data->prepare_func) { buf = ps_data->prepare_func (buf, ps_data, mux); if (buf) { /* Take the prepared buffer instead */ gst_buffer_unref (ps_data->queued.buf); ps_data->queued.buf = buf; } else { /* If data preparation returned NULL, use unprepared one */ buf = ps_data->queued.buf; } } ps_data->queued.pts = GST_BUFFER_PTS (buf); if (GST_CLOCK_TIME_IS_VALID (ps_data->queued.pts)) { ps_data->queued.pts = gst_segment_to_running_time (&c_data->segment, GST_FORMAT_TIME, ps_data->queued.pts); } ps_data->queued.dts = GST_BUFFER_DTS (buf); if (GST_CLOCK_TIME_IS_VALID (ps_data->queued.dts)) { ps_data->queued.dts = gst_segment_to_running_time (&c_data->segment, GST_FORMAT_TIME, ps_data->queued.dts); } if (GST_BUFFER_PTS_IS_VALID (buf) && GST_BUFFER_DTS_IS_VALID (buf)) { ps_data->queued.ts = MIN (ps_data->queued.dts, ps_data->queued.pts); } else if (GST_BUFFER_PTS_IS_VALID (buf) && !GST_BUFFER_DTS_IS_VALID (buf)) { ps_data->queued.ts = ps_data->queued.pts; } else if (GST_BUFFER_DTS_IS_VALID (buf) && !GST_BUFFER_PTS_IS_VALID (buf)) { GST_WARNING_OBJECT (c_data->pad, "got DTS without PTS"); ps_data->queued.ts = ps_data->queued.dts; } else { ps_data->queued.ts = GST_CLOCK_TIME_NONE; } GST_DEBUG_OBJECT (mux, "Queued buffer with ts %" GST_TIME_FORMAT ": " "uncorrected pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT ", " "buffer pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " for PID 0x%04x", GST_TIME_ARGS (ps_data->queued.ts), GST_TIME_ARGS (GST_BUFFER_PTS (buf)), GST_TIME_ARGS (GST_BUFFER_DTS (buf)), GST_TIME_ARGS (ps_data->queued.pts), GST_TIME_ARGS (ps_data->queued.dts), ps_data->stream_id); return buf; }
static MpegTsPadData * mpegtsmux_choose_best_stream (MpegTsMux * mux) { MpegTsPadData *best = NULL; GstCollectData *c_best = NULL; GSList *walk; for (walk = mux->collect->data; walk != NULL; walk = g_slist_next (walk)) { GstCollectData *c_data = (GstCollectData *) walk->data; MpegTsPadData *ts_data = (MpegTsPadData *) walk->data; if (ts_data->eos == FALSE) { if (ts_data->queued_buf == NULL) { GstBuffer *buf; ts_data->queued_buf = buf = gst_collect_pads_peek (mux->collect, c_data); if (buf != NULL) { if (ts_data->prepare_func) { buf = ts_data->prepare_func (buf, ts_data, mux); if (buf) { /* Take the prepared buffer instead */ gst_buffer_unref (ts_data->queued_buf); ts_data->queued_buf = buf; } else { /* If data preparation returned NULL, use unprepared one */ buf = ts_data->queued_buf; } } if (GST_BUFFER_TIMESTAMP (buf) != GST_CLOCK_TIME_NONE) { /* Ignore timestamps that go backward for now. FIXME: Handle all * incoming PTS */ if (ts_data->last_ts == GST_CLOCK_TIME_NONE || ts_data->last_ts < GST_BUFFER_TIMESTAMP (buf)) { ts_data->cur_ts = ts_data->last_ts = gst_segment_to_running_time (&c_data->segment, GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buf)); } else { GST_DEBUG_OBJECT (mux, "Ignoring PTS that has gone backward"); } } else ts_data->cur_ts = GST_CLOCK_TIME_NONE; GST_DEBUG_OBJECT (mux, "Pulled buffer with ts %" GST_TIME_FORMAT " (uncorrected ts %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT ") for PID 0x%04x", GST_TIME_ARGS (ts_data->cur_ts), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_TIMESTAMP (buf), ts_data->pid); /* Choose a stream we've never seen a timestamp for to ensure * we push enough buffers from it to reach a timestamp */ if (ts_data->last_ts == GST_CLOCK_TIME_NONE) { best = ts_data; c_best = c_data; } } else { ts_data->eos = TRUE; continue; } } /* If we don't yet have a best pad, take this one, otherwise take * whichever has the oldest timestamp */ if (best != NULL) { if (ts_data->last_ts != GST_CLOCK_TIME_NONE && best->last_ts != GST_CLOCK_TIME_NONE && ts_data->last_ts < best->last_ts) { best = ts_data; c_best = c_data; } } else { best = ts_data; c_best = c_data; } } } if (c_best) { gst_buffer_unref (gst_collect_pads_pop (mux->collect, c_best)); } return best; }
static GstFlowReturn gst_ffmpegmux_collected (GstCollectPads * pads, gpointer user_data) { GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) user_data; GSList *collected; GstFFMpegMuxPad *best_pad; GstClockTime best_time; const GstTagList *tags; /* open "file" (gstreamer protocol to next element) */ if (!ffmpegmux->opened) { int open_flags = URL_WRONLY; /* we do need all streams to have started capsnego, * or things will go horribly wrong */ for (collected = ffmpegmux->collect->data; collected; collected = g_slist_next (collected)) { GstFFMpegMuxPad *collect_pad = (GstFFMpegMuxPad *) collected->data; AVStream *st = ffmpegmux->context->streams[collect_pad->padnum]; /* check whether the pad has successfully completed capsnego */ if (st->codec->codec_id == CODEC_ID_NONE) { GST_ELEMENT_ERROR (ffmpegmux, CORE, NEGOTIATION, (NULL), ("no caps set on stream %d (%s)", collect_pad->padnum, (st->codec->codec_type == CODEC_TYPE_VIDEO) ? "video" : "audio")); return GST_FLOW_ERROR; } /* set framerate for audio */ if (st->codec->codec_type == CODEC_TYPE_AUDIO) { switch (st->codec->codec_id) { case CODEC_ID_PCM_S16LE: case CODEC_ID_PCM_S16BE: case CODEC_ID_PCM_U16LE: case CODEC_ID_PCM_U16BE: case CODEC_ID_PCM_S8: case CODEC_ID_PCM_U8: st->codec->frame_size = 1; break; default: { GstBuffer *buffer; /* FIXME : This doesn't work for RAW AUDIO... * in fact I'm wondering if it even works for any kind of audio... */ buffer = gst_collect_pads_peek (ffmpegmux->collect, (GstCollectData *) collect_pad); if (buffer) { st->codec->frame_size = st->codec->sample_rate * GST_BUFFER_DURATION (buffer) / GST_SECOND; gst_buffer_unref (buffer); } } } } } /* tags */ tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (ffmpegmux)); if (tags) { gint i; gchar *s; /* get the interesting ones */ if (gst_tag_list_get_string (tags, GST_TAG_TITLE, &s)) { strncpy (ffmpegmux->context->title, s, sizeof (ffmpegmux->context->title)); } if (gst_tag_list_get_string (tags, GST_TAG_ARTIST, &s)) { strncpy (ffmpegmux->context->author, s, sizeof (ffmpegmux->context->author)); } if (gst_tag_list_get_string (tags, GST_TAG_COPYRIGHT, &s)) { strncpy (ffmpegmux->context->copyright, s, sizeof (ffmpegmux->context->copyright)); } if (gst_tag_list_get_string (tags, GST_TAG_COMMENT, &s)) { strncpy (ffmpegmux->context->comment, s, sizeof (ffmpegmux->context->comment)); } if (gst_tag_list_get_string (tags, GST_TAG_ALBUM, &s)) { strncpy (ffmpegmux->context->album, s, sizeof (ffmpegmux->context->album)); } if (gst_tag_list_get_string (tags, GST_TAG_GENRE, &s)) { strncpy (ffmpegmux->context->genre, s, sizeof (ffmpegmux->context->genre)); } if (gst_tag_list_get_int (tags, GST_TAG_TRACK_NUMBER, &i)) { ffmpegmux->context->track = i; } } /* set the streamheader flag for gstffmpegprotocol if codec supports it */ if (!strcmp (ffmpegmux->context->oformat->name, "flv")) { open_flags |= GST_FFMPEG_URL_STREAMHEADER; } if (url_fopen (&ffmpegmux->context->pb, ffmpegmux->context->filename, open_flags) < 0) { GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, TOO_LAZY, (NULL), ("Failed to open stream context in ffmux")); return GST_FLOW_ERROR; } if (av_set_parameters (ffmpegmux->context, NULL) < 0) { GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, INIT, (NULL), ("Failed to initialize muxer")); return GST_FLOW_ERROR; } /* now open the mux format */ if (av_write_header (ffmpegmux->context) < 0) { GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, SETTINGS, (NULL), ("Failed to write file header - check codec settings")); return GST_FLOW_ERROR; } /* we're now opened */ ffmpegmux->opened = TRUE; /* flush the header so it will be used as streamheader */ put_flush_packet (ffmpegmux->context->pb); } /* take the one with earliest timestamp, * and push it forward */ best_pad = NULL; best_time = GST_CLOCK_TIME_NONE; for (collected = ffmpegmux->collect->data; collected; collected = g_slist_next (collected)) { GstFFMpegMuxPad *collect_pad = (GstFFMpegMuxPad *) collected->data; GstBuffer *buffer = gst_collect_pads_peek (ffmpegmux->collect, (GstCollectData *) collect_pad); /* if there's no buffer, just continue */ if (buffer == NULL) { continue; } /* if we have no buffer yet, just use the first one */ if (best_pad == NULL) { best_pad = collect_pad; best_time = GST_BUFFER_TIMESTAMP (buffer); goto next_pad; } /* if we do have one, only use this one if it's older */ if (GST_BUFFER_TIMESTAMP (buffer) < best_time) { best_time = GST_BUFFER_TIMESTAMP (buffer); best_pad = collect_pad; } next_pad: gst_buffer_unref (buffer); /* Mux buffers with invalid timestamp first */ if (!GST_CLOCK_TIME_IS_VALID (best_time)) break; } /* now handle the buffer, or signal EOS if we have * no buffers left */ if (best_pad != NULL) { GstBuffer *buf; AVPacket pkt; gboolean need_free = FALSE; /* push out current buffer */ buf = gst_collect_pads_pop (ffmpegmux->collect, (GstCollectData *) best_pad); ffmpegmux->context->streams[best_pad->padnum]->codec->frame_number++; /* set time */ pkt.pts = gst_ffmpeg_time_gst_to_ff (GST_BUFFER_TIMESTAMP (buf), ffmpegmux->context->streams[best_pad->padnum]->time_base); pkt.dts = pkt.pts; if (strcmp (ffmpegmux->context->oformat->name, "gif") == 0) { AVStream *st = ffmpegmux->context->streams[best_pad->padnum]; AVPicture src, dst; need_free = TRUE; pkt.size = st->codec->width * st->codec->height * 3; pkt.data = g_malloc (pkt.size); dst.data[0] = pkt.data; dst.data[1] = NULL; dst.data[2] = NULL; dst.linesize[0] = st->codec->width * 3; gst_ffmpeg_avpicture_fill (&src, GST_BUFFER_DATA (buf), PIX_FMT_RGB24, st->codec->width, st->codec->height); av_picture_copy (&dst, &src, PIX_FMT_RGB24, st->codec->width, st->codec->height); } else { pkt.data = GST_BUFFER_DATA (buf); pkt.size = GST_BUFFER_SIZE (buf); } pkt.stream_index = best_pad->padnum; pkt.flags = 0; if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) pkt.flags |= PKT_FLAG_KEY; if (GST_BUFFER_DURATION_IS_VALID (buf)) pkt.duration = gst_ffmpeg_time_gst_to_ff (GST_BUFFER_DURATION (buf), ffmpegmux->context->streams[best_pad->padnum]->time_base); else pkt.duration = 0; av_write_frame (ffmpegmux->context, &pkt); gst_buffer_unref (buf); if (need_free) g_free (pkt.data); } else { /* close down */ av_write_trailer (ffmpegmux->context); ffmpegmux->opened = FALSE; put_flush_packet (ffmpegmux->context->pb); url_fclose (ffmpegmux->context->pb); gst_pad_push_event (ffmpegmux->srcpad, gst_event_new_eos ()); return GST_FLOW_UNEXPECTED; } return GST_FLOW_OK; }