4 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: YoungHun Kim <yh8004.kim@samsung.com>
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
21 #include "mm_transcode.h"
22 #include "mm_transcode_internal.h"
24 static void _mm_transcode_audio_capsfilter(GstCaps *caps, handle_s *handle);
25 static void _mm_transcode_video_capsfilter(GstCaps *caps, handle_s *handle);
26 static void _mm_transcode_video_capsfilter_call(handle_s *handle);
27 static void _mm_transcode_video_capsfilter_set_parameter(GstCaps *caps, handle_s *handle);
28 static int _mm_transcode_exec(handle_s *handle, handle_param_s *param);
29 static int _mm_transcode_play(handle_s *handle);
30 static int _mm_transcode_seek(handle_s *handle);
31 static gpointer _mm_transcode_thread_repeate(gpointer data);
33 GstPadProbeReturn _mm_cb_audio_output_stream_probe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
35 handle_s *handle = (handle_s *)user_data;
38 LOGE("[ERROR] - handle");
39 return GST_PAD_PROBE_REMOVE;
42 if (!handle->property) {
43 LOGE("[ERROR] - handle property");
44 return GST_PAD_PROBE_REMOVE;
47 gint64 start_pos_ts = handle->param->start_pos * G_GINT64_CONSTANT(1000000);
49 if (GST_BUFFER_PTS_IS_VALID(GST_PAD_PROBE_INFO_BUFFER(info))) {
50 if (0 == handle->property->AUDFLAG++) {
51 GstCaps *current_caps = gst_pad_get_current_caps(pad);
52 /* Need to audio caps converting when amrnbenc */
53 /* Not drop buffer with 'return FALSE' */
54 _mm_transcode_audio_capsfilter(current_caps, handle);
57 gst_caps_unref(current_caps);
60 if (handle->param->seeking) {
61 /* Shifting the decoded out buffer time as the start time */
62 if (GST_BUFFER_PTS(GST_PAD_PROBE_INFO_BUFFER(info)) >= start_pos_ts) {
63 GST_BUFFER_PTS(GST_PAD_PROBE_INFO_BUFFER(info)) -= start_pos_ts;
65 /* If input buffer time is less than start position,
66 * input buffer will be dropped.
68 return GST_PAD_PROBE_DROP;
73 return GST_PAD_PROBE_OK;
76 GstPadProbeReturn _mm_cb_video_output_stream_probe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
78 handle_s *handle = (handle_s *)user_data;
81 LOGE("[ERROR] - handle");
82 return GST_PAD_PROBE_REMOVE;
85 if (!handle->property) {
86 LOGE("[ERROR] - handle property");
87 return GST_PAD_PROBE_REMOVE;
90 gint64 start_pos_ts = handle->param->start_pos * G_GINT64_CONSTANT(1000000);
92 if (GST_BUFFER_PTS_IS_VALID(GST_PAD_PROBE_INFO_BUFFER(info))) {
93 if (0 == handle->property->VIDFLAG++) {
94 GstCaps *current_caps = gst_pad_get_current_caps(pad);
95 /* Not drop buffer with 'return FALSE' */
96 _mm_transcode_video_capsfilter(current_caps, handle);
99 gst_caps_unref(current_caps);
102 if (handle->param->seeking) {
103 /* Shifting the decoded out buffer time as the start time */
104 if (GST_BUFFER_PTS(GST_PAD_PROBE_INFO_BUFFER(info)) >= start_pos_ts) {
105 GST_BUFFER_PTS(GST_PAD_PROBE_INFO_BUFFER(info)) -= start_pos_ts;
107 /* If input buffer time is less than start position,
108 * input buffer will be dropped.
110 return GST_PAD_PROBE_DROP;
115 return GST_PAD_PROBE_OK;
118 GstPadProbeReturn _mm_cb_encodebin_sinkpad_event_probe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
120 handle_s *handle = (handle_s *)user_data;
121 GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info);
124 LOGE("[ERROR] - handle");
125 return GST_PAD_PROBE_REMOVE;
128 if (!handle->property) {
129 LOGE("[ERROR] - handle property");
130 return GST_PAD_PROBE_REMOVE;
134 LOGE("[ERROR] - event");
135 return GST_PAD_PROBE_REMOVE;
138 switch (GST_EVENT_TYPE(event)) {
139 case GST_EVENT_SEGMENT:
141 if (!handle->param->seeking)
144 const GstSegment *segment = NULL;
145 GstSegment *new_segment = NULL;
146 gst_event_parse_segment(event, &segment);
147 if (segment->format != GST_FORMAT_TIME)
150 new_segment = gst_segment_copy(segment);
151 gst_event_unref(event);
154 LOGE("[ERROR] segment copy error");
155 return GST_PAD_PROBE_REMOVE;
158 new_segment->start = 0;
159 new_segment->stop = handle->param->duration * G_GINT64_CONSTANT(1000000);
161 /* replace the new segment (change start/stop position) */
162 GstEvent *new_event = gst_event_new_segment(new_segment);
164 GST_PAD_PROBE_INFO_DATA(info) = new_event;
172 return GST_PAD_PROBE_OK;
175 GstAutoplugSelectResult _mm_cb_decode_bin_autoplug_select(GstElement *element, GstPad *pad, GstCaps *caps, GstElementFactory *factory, handle_s *handle)
177 const gchar *feature_name = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory));
178 const gchar *caps_str = NULL;
181 LOGE("[ERROR] - handle");
182 return MM_ERROR_INVALID_ARGUMENT;
185 if (!handle->property) {
186 LOGE("[ERROR] - handle property");
187 return MM_ERROR_TRANSCODE_INTERNAL;
190 caps_str = _mm_check_media_type(caps);
191 if (g_strrstr(caps_str, "audio")) {
192 handle->property->audiodecodename = (char *)malloc(sizeof(char) * ENC_BUFFER_SIZE);
193 if (handle->property->audiodecodename == NULL) {
194 LOGE("audiodecodename is NULL");
195 return GST_AUTOPLUG_SELECT_TRY;
197 memset(handle->property->audiodecodename, 0, ENC_BUFFER_SIZE);
198 g_strlcpy(handle->property->audiodecodename, feature_name, ENC_BUFFER_SIZE);
199 LOGD("[audio decode name %s : %s]", caps_str, handle->property->audiodecodename);
202 if (g_strrstr(caps_str, "video")) {
203 if (g_strrstr(feature_name, "omx") || g_strrstr(feature_name, "sprd")
204 || g_strrstr(feature_name, "v4l2") || g_strrstr(feature_name, "nxvideo")) {
205 /* emit autoplug-select to see what we should do with it. */
206 LOGD("SKIP %s Codec", feature_name);
207 return GST_AUTOPLUG_SELECT_SKIP;
209 handle->property->videodecodename = (char *)malloc(sizeof(char) * ENC_BUFFER_SIZE);
210 if (handle->property->videodecodename == NULL) {
211 LOGE("videodecodename is NULL");
212 return GST_AUTOPLUG_SELECT_TRY;
214 memset(handle->property->videodecodename, 0, ENC_BUFFER_SIZE);
215 g_strlcpy(handle->property->videodecodename, feature_name, ENC_BUFFER_SIZE);
216 LOGD("[video decode name %s : %s]", caps_str, handle->property->videodecodename);
220 return GST_AUTOPLUG_SELECT_TRY;
223 void _mm_cb_decoder_newpad_encoder(GstElement *decodebin, GstPad *pad, handle_s *handle)
226 LOGE("[ERROR] - handle");
230 if (!handle->decoder_vidp) {
231 LOGE("[ERROR] - handle decoder_vidp");
235 if (!handle->decoder_audp) {
236 LOGE("[ERROR] - handle decoder_audp");
240 if (!handle->encodebin) {
241 LOGE("[ERROR] - handle encodebin");
245 if (!handle->property) {
246 LOGE("[ERROR] - handle property");
250 LOGD("[============ new-decoded-pad ============]");
251 handle->property->caps = gst_pad_query_caps(pad, NULL);
252 const gchar *mime = _mm_check_media_type(handle->property->caps);
255 LOGE("[ERROR] - mime");
259 if (g_strrstr(mime, "video")) {
260 handle->property->linked_vidoutbin = TRUE;
263 if (gst_pad_is_linked(pad)) {
266 if (gst_pad_link(pad, (GstPad *) handle->decoder_vidp->sinkdecvideopad) != GST_PAD_LINK_OK)
267 LOGE("Error [pad - sinkdecvideopad]");
269 LOGD("Success [pad - sinkdecvideopad]");
272 } else if (g_strrstr(mime, "audio")) {
273 handle->property->linked_audoutbin = TRUE;
276 if (gst_pad_is_linked(pad)) {
279 if (gst_pad_link(pad, (GstPad *) handle->decoder_audp->sinkdecaudiopad) != GST_PAD_LINK_OK)
280 LOGE("Error [pad - sinkdecaudiopad]");
282 LOGD("Success [pad - sinkdecaudiopad]");
286 LOGD("gst structure error");
290 gboolean _mm_cb_print_position(handle_s *handle)
292 GstFormat fmt = GST_FORMAT_TIME;
296 LOGE("[ERROR] - handle");
300 if (!handle->property) {
301 LOGE("[ERROR] - handle property");
305 /* To avoid gst_element_query_position bs */
306 if (!handle->property->repeat_thread_exit) {
307 if (gst_element_query_position(handle->pipeline, fmt, &pos)) {
308 unsigned long current_pos = (unsigned long)(GST_TIME_AS_MSECONDS(pos));
309 if (handle->param->seeking == FALSE) {
310 handle->property->current_pos = current_pos;
311 handle->property->real_duration = handle->property->total_length;
312 } else if (handle->param->seeking == TRUE) {
313 handle->property->current_pos = current_pos - handle->param->start_pos;
314 if (handle->param->duration != 0) {
315 if (handle->param->start_pos + handle->param->duration > handle->property->total_length)
316 handle->property->real_duration = handle->property->total_length - handle->param->start_pos;
318 handle->property->real_duration = handle->param->duration;
319 /* seek to origin file length */
320 } else if (handle->param->duration == 0) {
321 handle->property->real_duration = handle->property->total_length - handle->param->start_pos;
325 if (handle->property->current_pos <= handle->property->real_duration) {
326 /* 2 = 1000 / 500 minimum printed cnt for last buffer */
327 if (handle->property->current_pos == 0 && handle->param->printed > 2)
328 handle->property->current_pos = handle->property->real_duration;
330 if (handle->property->progress_cb) {
331 /* for first buffer */
332 if (0 == handle->param->printed)
333 handle->property->current_pos = 0;
334 handle->property->progress_cb(handle->property->current_pos, handle->property->real_duration, handle->property->progress_cb_param);
335 handle->param->printed++;
344 gboolean _mm_cb_transcode_bus(GstBus *bus, GstMessage *message, gpointer userdata)
346 handle_s *handle = (handle_s *)userdata;
347 int ret = MM_ERROR_NONE;
350 LOGE("[ERROR] - handle");
354 if (!handle->property) {
355 LOGE("[ERROR] - handle property");
360 GstFormat fmt = GST_FORMAT_TIME;
361 MMHandleType MMHandle = (MMHandleType)handle;
363 switch (GST_MESSAGE_TYPE(message)) {
364 case GST_MESSAGE_ERROR:
368 gst_message_parse_error(message, &err, &debug);
371 LOGW("Fail to parse error message");
375 LOGE("[Source: %s] Error: %s", GST_OBJECT_NAME(GST_OBJECT_CAST(GST_ELEMENT(GST_MESSAGE_SRC(message)))), err->message);
377 ret = mm_transcode_cancel(MMHandle);
378 if (ret == MM_ERROR_NONE) {
379 LOGD("Success - Cancel Transcode");
381 LOGE("ERROR - Cancel Transcode");
388 TRANSCODE_FREE(debug);
389 TRANSCODE_FREE(handle->param);
390 /* g_main_loop_quit (handle->loop); */
394 case GST_MESSAGE_STATE_CHANGED:
396 if (GST_ELEMENT(GST_MESSAGE_SRC(message)) != handle->pipeline)
399 GstState State_Old, State_New, State_Pending;
400 gst_message_parse_state_changed(message, &State_Old, &State_New, &State_Pending);
402 LOGD("[Source: %s] [State: %d -> %d]", GST_OBJECT_NAME(GST_OBJECT_CAST(GST_ELEMENT(GST_MESSAGE_SRC(message)))), State_Old, State_New);
404 if (State_Old == GST_STATE_NULL && State_New == GST_STATE_READY) {
405 LOGD("[Set State: Pause]");
406 /* Pause Transcode */
407 ret = _mm_transcode_state_change(handle, GST_STATE_PAUSED);
408 if (ret == MM_ERROR_NONE) {
409 LOGD("Success - Pause pipeline");
411 LOGE("ERROR - Pause pipeline");
416 if (State_Old == GST_STATE_READY && State_New == GST_STATE_PAUSED) {
418 LOGD("[%s] Start New Segment pipeline", handle->param->outputfile);
419 ret = _mm_transcode_seek(handle);
421 if (ret == MM_ERROR_NONE) {
422 LOGD("Success - Set New Segment pipeline");
424 LOGD("[Null Trancode]");
425 if (_mm_transcode_state_change(handle, GST_STATE_NULL) != MM_ERROR_NONE) {
426 LOGE("ERROR -Null Pipeline");
429 g_mutex_lock(handle->property->thread_mutex);
430 LOGD("[g_mutex_lock]");
431 TRANSCODE_FREE(handle->param);
432 LOGD("g_free(param)");
433 g_cond_signal(handle->property->thread_cond);
434 LOGD("[g_cond_signal]");
435 g_mutex_unlock(handle->property->thread_mutex);
436 LOGD("[g_mutex_unlock]");
443 case GST_MESSAGE_ASYNC_DONE:
445 if (GST_ELEMENT(GST_MESSAGE_SRC(message)) != handle->pipeline)
448 if (gst_element_query_duration(handle->pipeline, fmt, &total_length) && handle->property->total_length == 0) {
449 LOGD("[GST_MESSAGE_ASYNC_DONE] Total Duration: %" G_GUINT64_FORMAT " ", total_length);
450 handle->property->total_length = (unsigned long)(GST_TIME_AS_MSECONDS(total_length));
453 handle->param->async_done = TRUE;
454 LOGD("GST_MESSAGE_ASYNC_DONE");
457 LOGD("[Play Trancode] [%lu ~ %lu]", handle->param->start_pos, handle->property->end_pos);
459 if (_mm_transcode_play(handle) != MM_ERROR_NONE) {
460 LOGE("ERROR - Play Pipeline");
466 case GST_MESSAGE_SEGMENT_DONE:
468 if (GST_ELEMENT(GST_MESSAGE_SRC(message)) != handle->pipeline)
471 handle->param->segment_done = TRUE;
472 LOGD("GST_MESSAGE_SEGMENT_DONE");
476 case GST_MESSAGE_EOS:
479 LOGD("[GST_MESSAGE_EOS] end-of-stream");
481 LOGD("[completed] %s (Transcode ID: %d)", handle->param->outputfile, handle->property->seek_idx++);
482 handle->property->AUDFLAG = 0;
483 handle->property->VIDFLAG = 0;
485 /* Null Transcode *//* Need to fresh filesink's property */
486 LOGD("[Null Trancode]");
487 if (_mm_transcode_state_change(handle, GST_STATE_NULL) != MM_ERROR_NONE) {
488 LOGE("ERROR -Null Pipeline");
491 /* checkpoint once more here (eos) and not unlink when Audio only */
492 if ((handle->param->start_pos > handle->property->total_length && handle->property->total_length != 0) && (handle->property->videoencoder != MM_VIDEOENCODER_NO_USE)) {
493 unlink(handle->param->outputfile);
494 LOGD("[unlink] %s %lu > %lu", handle->param->outputfile, handle->param->start_pos, handle->property->total_length);
496 g_mutex_lock(handle->property->thread_mutex);
497 handle->param->completed = TRUE;
498 handle->property->is_busy = FALSE;
499 g_cond_signal(handle->property->thread_cond);
500 LOGD("===> send completed signal");
501 g_mutex_unlock(handle->property->thread_mutex);
503 LOGD("[MMHandle] %p [msg_cb] %p [msg_cb_param] %p", MMHandle, handle->property->completed_cb, handle->property->completed_cb_param);
505 if (handle->property->progress_cb)
506 handle->property->progress_cb(handle->property->real_duration, handle->property->real_duration, handle->property->progress_cb_param);
508 if (handle->property->completed_cb)
509 handle->property->completed_cb(MM_ERROR_NONE, handle->property->completed_cb_param);
515 /*LOGD("unhandle message"); */
521 static void _mm_transcode_audio_capsfilter(GstCaps *caps, handle_s *handle)
526 LOGE("[ERROR] - handle");
530 if (!handle->encodebin) {
531 LOGE("[ERROR] - handle encodebin");
535 if (!handle->property) {
536 LOGE("[ERROR] - handle property");
541 LOGE("[ERROR] - caps");
545 if (!strcmp(handle->property->aenc, AMRENC))
546 caps = gst_caps_new_simple("audio/x-raw", "rate", G_TYPE_INT, 8000, "channels", G_TYPE_INT, 1, NULL);
547 else if (!strcmp(handle->property->aenc, AACENC))
548 caps = gst_caps_new_simple("audio/x-raw", "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 1, NULL);
550 TRANSCODE_FREE(handle->property->audiodecodename);
551 g_object_set(G_OBJECT(handle->decoder_audp->audflt), "caps", caps, NULL);
553 str = gst_caps_to_string(caps);
556 LOGD("audio decoder capsfilter audiocaps: %s", str);
564 int _mm_transcode_create(handle_s *handle)
566 int ret = MM_ERROR_NONE;
569 LOGE("[ERROR] - handle");
570 return MM_ERROR_INVALID_ARGUMENT;
573 ret = _mm_decodesrcbin_create(handle);
574 if (ret == MM_ERROR_NONE) {
575 LOGD("Success - Create decodesrcbin");
577 LOGE("ERROR - Create decodesrcbin");
581 ret = _mm_encodebin_set_venc_aenc(handle);
582 if (ret == MM_ERROR_NONE) {
583 LOGD("Success - Setup video and audio encoder of encodebin");
585 LOGE("ERROR -Setup video and audio encoder of encodebin ");
589 ret = _mm_encodebin_create(handle);
590 if (ret == MM_ERROR_NONE) {
591 LOGD("Success - Create encodebin");
593 LOGE("ERROR - Create encodebin");
598 ret = _mm_filesink_create(handle);
599 if (ret == MM_ERROR_NONE) {
600 LOGD("Success - Create Filesink");
602 LOGE("ERROR - Create Filesink");
609 static int _mm_transcode_exec(handle_s *handle, handle_param_s *param)
611 int ret = MM_ERROR_NONE;
614 LOGE("[ERROR] - handle");
615 return MM_ERROR_INVALID_ARGUMENT;
618 if (!handle->property) {
619 LOGE("[ERROR] - handle property");
620 return MM_ERROR_TRANSCODE_INTERNAL;
623 if (!param || !param->outputfile) {
624 LOGE("[ERROR] - param");
625 return MM_ERROR_TRANSCODE_INVALID_VALUE;
628 g_mutex_lock(handle->property->thread_mutex);
630 if (handle->property->repeat_thread_exit) {
631 g_mutex_unlock(handle->property->thread_mutex);
632 LOGD("unlock destory");
634 LOGD("start_pos: %lu, duration: %lu, seek_mode: %d output file name: %s\n", param->start_pos, param->duration, param->seek_mode, param->outputfile);
635 handle->param = g_new0(handle_param_s, 1);
636 /*g_value_init (handle->param, G_TYPE_INT); */
638 if (!handle->param) {
639 LOGE("[ERROR] - handle param");
640 return MM_ERROR_TRANSCODE_INTERNAL;
643 handle->param->resolution_width = param->resolution_width;
644 handle->param->resolution_height = param->resolution_height;
645 handle->param->fps_value = param->fps_value;
646 handle->param->start_pos = param->start_pos;
647 handle->param->duration = param->duration;
648 handle->param->seek_mode = param->seek_mode;
649 handle->param->outputfile = malloc(sizeof(gchar) * BUFFER_SIZE);
650 if (!handle->param->outputfile) {
651 TRANSCODE_FREE(handle->param);
652 return MM_ERROR_TRANSCODE_NO_FREE_SPACE;
655 memset(handle->param->outputfile, 0, BUFFER_SIZE);
656 g_strlcpy(handle->param->outputfile, param->outputfile, BUFFER_SIZE);
658 handle->param->seeking = param->seeking;
659 handle->param->async_done = FALSE;
660 handle->param->segment_done = FALSE;
661 handle->param->completed = FALSE;
662 handle->param->printed = 0;
663 LOGD("[SEEK: %d] width: %d height: %d fps_value: %d start_pos: %lu duration: %lu seek_mode: %d outputfile: %s",
664 handle->param->seeking, handle->param->resolution_width, handle->param->resolution_height,
665 handle->param->fps_value, handle->param->start_pos, handle->param->duration,
666 handle->param->seek_mode, handle->param->outputfile);
668 if (handle->property->total_length != 0 && handle->param->start_pos > handle->property->total_length) {
669 LOGD("[SKIP] [%s] because out of duration [%lu < %lu ~ %lu] ", handle->param->outputfile, handle->property->total_length, handle->param->start_pos, handle->param->duration);
670 g_mutex_unlock(handle->property->thread_mutex);
671 LOGD("[thread_mutex unlock]");
673 g_object_set(G_OBJECT(handle->filesink), "location", handle->param->outputfile, NULL);
674 LOGD("[%s] set filesink location", handle->param->outputfile);
676 /* Ready Transcode */
677 if (strlen(handle->param->outputfile) != 0) {
678 LOGD("[Set State: Ready]");
679 ret = _mm_transcode_state_change(handle, GST_STATE_READY);
680 if (ret == MM_ERROR_NONE) {
681 LOGD("Success - Ready pipeline");
683 LOGE("ERROR - Reay pipeline");
684 g_mutex_unlock(handle->property->thread_mutex);
689 if (0 == handle->property->seek_idx) {
690 LOGD("Link Filesink");
692 ret = _mm_filesink_link(handle);
693 if (ret == MM_ERROR_NONE) {
694 LOGD("Success - Link Filesink");
696 LOGE("ERROR - Link Filesink");
697 g_mutex_unlock(handle->property->thread_mutex);
702 g_cond_wait(handle->property->thread_cond, handle->property->thread_mutex);
703 LOGD("<=== get completed signal");
704 g_mutex_unlock(handle->property->thread_mutex);
711 int _mm_transcode_get_stream_info(handle_s *handle)
713 int ret = MM_ERROR_NONE;
716 LOGE("[ERROR] - handle");
717 return MM_ERROR_INVALID_ARGUMENT;
720 if (!handle->property) {
721 LOGE("[ERROR] - handle property");
722 return MM_ERROR_TRANSCODE_INTERNAL;
725 if (strlen(handle->property->sourcefile) == 0) {
726 LOGE("Invalid arguments [sourcefile size 0]\n");
727 return MM_ERROR_INVALID_ARGUMENT;
730 int audio_track_num = 0;
731 int video_track_num = 0;
733 ret = mm_file_get_stream_info(handle->property->sourcefile, &audio_track_num, &video_track_num);
734 if (ret == MM_ERROR_NONE) {
735 LOGD("Success - mm_file_get_stream_info");
737 LOGE("ERROR - mm_file_get_stream_info");
738 return MM_ERROR_TRANSCODE_INTERNAL;
742 handle->property->has_audio_stream = TRUE;
744 handle->property->has_audio_stream = FALSE;
746 LOGD("has_audio_stream: %d", handle->property->has_audio_stream);
749 handle->property->has_video_stream = TRUE;
751 handle->property->has_video_stream = FALSE;
753 LOGD("has_video_stream: %d", handle->property->has_video_stream);
755 if ((handle->property->videoencoder != MM_VIDEOENCODER_NO_USE && !handle->property->has_video_stream) || (handle->property->audioencoder != MM_AUDIOENCODER_NO_USE && !handle->property->has_audio_stream)) {
756 LOGE("No video || audio stream");
757 return MM_ERROR_INVALID_ARGUMENT;
763 int _mm_transcode_link(handle_s *handle)
765 int ret = MM_ERROR_NONE;
768 LOGE("[ERROR] - handle");
769 return MM_ERROR_INVALID_ARGUMENT;
772 ret = _mm_decodesrcbin_link(handle);
773 if (ret == MM_ERROR_NONE) {
774 LOGD("Success - decodesrcbin link");
776 LOGE("ERROR - decodesrcbin link");
780 ret = _mm_encodebin_link(handle);
781 if (ret == MM_ERROR_NONE) {
782 LOGD("Success - encodebin link");
784 LOGE("ERROR - encodebin link");
791 static void _mm_transcode_video_capsfilter(GstCaps *caps, handle_s *handle)
794 LOGE("[ERROR] - handle");
798 if (!handle->property) {
799 LOGE("[ERROR] - handle property");
804 LOGE("[ERROR] - caps");
805 TRANSCODE_FREE(handle->property->videodecodename);
809 LOGD("[First Video Buffer] Set CapsFilter Parameter");
810 _mm_transcode_video_capsfilter_set_parameter(caps, handle);
812 /* Not support enlarge video resolution */
813 LOGD("Execute Resize");
815 /* Not irrelevant to the ratio */
816 handle->param->resolution_height = handle->param->resolution_width * handle->in_height / handle->in_width;
819 LOGD("[Resize] resolution_width: %d, resolution_height: %d", handle->param->resolution_width, handle->param->resolution_height);
820 if (0 == handle->param->resolution_width || 0 == handle->param->resolution_height) {
821 LOGD("[Origin Resolution] Two resolutoin value = 0");
822 handle->param->resolution_width = handle->property->in_width;
823 handle->param->resolution_height = handle->property->in_height;
826 if (handle->param->resolution_width < VIDEO_RESOLUTION_WIDTH_SQCIF || handle->param->resolution_height < VIDEO_RESOLUTION_HEIGHT_SQCIF) {
827 LOGD("The Minimun resolution is SQCIF");
828 handle->param->resolution_width = VIDEO_RESOLUTION_WIDTH_SQCIF;
829 handle->param->resolution_height = VIDEO_RESOLUTION_HEIGHT_SQCIF;
832 if (handle->property->in_width < handle->param->resolution_width || handle->property->in_height < handle->param->resolution_height) {
833 LOGD("[Origin Resolution] resolutoin value > origin resolution");
834 handle->param->resolution_width = handle->property->in_width;
835 handle->param->resolution_height = handle->property->in_height;
838 LOGD("[Call CapsFilter] resolution_width: %d, resolution_height: %d", handle->param->resolution_width, handle->param->resolution_height);
839 _mm_transcode_video_capsfilter_call(handle);
840 TRANSCODE_FREE(handle->property->videodecodename);
843 static void _mm_transcode_video_capsfilter_call(handle_s *handle)
846 LOGE("[ERROR] - handle");
850 if (!handle->decoder_vidp) {
851 LOGE("[ERROR] - handle decoder video process bin");
855 if (!handle->property) {
856 LOGE("[ERROR] - handle property");
860 /* Configure videoscale to use 4-tap scaling for higher quality */
861 LOGD("Input Width: [%d] Input Hieght: [%d] Output Width: [%d], Output Height: [%d]", handle->property->in_width, handle->property->in_height, handle->param->resolution_width, handle->param->resolution_height);
863 g_object_set(G_OBJECT(handle->decoder_vidp->vidflt), "caps", gst_caps_new_simple(handle->property->mime, "format", G_TYPE_STRING, handle->property->format, "width", G_TYPE_INT, handle->param->resolution_width, "height", G_TYPE_INT, handle->param->resolution_height, "framerate", GST_TYPE_FRACTION, handle->property->fps_n, handle->property->fps_d, "pixel-aspect-ratio", GST_TYPE_FRACTION, handle->property->aspect_x, handle->property->aspect_y, NULL), NULL);
866 static void _mm_transcode_video_capsfilter_set_parameter(GstCaps *caps, handle_s *handle)
868 const GValue *par, *fps;
871 LOGE("[ERROR] - handle");
875 if (!handle->property) {
876 LOGE("[ERROR] - handle property");
881 LOGE("[ERROR] - caps");
885 GstStructure *_str = gst_caps_get_structure(caps, 0);
886 handle->property->mime = _mm_check_media_type(caps);
887 LOGD("mime: %s", handle->property->mime);
889 const gchar *format = gst_structure_get_string(_str, "format");
890 g_strlcpy(handle->property->format, format, sizeof(handle->property->format));
892 switch (gst_video_format_from_string(handle->property->format)) {
893 case GST_VIDEO_FORMAT_I420:
894 case GST_VIDEO_FORMAT_RGB:
895 case GST_VIDEO_FORMAT_NV12:
896 LOGD("format: %s", handle->property->format);
899 case GST_VIDEO_FORMAT_UNKNOWN:
900 if (strcmp(handle->property->format, "SN12") == 0 || strcmp(handle->property->format, "ST12") == 0)
901 LOGD("format: %s", handle->property->format);
909 if (!gst_structure_get_int(_str, "width", &handle->property->in_width) || !gst_structure_get_int(_str, "height", &handle->property->in_height))
910 LOGE("error gst_structure_get_int [width] [height]");
912 LOGD("Origin File's Width: [%u] Origin File's Hieght: [%u]", handle->property->in_width, handle->property->in_height);
914 fps = gst_structure_get_value(_str, "framerate");
917 handle->property->fps_n = gst_value_get_fraction_numerator(fps);
918 handle->property->fps_d = gst_value_get_fraction_denominator(fps);
919 LOGD("[Origin framerate] gst_value_get_fraction_numerator: %d, gst_value_get_fraction_denominator: %d", handle->property->fps_n, handle->property->fps_d);
922 if (handle->param->fps_value >= 5 && handle->param->fps_value <= 30 && handle->param->fps_value <= handle->property->fps_n) {
923 handle->property->fps_n = (gint) handle->param->fps_value;
924 handle->property->fps_d = 1;
926 LOGD("[framerate] gst_value_get_fraction_numerator: %d, gst_value_get_fraction_denominator: %d", handle->property->fps_n, handle->property->fps_d);
928 par = gst_structure_get_value(_str, "pixel-aspect-ratio");
930 handle->property->aspect_x = gst_value_get_fraction_numerator(par);
931 handle->property->aspect_y = gst_value_get_fraction_denominator(par);
933 handle->property->aspect_x = handle->property->aspect_y = 1;
935 LOGD("[pixel-aspect-ratio] gst_value_get_fraction_numerator: %d, gst_value_get_fraction_denominator: %d", handle->property->aspect_x, handle->property->aspect_y);
939 int _mm_transcode_set_handle_element(handle_s *handle, const char *in_Filename, mm_containerformat_e containerformat, mm_videoencoder_e videoencoder, mm_audioencoder_e audioencoder)
941 int ret = MM_ERROR_NONE;
944 LOGE("[ERROR] - handle");
945 return MM_ERROR_INVALID_ARGUMENT;
948 if (!handle->property) {
949 LOGE("[ERROR] - handle property");
950 return MM_ERROR_TRANSCODE_INTERNAL;
953 if (in_Filename == NULL) {
954 LOGE("Invalid arguments [filename null]\n");
955 return MM_ERROR_INVALID_ARGUMENT;
958 if (strlen(in_Filename) == 0 || strlen(in_Filename) > BUFFER_SIZE) {
959 LOGE("Invalid arguments [filename size: %d]\n", strlen(in_Filename));
960 return MM_ERROR_INVALID_ARGUMENT;
963 handle->property->sourcefile = malloc(sizeof(char) * BUFFER_SIZE);
964 if (handle->property->sourcefile) {
965 memset(handle->property->sourcefile, 0, BUFFER_SIZE);
966 g_strlcpy(handle->property->sourcefile, in_Filename, BUFFER_SIZE);
967 LOGD("%s", handle->property->sourcefile);
969 LOGE("[ERROR] malloc fail of sourcefile");
970 return MM_ERROR_TRANSCODE_INTERNAL;
973 handle->property->containerformat = containerformat;
974 handle->property->videoencoder = videoencoder;
975 handle->property->audioencoder = audioencoder;
977 LOGD("container format: %d videoencoder:%d, audioencoder: %d", handle->property->containerformat, handle->property->videoencoder, handle->property->audioencoder);
982 int _mm_transcode_set_handle_parameter(handle_param_s *param, unsigned int resolution_width, unsigned int resolution_height, unsigned int fps_value, unsigned long start_pos, unsigned long duration, mm_seek_mode_e seek_mode, const char *out_Filename)
984 int ret = MM_ERROR_NONE;
988 return MM_ERROR_TRANSCODE_INTERNAL;
991 param->resolution_width = resolution_width;
992 param->resolution_height = resolution_height;
993 param->fps_value = fps_value;
995 param->start_pos = start_pos;
996 param->duration = duration;
997 param->seek_mode = seek_mode;
998 LOGD("resolution_width: %d, resolution_height: %d, fps_value: %d, start_pos: %lu, duration: %lu, seek_mode: %d \n",
999 param->resolution_width, param->resolution_height, fps_value, param->start_pos, param->duration, param->seek_mode);
1001 if (start_pos == 0 && duration == 0)
1002 param->seeking = FALSE;
1004 param->seeking = TRUE;
1007 param->outputfile = malloc(sizeof(gchar) * BUFFER_SIZE);
1008 if (!param->outputfile) {
1009 LOGE("[ERROR] outputfile");
1010 return MM_ERROR_TRANSCODE_NO_FREE_SPACE;
1012 memset(param->outputfile, 0, BUFFER_SIZE);
1013 g_strlcpy(param->outputfile, out_Filename, BUFFER_SIZE);
1014 LOGD("%s(%d)", param->outputfile, strlen(out_Filename));
1016 LOGE("out_Filename error");
1017 return MM_ERROR_INVALID_ARGUMENT;
1023 int _mm_transcode_state_change(handle_s *handle, GstState gst_state)
1025 int ret = MM_ERROR_NONE;
1026 GstStateChangeReturn ret_state;
1028 if (gst_state == GST_STATE_NULL)
1029 LOGD("Before - GST_STATE_NULL");
1030 else if (gst_state == GST_STATE_READY)
1031 LOGD("Before - GST_STATE_READY");
1032 else if (gst_state == GST_STATE_PAUSED)
1033 LOGD("Before - GST_STATE_PAUSED");
1034 else if (gst_state == GST_STATE_PLAYING)
1035 LOGD("Before - GST_STATE_PLAYING");
1037 ret_state = gst_element_set_state(handle->pipeline, gst_state);
1038 if (ret_state == GST_STATE_CHANGE_FAILURE) {
1039 if (gst_state == GST_STATE_NULL)
1040 LOGE("ERROR - SET GST_STATE_NULL");
1041 else if (gst_state == GST_STATE_READY)
1042 LOGE("ERROR - SET GST_STATE_READY");
1043 else if (gst_state == GST_STATE_PAUSED)
1044 LOGE("ERROR - SET GST_STATE_PAUSED");
1045 else if (gst_state == GST_STATE_PLAYING)
1046 LOGE("ERROR - SET GST_STATE_PLAYING");
1048 return MM_ERROR_TRANSCODE_INTERNAL;
1050 if (gst_state == GST_STATE_NULL)
1051 LOGD("Success - SET GST_STATE_NULL");
1052 else if (gst_state == GST_STATE_READY)
1053 LOGD("Success - SET GST_STATE_READY");
1054 else if (gst_state == GST_STATE_PAUSED)
1055 LOGD("Success - SET GST_STATE_PAUSED");
1056 else if (gst_state == GST_STATE_PLAYING)
1057 LOGD("Success - SET GST_STATE_PLAYING");
1060 ret_state = gst_element_get_state(handle->pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
1061 if (ret_state == GST_STATE_CHANGE_FAILURE) {
1062 if (gst_state == GST_STATE_NULL)
1063 LOGE("ERROR - GET GST_STATE_NULL");
1064 else if (gst_state == GST_STATE_READY)
1065 LOGE("ERROR - GET GST_STATE_READY");
1066 else if (gst_state == GST_STATE_PAUSED)
1067 LOGE("ERROR - GET GST_STATE_PAUSED");
1068 else if (gst_state == GST_STATE_PLAYING)
1069 LOGE("ERROR - GET GST_STATE_PLAYING");
1071 return MM_ERROR_TRANSCODE_INTERNAL;
1073 if (gst_state == GST_STATE_NULL)
1074 LOGD("Success - GET GST_STATE_NULL");
1075 else if (gst_state == GST_STATE_READY)
1076 LOGD("Success - GET GST_STATE_READY");
1077 else if (gst_state == GST_STATE_PAUSED)
1078 LOGD("Success - GET GST_STATE_PAUSED");
1079 else if (gst_state == GST_STATE_PLAYING)
1080 LOGD("Success - GET GST_STATE_PLAYING");
1086 int _mm_transcode_param_flush(handle_s *handle)
1088 int ret = MM_ERROR_NONE;
1091 LOGE("[ERROR] - handle");
1092 return MM_ERROR_INVALID_ARGUMENT;
1095 if (!handle->encodebin) {
1096 LOGE("[ERROR] - handle encodebin");
1097 return MM_ERROR_TRANSCODE_INTERNAL;
1100 if (!handle->property) {
1101 LOGE("[ERROR] - handle property");
1102 return MM_ERROR_TRANSCODE_INTERNAL;
1105 handle->property->linked_vidoutbin = FALSE;
1106 handle->property->linked_audoutbin = FALSE;
1107 handle->encodebin->encodebin_profile = 0;
1108 handle->property->AUDFLAG = 0;
1109 handle->property->VIDFLAG = 0;
1110 handle->encodebin->audio_event_probe_id = 0;
1111 handle->encodebin->video_event_probe_id = 0;
1113 handle->property->total_length = 0;
1114 handle->property->repeat_thread_exit = FALSE;
1115 handle->property->is_busy = FALSE;
1116 handle->property->audio_cb_probe_id = 0;
1117 handle->property->video_cb_probe_id = 0;
1118 handle->property->progress_event_id = 0;
1119 handle->property->seek_idx = 0;
1124 static int _mm_transcode_play(handle_s *handle)
1126 int ret = MM_ERROR_NONE;
1129 LOGE("[ERROR] - handle");
1130 return MM_ERROR_INVALID_ARGUMENT;
1133 if (!handle->property) {
1134 LOGE("[ERROR] - handle property");
1135 return MM_ERROR_TRANSCODE_INTERNAL;
1138 ret = _mm_transcode_state_change(handle, GST_STATE_PLAYING);
1139 if (ret != MM_ERROR_NONE) {
1140 LOGE("ERROR -Playing Pipeline");
1144 LOGD("[SEEK: %d] width: %d height: %d start_pos: %lu duration: %lu (%lu) seek_mode: %d outputfile: %s",
1145 handle->param->seeking, handle->param->resolution_width, handle->param->resolution_height,
1146 handle->param->start_pos, handle->param->duration, handle->property->end_pos,
1147 handle->param->seek_mode, handle->param->outputfile);
1149 if (!handle->property->progress_event_id) {
1150 handle->property->progress_event_id = g_timeout_add(LAZY_PAUSE_TIMEOUT_MSEC, (GSourceFunc) _mm_cb_print_position, handle);
1151 LOGD("Timer (id=[%d], timeout=[%d ms])\n", handle->property->progress_event_id, LAZY_PAUSE_TIMEOUT_MSEC);
1157 static int _mm_transcode_seek(handle_s *handle)
1159 int ret = MM_ERROR_NONE;
1162 LOGE("[ERROR] - handle");
1163 return MM_ERROR_INVALID_ARGUMENT;
1166 if (!handle->property) {
1167 LOGE("[ERROR] - handle property");
1168 return MM_ERROR_TRANSCODE_INTERNAL;
1171 GList *walk_element = handle->property->sink_elements;
1172 gint64 start_pos, end_pos;
1174 GstSeekFlags _Flags = GST_SEEK_FLAG_NONE;
1176 start_pos = handle->param->start_pos * G_GINT64_CONSTANT(1000000);
1177 handle->property->end_pos = handle->param->start_pos + handle->param->duration;
1179 if (handle->param->start_pos > handle->property->total_length && handle->property->seek_idx) {
1180 LOGE("[%lu ~ %lu] out of %lu",
1181 handle->param->start_pos, handle->property->end_pos, handle->property->total_length);
1182 return MM_ERROR_TRANSCODE_SEEK_FAILED;
1184 if (handle->param->duration != 0) {
1185 end_pos = start_pos + handle->param->duration * G_GINT64_CONSTANT(1000000);
1186 } else if (handle->param->duration == 0) {
1187 /* seek to origin file length */
1188 end_pos = handle->property->total_length * G_GINT64_CONSTANT(1000000);
1191 LOGD("seek time : [ (%lu msec) : (%lu msec) ]\n",
1192 handle->param->start_pos, handle->property->end_pos);
1194 while (walk_element) {
1195 GstElement *seekable_element = GST_ELEMENT(walk_element->data);
1197 if (!seekable_element) {
1198 LOGE("ERROR - seekable");
1199 return MM_ERROR_TRANSCODE_INTERNAL;
1202 if (handle->param->seek_mode == MM_SEEK_ACCURATE)
1203 _Flags = GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH;
1204 else if (handle->param->seek_mode == MM_SEEK_INACCURATE)
1205 _Flags = GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_FLUSH;
1207 if (!gst_element_seek(seekable_element, rate, GST_FORMAT_TIME, _Flags, GST_SEEK_TYPE_SET, start_pos, GST_SEEK_TYPE_SET, end_pos)) {
1208 LOGE("ERROR - gst_element_seek (on) event : %s", GST_OBJECT_NAME(GST_OBJECT_CAST(seekable_element)));
1209 return MM_ERROR_TRANSCODE_SEEK_FAILED;
1213 walk_element = g_list_next(walk_element);
1220 int _mm_transcode_thread(handle_s *handle)
1222 int ret = MM_ERROR_NONE;
1225 LOGE("[ERROR] - handle");
1226 return MM_ERROR_INVALID_ARGUMENT;
1229 if (!handle->property) {
1230 LOGE("[ERROR] - handle property");
1231 return MM_ERROR_TRANSCODE_INTERNAL;
1234 if (!handle->property->thread_mutex) {
1235 handle->property->thread_mutex = g_new(GMutex, 1);
1236 g_mutex_init(handle->property->thread_mutex);
1237 LOGD("create thread_mutex: %p", handle->property->thread_mutex);
1239 LOGE("ERROR - thread_mutex is already created");
1242 if (!handle->property->thread_exit_mutex) {
1243 handle->property->thread_exit_mutex = g_new(GMutex, 1);
1244 g_mutex_init(handle->property->thread_exit_mutex);
1245 LOGD("create exit mutex: %p", handle->property->thread_exit_mutex);
1247 LOGE("ERROR - thread_exit_mutex is already created");
1250 /* These are a communicator for thread */
1251 if (!handle->property->queue) {
1252 handle->property->queue = g_async_queue_new();
1253 LOGD("create async queue: %p", handle->property->queue);
1255 LOGE("ERROR - async queue is already created");
1258 if (!handle->property->thread_cond) {
1259 handle->property->thread_cond = g_new(GCond, 1);
1260 g_cond_init(handle->property->thread_cond);
1261 LOGD("create thread cond: %p", handle->property->thread_cond);
1263 LOGE("thread cond is already created");
1266 /* create threads */
1267 LOGD("create thread");
1268 handle->property->thread = g_thread_new(NULL, (GThreadFunc)_mm_transcode_thread_repeate, (gpointer)handle);
1269 if (!handle->property->thread) {
1270 LOGE("ERROR - create thread");
1271 return MM_ERROR_TRANSCODE_INTERNAL;
1273 LOGD("create thread: %p", handle->property->thread);
1279 static gpointer _mm_transcode_thread_repeate(gpointer data)
1281 handle_s *handle = (handle_s *) data;
1282 int ret = MM_ERROR_NONE;
1285 LOGE("[ERROR] - handle");
1289 if (!handle->property) {
1290 LOGE("[ERROR] - handle property");
1296 int length = g_async_queue_length(handle->property->queue);
1298 LOGD("[QUEUE #] %d", length);
1299 handle->property->is_busy = TRUE;
1302 handle_param_s *pop_data = (handle_param_s *) g_async_queue_pop(handle->property->queue);
1304 if (handle->property->repeat_thread_exit || !handle->property->is_busy) {
1305 LOGD("[Receive Last Queue]");
1309 LOGD("[pop queue] resolution_width: %d, resolution_height: %d, start_pos: %lu, duration: %lu, seek_mode: %d outputfile: %s\n",
1310 pop_data->resolution_width, pop_data->resolution_height, pop_data->start_pos,
1311 pop_data->duration, pop_data->seek_mode, pop_data->outputfile);
1314 ret = _mm_transcode_exec(handle, pop_data);
1315 if (ret == MM_ERROR_NONE) {
1316 LOGD("Success - transcode_exec");
1318 LOGD("Destroy - transcode_exec");
1319 LOGD("<=== get exit (%d) signal", handle->property->repeat_thread_exit);
1324 LOGD("exit thread");