• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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