1 /*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "config.h"
17 #include "gst_mux_bin.h"
18 #include "av_common.h"
19 #include "gstbasesink.h"
20 #include "gstbaseparse.h"
21
22 enum {
23 PROP_0,
24 PROP_FD,
25 PROP_MUX,
26 PROP_DEGREES,
27 PROP_LATITUDE,
28 PROP_LONGITUDE,
29 };
30
31 enum {
32 SIGNAL_ADD_TRACK,
33 LAST_SIGNAL
34 };
35
36 static guint gst_mux_bin_signals[LAST_SIGNAL] = { 0 };
37 using namespace OHOS;
38 #define gst_mux_bin_parent_class parent_class
39 G_DEFINE_TYPE(GstMuxBin, gst_mux_bin, GST_TYPE_PIPELINE);
40
41 static void gst_mux_bin_finalize(GObject *object);
42 static void gst_mux_bin_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *param_spec);
43 static GstStateChangeReturn gst_mux_bin_change_state(GstElement *element, GstStateChange transition);
44
gst_mux_bin_add_track(GstMuxBin * mux_bin,const char * src_name,const char * parse_name,int32_t track_type)45 static void gst_mux_bin_add_track(GstMuxBin *mux_bin, const char *src_name, const char *parse_name, int32_t track_type)
46 {
47 g_return_if_fail(mux_bin != nullptr);
48 g_return_if_fail(src_name != nullptr);
49 g_return_if_fail(parse_name != nullptr);
50 GstTrackInfo *info = g_new(GstTrackInfo, 1);
51 g_return_if_fail(info != nullptr);
52 info->srcName_ = g_strdup(static_cast<char *>(src_name));
53 info->parseName_ = g_strdup(static_cast<char *>(parse_name));
54 switch (static_cast<OHOS::Media::MediaType>(track_type)) {
55 case OHOS::Media::MEDIA_TYPE_VID:
56 mux_bin->video_src_list = g_slist_append(mux_bin->video_src_list, info);
57 break;
58 case OHOS::Media::MEDIA_TYPE_AUD:
59 mux_bin->audio_src_list = g_slist_append(mux_bin->audio_src_list, info);
60 break;
61 default:
62 break;
63 }
64 }
65
gst_mux_bin_class_init(GstMuxBinClass * klass)66 static void gst_mux_bin_class_init(GstMuxBinClass *klass)
67 {
68 g_return_if_fail(klass != nullptr);
69 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
70 GstElementClass *gstelement_class = GST_ELEMENT_CLASS(klass);
71
72 g_return_if_fail(gobject_class != nullptr);
73 g_return_if_fail(gstelement_class != nullptr);
74
75 gobject_class->finalize = gst_mux_bin_finalize;
76 gobject_class->set_property = gst_mux_bin_set_property;
77
78 g_object_class_install_property(gobject_class, PROP_FD,
79 g_param_spec_int("fd", "FD", "fd of the output file",
80 G_MININT32, G_MAXINT32, -1, (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
81
82 g_object_class_install_property(gobject_class, PROP_MUX,
83 g_param_spec_string("mux", "Mux", "type of the mux",
84 nullptr, (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
85
86 g_object_class_install_property(gobject_class, PROP_DEGREES,
87 g_param_spec_int("rotation", "Rotation", "rotation angle of the output file",
88 0, G_MAXINT32, 0, (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
89
90 g_object_class_install_property(gobject_class, PROP_LATITUDE,
91 g_param_spec_int("latitude", "Latitude", "latitude of the output file",
92 G_MININT32, G_MAXINT32, 0, (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
93
94 g_object_class_install_property(gobject_class, PROP_LONGITUDE,
95 g_param_spec_int("longitude", "Longitude", "longitude of the output file",
96 G_MININT32, G_MAXINT32, 0, (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
97
98 gst_mux_bin_signals[SIGNAL_ADD_TRACK] =
99 g_signal_new("add-track", G_TYPE_FROM_CLASS(klass),
100 static_cast<GSignalFlags>(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
101 G_STRUCT_OFFSET(GstMuxBinClass, add_track), NULL, NULL, NULL,
102 G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT); // 3 parameters
103
104 gst_element_class_set_static_metadata(gstelement_class,
105 "Mux Bin", "Generic/Bin/Mux",
106 "Auto construct mux pipeline", "OpenHarmony");
107
108 klass->add_track = gst_mux_bin_add_track;
109 gstelement_class->change_state = gst_mux_bin_change_state;
110 }
111
gst_mux_bin_init(GstMuxBin * mux_bin)112 static void gst_mux_bin_init(GstMuxBin *mux_bin)
113 {
114 g_return_if_fail(mux_bin != nullptr);
115 GST_INFO_OBJECT(mux_bin, "gst_mux_bin_init");
116 mux_bin->video_src_list = nullptr;
117 mux_bin->audio_src_list = nullptr;
118 mux_bin->split_mux_sink = nullptr;
119 mux_bin->out_fd = -1;
120 mux_bin->mux = nullptr;
121 mux_bin->rotation = 0;
122 mux_bin->latitude = 0;
123 mux_bin->longitude = 0;
124 }
125
gst_mux_bin_free_list(GSList * list)126 static void gst_mux_bin_free_list(GSList *list)
127 {
128 GSList *iter = list;
129 while (iter != nullptr && iter->data != nullptr) {
130 if ((reinterpret_cast<GstTrackInfo *>(iter->data))->srcName_ != nullptr) {
131 g_free((reinterpret_cast<GstTrackInfo *>(iter->data))->srcName_);
132 (reinterpret_cast<GstTrackInfo *>(iter->data))->srcName_ = nullptr;
133 }
134 if ((reinterpret_cast<GstTrackInfo *>(iter->data))->parseName_ != nullptr) {
135 g_free((reinterpret_cast<GstTrackInfo *>(iter->data))->parseName_);
136 (reinterpret_cast<GstTrackInfo *>(iter->data))->parseName_ = nullptr;
137 }
138 if ((reinterpret_cast<GstTrackInfo *>(iter->data))->src_ != nullptr) {
139 gst_object_unref((reinterpret_cast<GstTrackInfo *>(iter->data))->src_);
140 (reinterpret_cast<GstTrackInfo *>(iter->data))->src_ = nullptr;
141 }
142 if ((reinterpret_cast<GstTrackInfo *>(iter->data))->parse_ != nullptr) {
143 gst_object_unref((reinterpret_cast<GstTrackInfo *>(iter->data))->parse_);
144 (reinterpret_cast<GstTrackInfo *>(iter->data))->parse_ = nullptr;
145 }
146 g_free(iter->data);
147 iter->data = nullptr;
148 iter = iter->next;
149 }
150 }
151
gst_mux_bin_finalize(GObject * object)152 static void gst_mux_bin_finalize(GObject *object)
153 {
154 g_return_if_fail(object != nullptr);
155 GstMuxBin *mux_bin = GST_MUX_BIN(object);
156 GST_INFO_OBJECT(mux_bin, "gst_mux_bin_finalize");
157
158 if (mux_bin->mux != nullptr) {
159 g_free(mux_bin->mux);
160 mux_bin->mux = nullptr;
161 }
162
163 if (mux_bin->split_mux_sink != nullptr) {
164 gst_object_unref(mux_bin->split_mux_sink);
165 mux_bin->split_mux_sink = nullptr;
166 }
167
168 gst_mux_bin_free_list(mux_bin->video_src_list);
169 g_slist_free(mux_bin->video_src_list);
170 mux_bin->video_src_list = nullptr;
171
172 gst_mux_bin_free_list(mux_bin->audio_src_list);
173 g_slist_free(mux_bin->audio_src_list);
174 mux_bin->audio_src_list = nullptr;
175
176 G_OBJECT_CLASS(parent_class)->finalize(object);
177 }
178
179
gst_mux_bin_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * param_spec)180 static void gst_mux_bin_set_property(GObject *object, guint prop_id,
181 const GValue *value, GParamSpec *param_spec)
182 {
183 g_return_if_fail(object != nullptr);
184 g_return_if_fail(value != nullptr);
185 (void)param_spec;
186 GstMuxBin *mux_bin = GST_MUX_BIN(object);
187 switch (prop_id) {
188 case PROP_FD:
189 mux_bin->out_fd = g_value_get_int(value);
190 break;
191 case PROP_MUX:
192 mux_bin->mux = g_strdup(g_value_get_string(value));
193 break;
194 case PROP_DEGREES:
195 mux_bin->rotation = g_value_get_int(value);
196 break;
197 case PROP_LATITUDE:
198 mux_bin->latitude = g_value_get_int(value);
199 break;
200 case PROP_LONGITUDE:
201 mux_bin->longitude = g_value_get_int(value);
202 break;
203 default:
204 break;
205 }
206 }
207
gst_mux_bin_create_splitmuxsink(GstMuxBin * mux_bin)208 static bool gst_mux_bin_create_splitmuxsink(GstMuxBin *mux_bin)
209 {
210 g_return_val_if_fail(mux_bin != nullptr, false);
211 g_return_val_if_fail(mux_bin->out_fd >= 0, false);
212 g_return_val_if_fail(mux_bin->mux != nullptr, false);
213 GST_INFO_OBJECT(mux_bin, "gst_mux_bin_create_splitmuxsink");
214
215 mux_bin->split_mux_sink = GST_ELEMENT(gst_object_ref_sink(
216 gst_element_factory_make("splitmuxsink", "splitmuxsink")));
217 g_return_val_if_fail(mux_bin->split_mux_sink != nullptr, false);
218
219 GstElement *fdsink = gst_element_factory_make("fdsink", "fdsink");
220 g_return_val_if_fail(fdsink != nullptr, false);
221
222 g_object_set(fdsink, "fd", mux_bin->out_fd, nullptr);
223 gst_base_sink_set_async_enabled(GST_BASE_SINK(fdsink), FALSE);
224 g_object_set(mux_bin->split_mux_sink, "sink", fdsink, nullptr);
225
226 GstElement *qtmux = gst_element_factory_make(mux_bin->mux, mux_bin->mux);
227 g_return_val_if_fail(qtmux != nullptr, false);
228 g_object_set(qtmux, "orientation-hint", mux_bin->rotation, "set-latitude", mux_bin->latitude,
229 "set-longitude", mux_bin->longitude, nullptr);
230 g_object_set(mux_bin->split_mux_sink, "muxer", qtmux, nullptr);
231
232 return true;
233 }
234
gst_mux_bin_create_parse(GstMuxBin * mux_bin,const char * parse_name)235 static GstElement *gst_mux_bin_create_parse(GstMuxBin *mux_bin, const char* parse_name)
236 {
237 g_return_val_if_fail(mux_bin != nullptr, nullptr);
238 GST_INFO_OBJECT(mux_bin, "gst_mux_bin_create_parse");
239
240 GstElement *parse = nullptr;
241 if (strncmp(parse_name, "h264parse", strlen("h264parse")) == 0) {
242 parse = GST_ELEMENT(gst_object_ref_sink(gst_element_factory_make("h264parse", parse_name)));
243 g_return_val_if_fail(parse != nullptr, nullptr);
244 } else if (strncmp(parse_name, "mpeg4videoparse", strlen("mpeg4videoparse")) == 0) {
245 parse = GST_ELEMENT(gst_object_ref_sink(gst_element_factory_make("mpeg4videoparse", parse_name)));
246 g_return_val_if_fail(parse != nullptr, nullptr);
247 g_object_set(parse, "config-interval", -1, "drop", false, nullptr);
248 } else if (strncmp(parse_name, "aacparse", strlen("aacparse")) == 0) {
249 parse = GST_ELEMENT(gst_object_ref_sink(gst_element_factory_make("aacparse", parse_name)));
250 g_return_val_if_fail(parse != nullptr, nullptr);
251 } else {
252 GST_ERROR_OBJECT(mux_bin, "Invalid videoParse");
253 }
254
255 return parse;
256 }
257
gst_mux_bin_create_src(GstMuxBin * mux_bin,OHOS::Media::MediaType track_type)258 static bool gst_mux_bin_create_src(GstMuxBin *mux_bin, OHOS::Media::MediaType track_type)
259 {
260 g_return_val_if_fail(mux_bin != nullptr, false);
261 GST_INFO_OBJECT(mux_bin, "gst_mux_bin_create_src");
262
263 GSList *iter = nullptr;
264 switch (track_type) {
265 case OHOS::Media::MEDIA_TYPE_VID:
266 iter = mux_bin->video_src_list;
267 break;
268 case OHOS::Media::MEDIA_TYPE_AUD:
269 iter = mux_bin->audio_src_list;
270 break;
271 default:
272 break;
273 }
274 while (iter != nullptr) {
275 GstElement *app_src = GST_ELEMENT(gst_object_ref_sink(gst_element_factory_make(
276 "appsrc", (reinterpret_cast<GstTrackInfo *>(iter->data))->srcName_)));
277 g_return_val_if_fail(app_src != nullptr, false);
278 g_object_set(app_src, "is-live", true, "format", GST_FORMAT_TIME, nullptr);
279 (reinterpret_cast<GstTrackInfo *>(iter->data))->src_ = app_src;
280 if (strstr((reinterpret_cast<GstTrackInfo *>(iter->data))->parseName_, "parse")) {
281 GstElement *parse = gst_mux_bin_create_parse(mux_bin,
282 (reinterpret_cast<GstTrackInfo *>(iter->data))->parseName_);
283 g_return_val_if_fail(parse != nullptr, false);
284 (reinterpret_cast<GstTrackInfo *>(iter->data))->parse_ = parse;
285 } else {
286 (reinterpret_cast<GstTrackInfo *>(iter->data))->parse_ = nullptr;
287 }
288 iter = iter->next;
289 }
290
291 return true;
292 }
293
gst_mux_bin_create_element(GstMuxBin * mux_bin)294 static bool gst_mux_bin_create_element(GstMuxBin *mux_bin)
295 {
296 g_return_val_if_fail(mux_bin != nullptr, false);
297 GST_INFO_OBJECT(mux_bin, "gst_mux_bin_create_element");
298
299 if (!gst_mux_bin_create_splitmuxsink(mux_bin)) {
300 GST_ERROR_OBJECT(mux_bin, "Failed to call gst_mux_bin_create_splitmuxsink");
301 return false;
302 }
303
304 if (!gst_mux_bin_create_src(mux_bin, OHOS::Media::MEDIA_TYPE_VID)) {
305 GST_ERROR_OBJECT(mux_bin, "Failed to call create_video_src");
306 return false;
307 }
308
309 if (!gst_mux_bin_create_src(mux_bin, OHOS::Media::MEDIA_TYPE_AUD)) {
310 GST_ERROR_OBJECT(mux_bin, "Failed to call create_audio_src");
311 return false;
312 }
313
314 return true;
315 }
316
gst_mux_bin_add_element_to_bin(GstMuxBin * mux_bin)317 static bool gst_mux_bin_add_element_to_bin(GstMuxBin *mux_bin)
318 {
319 g_return_val_if_fail(mux_bin != nullptr, false);
320 g_return_val_if_fail(mux_bin->split_mux_sink != nullptr, false);
321 GST_INFO_OBJECT(mux_bin, "gst_mux_bin_add_element_to_bin");
322
323 bool ret;
324 GSList *iter = mux_bin->video_src_list;
325 while (iter != nullptr) {
326 ret = gst_bin_add(GST_BIN(mux_bin), (reinterpret_cast<GstTrackInfo *>(iter->data))->src_);
327 g_return_val_if_fail(ret == TRUE, false);
328 if ((reinterpret_cast<GstTrackInfo *>(iter->data))->parse_ != nullptr) {
329 ret = gst_bin_add(GST_BIN(mux_bin), (reinterpret_cast<GstTrackInfo *>(iter->data))->parse_);
330 g_return_val_if_fail(ret == TRUE, false);
331 }
332 iter = iter->next;
333 }
334 iter = mux_bin->audio_src_list;
335 while (iter != nullptr) {
336 ret = gst_bin_add(GST_BIN(mux_bin), (reinterpret_cast<GstTrackInfo *>(iter->data))->src_);
337 g_return_val_if_fail(ret == TRUE, false);
338 if ((reinterpret_cast<GstTrackInfo *>(iter->data))->parse_ != nullptr) {
339 ret = gst_bin_add(GST_BIN(mux_bin), (reinterpret_cast<GstTrackInfo *>(iter->data))->parse_);
340 g_return_val_if_fail(ret == TRUE, false);
341 }
342 iter = iter->next;
343 }
344 ret = gst_bin_add(GST_BIN(mux_bin), mux_bin->split_mux_sink);
345 g_return_val_if_fail(ret == TRUE, false);
346
347 return true;
348 }
349
gst_mux_bin_connect_parse(GstMuxBin * mux_bin,GstElement * parse,GstPad * upstream_pad,GstPad * downstream_pad)350 static bool gst_mux_bin_connect_parse(
351 GstMuxBin *mux_bin, GstElement *parse, GstPad *upstream_pad, GstPad *downstream_pad)
352 {
353 g_return_val_if_fail(mux_bin != nullptr, false);
354 g_return_val_if_fail(parse != nullptr, false);
355 g_return_val_if_fail(upstream_pad != nullptr, false);
356 g_return_val_if_fail(downstream_pad != nullptr, false);
357 GST_INFO_OBJECT(mux_bin, "gst_mux_bin_connect_parse");
358
359 GstPad *parse_sink_pad = gst_element_get_static_pad(parse, "sink");
360 if (gst_pad_link(upstream_pad, parse_sink_pad) != GST_PAD_LINK_OK) {
361 gst_object_unref(parse_sink_pad);
362 GST_ERROR_OBJECT(mux_bin, "Failed to link src_src_pad and parse_sink_pad");
363 return false;
364 }
365 gst_object_unref(parse_sink_pad);
366 GstPad *parse_src_pad = gst_element_get_static_pad(parse, "src");
367 if (gst_pad_link(parse_src_pad, downstream_pad) != GST_PAD_LINK_OK) {
368 gst_object_unref(parse_src_pad);
369 GST_ERROR_OBJECT(mux_bin, "Failed to link parse_src_pad and split_mux_sink_sink_pad");
370 return false;
371 }
372 gst_object_unref(parse_src_pad);
373
374 return true;
375 }
376
gst_mux_bin_connect_element(GstMuxBin * mux_bin,OHOS::Media::MediaType type)377 static bool gst_mux_bin_connect_element(GstMuxBin *mux_bin, OHOS::Media::MediaType type)
378 {
379 g_return_val_if_fail(mux_bin != nullptr, false);
380 g_return_val_if_fail(mux_bin->split_mux_sink != nullptr, false);
381 GST_INFO_OBJECT(mux_bin, "gst_mux_bin_connect_element");
382
383 GSList *iter = nullptr;
384 if (type == OHOS::Media::MEDIA_TYPE_VID) {
385 iter = mux_bin->video_src_list;
386 } else if (type == OHOS::Media::MEDIA_TYPE_AUD) {
387 iter = mux_bin->audio_src_list;
388 } else {
389 GST_ERROR_OBJECT(mux_bin, "Failed to check track type");
390 return false;
391 }
392
393 while (iter != nullptr) {
394 GstPad *src_src_pad = gst_element_get_static_pad((reinterpret_cast<GstTrackInfo *>(iter->data))->src_, "src");
395 GstPad *split_mux_sink_sink_pad = nullptr;
396 if (type == OHOS::Media::MEDIA_TYPE_VID) {
397 split_mux_sink_sink_pad = gst_element_request_pad_simple(mux_bin->split_mux_sink, "video");
398 } else if (type == OHOS::Media::MEDIA_TYPE_AUD) {
399 split_mux_sink_sink_pad = gst_element_request_pad_simple(mux_bin->split_mux_sink, "audio_%u");
400 }
401 if ((reinterpret_cast<GstTrackInfo *>(iter->data))->parse_ != nullptr) {
402 if (!gst_mux_bin_connect_parse(mux_bin, (reinterpret_cast<GstTrackInfo *>(iter->data))->parse_,
403 src_src_pad, split_mux_sink_sink_pad)) {
404 gst_object_unref(split_mux_sink_sink_pad);
405 gst_object_unref(src_src_pad);
406 GST_ERROR_OBJECT(mux_bin, "Failed to call connect_parse");
407 return false;
408 }
409 } else {
410 if (gst_pad_link(src_src_pad, split_mux_sink_sink_pad) != GST_PAD_LINK_OK) {
411 gst_object_unref(split_mux_sink_sink_pad);
412 gst_object_unref(src_src_pad);
413 GST_ERROR_OBJECT(mux_bin, "Failed to link src_src_pad and split_mux_sink_sink_pad");
414 return false;
415 }
416 }
417 gst_object_unref(split_mux_sink_sink_pad);
418 gst_object_unref(src_src_pad);
419 iter = iter->next;
420 }
421
422 return true;
423 }
424
gst_mux_bin_change_state(GstElement * element,GstStateChange transition)425 static GstStateChangeReturn gst_mux_bin_change_state(GstElement *element, GstStateChange transition)
426 {
427 g_return_val_if_fail(element != nullptr, GST_STATE_CHANGE_FAILURE);
428 GstMuxBin *mux_bin = GST_MUX_BIN(element);
429 GST_INFO_OBJECT(mux_bin, "gst_mux_bin_change_state");
430
431 switch (transition) {
432 case GST_STATE_CHANGE_NULL_TO_READY: {
433 if (!gst_mux_bin_create_element(mux_bin)) {
434 GST_ERROR_OBJECT(mux_bin, "Failed to create element");
435 return GST_STATE_CHANGE_FAILURE;
436 }
437 if (!gst_mux_bin_add_element_to_bin(mux_bin)) {
438 GST_ERROR_OBJECT(mux_bin, "Failed to add element to bin");
439 return GST_STATE_CHANGE_FAILURE;
440 }
441 break;
442 }
443 case GST_STATE_CHANGE_READY_TO_PAUSED: {
444 if (!gst_mux_bin_connect_element(mux_bin, OHOS::Media::MEDIA_TYPE_VID)) {
445 GST_ERROR_OBJECT(mux_bin, "Failed to connect element");
446 return GST_STATE_CHANGE_FAILURE;
447 }
448 if (!gst_mux_bin_connect_element(mux_bin, OHOS::Media::MEDIA_TYPE_AUD)) {
449 GST_ERROR_OBJECT(mux_bin, "Failed to connect element");
450 return GST_STATE_CHANGE_FAILURE;
451 }
452 break;
453 }
454 default:
455 break;
456 }
457 return GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
458 }
459
plugin_init(GstPlugin * plugin)460 static gboolean plugin_init(GstPlugin *plugin)
461 {
462 g_return_val_if_fail(plugin != nullptr, FALSE);
463 return gst_element_register(plugin, "muxbin", GST_RANK_PRIMARY, GST_TYPE_MUX_BIN);
464 }
465
466 GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
467 GST_VERSION_MINOR,
468 _avmuxer_bin,
469 "GStreamer Mux Bin",
470 plugin_init,
471 PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
472