static GstFlowReturn gst_ffmpegdeinterlace_chain (GstPad * pad, GstBuffer * inbuf) { GstFFMpegDeinterlace *deinterlace = GST_FFMPEGDEINTERLACE (gst_pad_get_parent (pad)); GstBuffer *outbuf = NULL; GstFlowReturn result; result = gst_pad_alloc_buffer (deinterlace->srcpad, GST_BUFFER_OFFSET_NONE, deinterlace->to_size, GST_PAD_CAPS (deinterlace->srcpad), &outbuf); if (result == GST_FLOW_OK) { gst_ffmpeg_avpicture_fill (&deinterlace->from_frame, GST_BUFFER_DATA (inbuf), deinterlace->pixfmt, deinterlace->width, deinterlace->height); gst_ffmpeg_avpicture_fill (&deinterlace->to_frame, GST_BUFFER_DATA (outbuf), deinterlace->pixfmt, deinterlace->width, deinterlace->height); avpicture_deinterlace (&deinterlace->to_frame, &deinterlace->from_frame, deinterlace->pixfmt, deinterlace->width, deinterlace->height); gst_buffer_copy_metadata (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS); result = gst_pad_push (deinterlace->srcpad, outbuf); } gst_buffer_unref (inbuf); return result; }
static GstFlowReturn gst_ffmpegdeinterlace_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) { GstFFMpegDeinterlace *deinterlace = GST_FFMPEGDEINTERLACE (parent); GstBuffer *outbuf = NULL; GstFlowReturn result; GstMapInfo from_map, to_map; GST_OBJECT_LOCK (deinterlace); if (deinterlace->reconfigure) { if (deinterlace->new_mode != -1) deinterlace->mode = deinterlace->new_mode; deinterlace->new_mode = -1; deinterlace->reconfigure = FALSE; GST_OBJECT_UNLOCK (deinterlace); if (gst_pad_has_current_caps (deinterlace->srcpad)) { GstCaps *caps; caps = gst_pad_get_current_caps (deinterlace->sinkpad); gst_ffmpegdeinterlace_sink_setcaps (deinterlace->sinkpad, caps); gst_caps_unref (caps); } } else { GST_OBJECT_UNLOCK (deinterlace); } if (deinterlace->passthrough) return gst_pad_push (deinterlace->srcpad, inbuf); outbuf = gst_buffer_new_and_alloc (deinterlace->to_size); gst_buffer_map (inbuf, &from_map, GST_MAP_READ); gst_ffmpeg_avpicture_fill (&deinterlace->from_frame, from_map.data, deinterlace->pixfmt, deinterlace->width, deinterlace->height); gst_buffer_map (outbuf, &to_map, GST_MAP_WRITE); gst_ffmpeg_avpicture_fill (&deinterlace->to_frame, to_map.data, deinterlace->pixfmt, deinterlace->width, deinterlace->height); avpicture_deinterlace (&deinterlace->to_frame, &deinterlace->from_frame, deinterlace->pixfmt, deinterlace->width, deinterlace->height); gst_buffer_unmap (outbuf, &to_map); gst_buffer_unmap (inbuf, &from_map); gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1); result = gst_pad_push (deinterlace->srcpad, outbuf); gst_buffer_unref (inbuf); return result; }
static GstFlowReturn gst_ffmpegdeinterlace_chain (GstPad * pad, GstBuffer * inbuf) { GstFFMpegDeinterlace *deinterlace = GST_FFMPEGDEINTERLACE (gst_pad_get_parent (pad)); GstBuffer *outbuf = NULL; GstFlowReturn result; GST_OBJECT_LOCK (deinterlace); if (deinterlace->reconfigure) { if (deinterlace->new_mode != -1) deinterlace->mode = deinterlace->new_mode; deinterlace->new_mode = -1; deinterlace->reconfigure = FALSE; GST_OBJECT_UNLOCK (deinterlace); if (GST_PAD_CAPS (deinterlace->srcpad)) gst_ffmpegdeinterlace_sink_setcaps (deinterlace->sinkpad, GST_PAD_CAPS (deinterlace->sinkpad)); } else { GST_OBJECT_UNLOCK (deinterlace); } if (deinterlace->passthrough) return gst_pad_push (deinterlace->srcpad, inbuf); result = gst_pad_alloc_buffer (deinterlace->srcpad, GST_BUFFER_OFFSET_NONE, deinterlace->to_size, GST_PAD_CAPS (deinterlace->srcpad), &outbuf); if (result == GST_FLOW_OK) { gst_ffmpeg_avpicture_fill (&deinterlace->from_frame, GST_BUFFER_DATA (inbuf), deinterlace->pixfmt, deinterlace->width, deinterlace->height); gst_ffmpeg_avpicture_fill (&deinterlace->to_frame, GST_BUFFER_DATA (outbuf), deinterlace->pixfmt, deinterlace->width, deinterlace->height); avpicture_deinterlace (&deinterlace->to_frame, &deinterlace->from_frame, deinterlace->pixfmt, deinterlace->width, deinterlace->height); gst_buffer_copy_metadata (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS); result = gst_pad_push (deinterlace->srcpad, outbuf); } gst_buffer_unref (inbuf); return result; }
static GstFlowReturn gst_ffmpegscale_transform (GstBaseTransform * trans, GstBuffer * inbuf, GstBuffer * outbuf) { GstFFMpegScale *scale = GST_FFMPEGSCALE (trans); AVPicture in_frame, out_frame; gst_buffer_copy_metadata (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS); gst_ffmpeg_avpicture_fill (&in_frame, GST_BUFFER_DATA (inbuf), scale->pixfmt, scale->in_width, scale->in_height); gst_ffmpeg_avpicture_fill (&out_frame, GST_BUFFER_DATA (outbuf), scale->pixfmt, scale->out_width, scale->out_height); img_resample (scale->res, &out_frame, &in_frame); return GST_FLOW_OK; }
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; }
static GstFlowReturn gst_ffmpegenc_chain_video (GstPad * pad, GstBuffer * inbuf) { GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) (GST_PAD_PARENT (pad)); GstBuffer *outbuf; gint ret_size = 0, frame_size; gboolean force_keyframe; GST_DEBUG_OBJECT (ffmpegenc, "Received buffer of time %" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf))); GST_OBJECT_LOCK (ffmpegenc); force_keyframe = ffmpegenc->force_keyframe; ffmpegenc->force_keyframe = FALSE; GST_OBJECT_UNLOCK (ffmpegenc); if (force_keyframe) ffmpegenc->picture->pict_type = FF_I_TYPE; frame_size = gst_ffmpeg_avpicture_fill ((AVPicture *) ffmpegenc->picture, GST_BUFFER_DATA (inbuf), ffmpegenc->context->pix_fmt, ffmpegenc->context->width, ffmpegenc->context->height); g_return_val_if_fail (frame_size == GST_BUFFER_SIZE (inbuf), GST_FLOW_ERROR); ffmpegenc->picture->pts = gst_ffmpeg_time_gst_to_ff (GST_BUFFER_TIMESTAMP (inbuf) / ffmpegenc->context->ticks_per_frame, ffmpegenc->context->time_base); ffmpegenc_setup_working_buf (ffmpegenc); ret_size = avcodec_encode_video (ffmpegenc->context, ffmpegenc->working_buf, ffmpegenc->working_buf_size, ffmpegenc->picture); if (ret_size < 0) { #ifndef GST_DISABLE_GST_DEBUG GstFFMpegEncClass *oclass = (GstFFMpegEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc)); GST_ERROR_OBJECT (ffmpegenc, "ffenc_%s: failed to encode buffer", oclass->in_plugin->name); #endif /* GST_DISABLE_GST_DEBUG */ gst_buffer_unref (inbuf); return GST_FLOW_OK; } /* handle b-frame delay when no output, so we don't output empty frames; * timestamps and so can permute a bit between coding and display order * but keyframes should still end up with the proper metadata */ g_queue_push_tail (ffmpegenc->delay, inbuf); if (ret_size) inbuf = g_queue_pop_head (ffmpegenc->delay); else return GST_FLOW_OK; /* save stats info if there is some as well as a stats file */ if (ffmpegenc->file && ffmpegenc->context->stats_out) if (fprintf (ffmpegenc->file, "%s", ffmpegenc->context->stats_out) < 0) GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, WRITE, (("Could not write to file \"%s\"."), ffmpegenc->filename), GST_ERROR_SYSTEM); outbuf = gst_buffer_new_and_alloc (ret_size); memcpy (GST_BUFFER_DATA (outbuf), ffmpegenc->working_buf, ret_size); GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf); GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf); /* buggy codec may not set coded_frame */ if (ffmpegenc->context->coded_frame) { if (!ffmpegenc->context->coded_frame->key_frame) GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); } else GST_WARNING_OBJECT (ffmpegenc, "codec did not provide keyframe info"); gst_buffer_set_caps (outbuf, GST_PAD_CAPS (ffmpegenc->srcpad)); gst_buffer_unref (inbuf); /* Reset frame type */ if (ffmpegenc->picture->pict_type) ffmpegenc->picture->pict_type = 0; if (force_keyframe) { gst_pad_push_event (ffmpegenc->srcpad, gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, gst_structure_new ("GstForceKeyUnit", "timestamp", G_TYPE_UINT64, GST_BUFFER_TIMESTAMP (outbuf), NULL))); } return gst_pad_push (ffmpegenc->srcpad, outbuf); }