void SignalGenerator::scheduleNextFrame(bool prerolling) { IDeckLinkMutableVideoFrame *currentFrame; if (prerolling == false) { // If not prerolling, make sure that playback is still active if (running == false) return; } if (outputSignal == kOutputSignalPip) { if ((totalFramesScheduled % framesPerSecond) == 0) currentFrame = videoFrameBars; else currentFrame = videoFrameBlack; } else { if ((totalFramesScheduled % framesPerSecond) == 0) currentFrame = videoFrameBlack; else currentFrame = videoFrameBars; } printf("frames: %d\n", timeCode->frames()); currentFrame->SetTimecodeFromComponents(timeCodeFormat, timeCode->hours(), timeCode->minutes(), timeCode->seconds(), timeCode->frames(), bmdTimecodeFlagDefault); if (deckLinkOutput->ScheduleVideoFrame(currentFrame, (totalFramesScheduled * frameDuration), frameDuration, frameTimescale) != S_OK) goto out; totalFramesScheduled += 1; out: timeCode->update(); }
static GstFlowReturn gst_decklink_video_sink_prepare (GstBaseSink * bsink, GstBuffer * buffer) { GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (bsink); GstVideoFrame vframe; IDeckLinkMutableVideoFrame *frame; guint8 *outdata, *indata; GstFlowReturn flow_ret; HRESULT ret; GstClockTime timestamp, duration; GstClockTime running_time, running_time_duration; GstClockTime latency, render_delay; GstClockTimeDiff ts_offset; gint i; GstDecklinkVideoFormat caps_format; BMDPixelFormat format; gint bpp; GstVideoTimeCodeMeta *tc_meta; GST_DEBUG_OBJECT (self, "Preparing buffer %p", buffer); // FIXME: Handle no timestamps if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) { return GST_FLOW_ERROR; } caps_format = gst_decklink_type_from_video_format (self->info.finfo->format); format = gst_decklink_pixel_format_from_type (caps_format); bpp = gst_decklink_bpp_from_type (caps_format); timestamp = GST_BUFFER_TIMESTAMP (buffer); duration = GST_BUFFER_DURATION (buffer); if (duration == GST_CLOCK_TIME_NONE) { duration = gst_util_uint64_scale_int (GST_SECOND, self->info.fps_d, self->info.fps_n); } running_time = gst_segment_to_running_time (&GST_BASE_SINK_CAST (self)->segment, GST_FORMAT_TIME, timestamp); running_time_duration = gst_segment_to_running_time (&GST_BASE_SINK_CAST (self)->segment, GST_FORMAT_TIME, timestamp + duration) - running_time; /* See gst_base_sink_adjust_time() */ latency = gst_base_sink_get_latency (bsink); render_delay = gst_base_sink_get_render_delay (bsink); ts_offset = gst_base_sink_get_ts_offset (bsink); running_time += latency; if (ts_offset < 0) { ts_offset = -ts_offset; if ((GstClockTime) ts_offset < running_time) running_time -= ts_offset; else running_time = 0; } else { running_time += ts_offset; } if (running_time > render_delay) running_time -= render_delay; else running_time = 0; ret = self->output->output->CreateVideoFrame (self->info.width, self->info.height, self->info.stride[0], format, bmdFrameFlagDefault, &frame); if (ret != S_OK) { GST_ELEMENT_ERROR (self, STREAM, FAILED, (NULL), ("Failed to create video frame: 0x%08x", ret)); return GST_FLOW_ERROR; } if (!gst_video_frame_map (&vframe, &self->info, buffer, GST_MAP_READ)) { GST_ERROR_OBJECT (self, "Failed to map video frame"); flow_ret = GST_FLOW_ERROR; goto out; } frame->GetBytes ((void **) &outdata); indata = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0); for (i = 0; i < self->info.height; i++) { memcpy (outdata, indata, GST_VIDEO_FRAME_WIDTH (&vframe) * bpp); indata += GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0); outdata += frame->GetRowBytes (); } gst_video_frame_unmap (&vframe); tc_meta = gst_buffer_get_video_time_code_meta (buffer); if (tc_meta) { BMDTimecodeFlags bflags = (BMDTimecodeFlags) 0; gchar *tc_str; if (((GstVideoTimeCodeFlags) (tc_meta->tc. config.flags)) & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) bflags = (BMDTimecodeFlags) (bflags | bmdTimecodeIsDropFrame); else bflags = (BMDTimecodeFlags) (bflags | bmdTimecodeFlagDefault); if (tc_meta->tc.field_count == 2) bflags = (BMDTimecodeFlags) (bflags | bmdTimecodeFieldMark); tc_str = gst_video_time_code_to_string (&tc_meta->tc); ret = frame->SetTimecodeFromComponents (self->timecode_format, (uint8_t) tc_meta->tc.hours, (uint8_t) tc_meta->tc.minutes, (uint8_t) tc_meta->tc.seconds, (uint8_t) tc_meta->tc.frames, bflags); if (ret != S_OK) { GST_ERROR_OBJECT (self, "Failed to set timecode %s to video frame: 0x%08x", tc_str, ret); flow_ret = GST_FLOW_ERROR; g_free (tc_str); goto out; } GST_DEBUG_OBJECT (self, "Set frame timecode to %s", tc_str); g_free (tc_str); } convert_to_internal_clock (self, &running_time, &running_time_duration); GST_LOG_OBJECT (self, "Scheduling video frame %p at %" GST_TIME_FORMAT " with duration %" GST_TIME_FORMAT, frame, GST_TIME_ARGS (running_time), GST_TIME_ARGS (running_time_duration)); ret = self->output->output->ScheduleVideoFrame (frame, running_time, running_time_duration, GST_SECOND); if (ret != S_OK) { GST_ELEMENT_ERROR (self, STREAM, FAILED, (NULL), ("Failed to schedule frame: 0x%08x", ret)); flow_ret = GST_FLOW_ERROR; goto out; } flow_ret = GST_FLOW_OK; out: frame->Release (); return flow_ret; }