43 GStreamerVulkanWriter(VkPhysicalDevice phys, VkDevice dev, VkQueue q, uint32_t queueFamilyIndex, VkCommandPool commandPool, uint32_t w, uint32_t h, VkFormat fmt,
const std::string& outFile, uint32_t fpsNumer = 30, uint32_t fpsDenom = 1)
44 : m_phys(phys), m_dev(dev), m_queue(q), m_qfam(queueFamilyIndex), m_cmdPool(commandPool), m_w(w), m_h(h), m_fmt(fmt), m_fpsN(fpsNumer), m_fpsD(fpsDenom)
46 if (m_fmt != VK_FORMAT_B8G8R8A8_UNORM && m_fmt != VK_FORMAT_R8G8B8A8_UNORM)
47 throw std::runtime_error(
"unsupported format");
49 m_frameBytes = m_w * m_h * m_stride;
51 initGStreamer(outFile);
58 if (m_appsrc) gst_app_src_end_of_stream(GST_APP_SRC(m_appsrc));
59 GstBus* bus = gst_element_get_bus(m_pipeline);
64 GstMessage* msg = gst_bus_timed_pop_filtered(bus, 3 * GST_SECOND, (GstMessageType)(GST_MESSAGE_EOS | GST_MESSAGE_ERROR | GST_MESSAGE_STATE_CHANGED));
66 gst_message_unref(msg);
67 if (msg && (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_EOS || GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR))
break;
69 gst_object_unref(bus);
71 gst_element_set_state(m_pipeline, GST_STATE_NULL);
72 gst_object_unref(m_pipeline);
74 if (m_map) vkUnmapMemory(m_dev, m_mem);
75 if (m_buf) vkDestroyBuffer(m_dev, m_buf,
nullptr);
76 if (m_mem) vkFreeMemory(m_dev, m_mem,
nullptr);
83 void pushFrame(VkImage srcImage, VkImageLayout currentLayout, uint64_t frameIndex)
85 VkCommandBuffer cmd = beginCmd();
86 VkImageMemoryBarrier pre{};
87 pre.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
88 pre.oldLayout = currentLayout;
89 pre.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
90 pre.srcQueueFamilyIndex = m_qfam;
91 pre.dstQueueFamilyIndex = m_qfam;
93 pre.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
94 pre.subresourceRange.baseMipLevel = 0;
95 pre.subresourceRange.levelCount = 1;
96 pre.subresourceRange.baseArrayLayer = 0;
97 pre.subresourceRange.layerCount = 1;
98 pre.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT;
99 pre.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
100 vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0,
nullptr, 0,
nullptr, 1, &pre);
102 VkBufferImageCopy region{};
103 region.bufferOffset = 0;
104 region.bufferRowLength = 0;
105 region.bufferImageHeight = 0;
106 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
107 region.imageSubresource.mipLevel = 0;
108 region.imageSubresource.baseArrayLayer = 0;
109 region.imageSubresource.layerCount = 1;
110 region.imageOffset = { 0, 0, 0 };
111 region.imageExtent = { m_w, m_h, 1 };
112 vkCmdCopyImageToBuffer(cmd, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buf, 1, ®ion);
114 VkImageMemoryBarrier post{};
115 post.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
116 post.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
117 post.newLayout = currentLayout;
118 post.srcQueueFamilyIndex = m_qfam;
119 post.dstQueueFamilyIndex = m_qfam;
120 post.image = srcImage;
121 post.subresourceRange = pre.subresourceRange;
122 post.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
123 post.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT;
124 vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0,
nullptr, 0,
nullptr, 1, &post);
128 GstBuffer* buffer = gst_buffer_new_and_alloc(m_frameBytes);
130 gst_buffer_map(buffer, &info, GST_MAP_WRITE);
131 std::memcpy(info.data, m_map, m_frameBytes);
132 gst_buffer_unmap(buffer, &info);
133 GST_BUFFER_PTS(buffer) = gst_util_uint64_scale(frameIndex * (uint64_t)m_fpsD, GST_SECOND, m_fpsN);
134 GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale((uint64_t)m_fpsD, GST_SECOND, m_fpsN);
135 gst_app_src_push_buffer(GST_APP_SRC(m_appsrc), buffer);
141 if (m_appsrc) gst_app_src_end_of_stream(GST_APP_SRC(m_appsrc));
145 VkPhysicalDevice m_phys{};
149 VkCommandPool m_cmdPool{};
150 uint32_t m_w{}, m_h{}, m_stride{};
153 VkDeviceMemory m_mem{};
155 VkDeviceSize m_frameBytes{};
156 GstElement* m_pipeline{};
157 GstElement* m_appsrc{};
158 uint32_t m_fpsN{}, m_fpsD{};
160 uint32_t findMemoryType(uint32_t bits, VkMemoryPropertyFlags flags)
162 VkPhysicalDeviceMemoryProperties mp{};
163 vkGetPhysicalDeviceMemoryProperties(m_phys, &mp);
164 for (uint32_t i = 0; i < mp.memoryTypeCount; ++i)
166 if ((bits & (1u << i)) && (mp.memoryTypes[i].propertyFlags & flags) == flags)
return i;
168 throw std::runtime_error(
"no memory type");
173 VkBufferCreateInfo bi{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
174 bi.size = m_frameBytes;
175 bi.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
176 bi.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
177 if (vkCreateBuffer(m_dev, &bi,
nullptr, &m_buf) != VK_SUCCESS)
throw std::runtime_error(
"vkCreateBuffer");
178 VkMemoryRequirements req{};
179 vkGetBufferMemoryRequirements(m_dev, m_buf, &req);
180 VkMemoryAllocateInfo ai{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
181 ai.allocationSize = req.size;
182 ai.memoryTypeIndex = findMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
183 if (vkAllocateMemory(m_dev, &ai,
nullptr, &m_mem) != VK_SUCCESS)
throw std::runtime_error(
"vkAllocateMemory");
184 if (vkBindBufferMemory(m_dev, m_buf, m_mem, 0) != VK_SUCCESS)
throw std::runtime_error(
"vkBindBufferMemory");
185 if (vkMapMemory(m_dev, m_mem, 0, m_frameBytes, 0, &m_map) != VK_SUCCESS)
throw std::runtime_error(
"vkMapMemory");
188 VkCommandBuffer beginCmd()
190 VkCommandBufferAllocateInfo ai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
191 ai.commandPool = m_cmdPool;
192 ai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
193 ai.commandBufferCount = 1;
194 VkCommandBuffer cmd{};
195 if (vkAllocateCommandBuffers(m_dev, &ai, &cmd) != VK_SUCCESS)
throw std::runtime_error(
"vkAllocateCommandBuffers");
196 VkCommandBufferBeginInfo bi{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
197 bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
198 if (vkBeginCommandBuffer(cmd, &bi) != VK_SUCCESS)
throw std::runtime_error(
"vkBeginCommandBuffer");
202 void endCmdAndWait(VkCommandBuffer cmd)
204 if (vkEndCommandBuffer(cmd) != VK_SUCCESS)
throw std::runtime_error(
"vkEndCommandBuffer");
205 VkFenceCreateInfo fi{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
207 if (vkCreateFence(m_dev, &fi,
nullptr, &fence) != VK_SUCCESS)
throw std::runtime_error(
"vkCreateFence");
208 VkSubmitInfo si{ VK_STRUCTURE_TYPE_SUBMIT_INFO };
209 si.commandBufferCount = 1;
210 si.pCommandBuffers = &cmd;
211 if (vkQueueSubmit(m_queue, 1, &si, fence) != VK_SUCCESS)
throw std::runtime_error(
"vkQueueSubmit");
212 vkWaitForFences(m_dev, 1, &fence, VK_TRUE, UINT64_C(0xffffffffffffffff));
213 vkDestroyFence(m_dev, fence,
nullptr);
214 vkFreeCommandBuffers(m_dev, m_cmdPool, 1, &cmd);
217 void initGStreamer(
const std::string& outFile)
219 static bool inited =
false;
220 if (!inited) {
int argc = 0;
char** argv =
nullptr; gst_init(&argc, &argv); inited =
true; }
221 const char* vfmt = (m_fmt == VK_FORMAT_B8G8R8A8_UNORM) ?
"BGRA" :
"RGBA";
223 "appsrc name=src is-live=false format=time do-timestamp=false "
224 "! videoconvert n-threads=0 "
225 "! x264enc tune=zerolatency speed-preset=ultrafast bitrate=5000 key-int-max=60 "
227 "! mp4mux faststart=true "
228 "! filesink location=\"" + outFile +
"\"";
229 GError* err =
nullptr;
230 m_pipeline = gst_parse_launch(pipe.c_str(), &err);
231 if (!m_pipeline || err)
throw std::runtime_error(
"gst_parse_launch");
232 m_appsrc = gst_bin_get_by_name(GST_BIN(m_pipeline),
"src");
233 if (!m_appsrc)
throw std::runtime_error(
"appsrc not found");
234 GstCaps* caps = gst_caps_new_simple(
236 "format", G_TYPE_STRING, vfmt,
237 "width", G_TYPE_INT, (
int)m_w,
238 "height", G_TYPE_INT, (
int)m_h,
239 "framerate", GST_TYPE_FRACTION, (
int)m_fpsN, (
int)m_fpsD,
241 g_object_set(G_OBJECT(m_appsrc),
244 "format", GST_FORMAT_TIME,
247 gst_caps_unref(caps);
248 gst_element_set_state(m_pipeline, GST_STATE_PLAYING);