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") || g_strrstr(feature_name, "v4l2")) {
204 /* emit autoplug-select to see what we should do with it. */
205 LOGD("SKIP %s Codec", feature_name);
206 return GST_AUTOPLUG_SELECT_SKIP;
208 handle->property->videodecodename = (char *)malloc(sizeof(char) * ENC_BUFFER_SIZE);
209 if (handle->property->videodecodename == NULL) {
210 LOGE("videodecodename is NULL");
211 return GST_AUTOPLUG_SELECT_TRY;
213 memset(handle->property->videodecodename, 0, ENC_BUFFER_SIZE);
214 g_strlcpy(handle->property->videodecodename, feature_name, ENC_BUFFER_SIZE);
215 LOGD("[video decode name %s : %s]", caps_str, handle->property->videodecodename);
219 return GST_AUTOPLUG_SELECT_TRY;
222 void _mm_cb_decoder_newpad_encoder(GstElement *decodebin, GstPad *pad, handle_s *handle)
225 LOGE("[ERROR] - handle");
229 if (!handle->decoder_vidp) {
230 LOGE("[ERROR] - handle decoder_vidp");
234 if (!handle->decoder_audp) {
235 LOGE("[ERROR] - handle decoder_audp");
239 if (!handle->encodebin) {
240 LOGE("[ERROR] - handle encodebin");
244 if (!handle->property) {
245 LOGE("[ERROR] - handle property");
249 LOGD("[============ new-decoded-pad ============]");
250 handle->property->caps = gst_pad_query_caps(pad, NULL);
251 const gchar *mime = _mm_check_media_type(handle->property->caps);
254 LOGE("[ERROR] - mime");
258 if (g_strrstr(mime, "video")) {
259 handle->property->linked_vidoutbin = TRUE;
262 if (gst_pad_is_linked(pad)) {
265 if (gst_pad_link(pad, (GstPad *) handle->decoder_vidp->sinkdecvideopad) != GST_PAD_LINK_OK)
266 LOGE("Error [pad - sinkdecvideopad]");
268 LOGD("Success [pad - sinkdecvideopad]");
271 } else if (g_strrstr(mime, "audio")) {
272 handle->property->linked_audoutbin = TRUE;
275 if (gst_pad_is_linked(pad)) {
278 if (gst_pad_link(pad, (GstPad *) handle->decoder_audp->sinkdecaudiopad) != GST_PAD_LINK_OK)
279 LOGE("Error [pad - sinkdecaudiopad]");
281 LOGD("Success [pad - sinkdecaudiopad]");
285 LOGD("gst structure error");
289 gboolean _mm_cb_print_position(handle_s *handle)
291 GstFormat fmt = GST_FORMAT_TIME;
295 LOGE("[ERROR] - handle");
299 if (!handle->property) {
300 LOGE("[ERROR] - handle property");
304 /* To avoid gst_element_query_position bs */
305 if (!handle->property->repeat_thread_exit) {
306 if (gst_element_query_position(handle->pipeline, fmt, &pos)) {
307 unsigned long current_pos = (unsigned long)(GST_TIME_AS_MSECONDS(pos));
308 if (handle->param->seeking == FALSE) {
309 handle->property->current_pos = current_pos;
310 handle->property->real_duration = handle->property->total_length;
311 } else if (handle->param->seeking == TRUE) {
312 handle->property->current_pos = current_pos - handle->param->start_pos;
313 if (handle->param->duration != 0) {
314 if (handle->param->start_pos + handle->param->duration > handle->property->total_length)
315 handle->property->real_duration = handle->property->total_length - handle->param->start_pos;
317 handle->property->real_duration = handle->param->duration;
318 /* seek to origin file length */
319 } else if (handle->param->duration == 0) {
320 handle->property->real_duration = handle->property->total_length - handle->param->start_pos;
324 if (handle->property->current_pos <= handle->property->real_duration) {
325 /* 2 = 1000 / 500 minimum printed cnt for last buffer */
326 if (handle->property->current_pos == 0 && handle->param->printed > 2)
327 handle->property->current_pos = handle->property->real_duration;
329 if (handle->property->progress_cb) {
330 /* for first buffer */
331 if (0 == handle->param->printed)
332 handle->property->current_pos = 0;
333 handle->property->progress_cb(handle->property->current_pos, handle->property->real_duration, handle->property->progress_cb_param);
334 handle->param->printed++;
343 gboolean _mm_cb_transcode_bus(GstBus *bus, GstMessage *message, gpointer userdata)
345 handle_s *handle = (handle_s *)userdata;
346 int ret = MM_ERROR_NONE;
349 LOGE("[ERROR] - handle");
353 if (!handle->property) {
354 LOGE("[ERROR] - handle property");
359 GstFormat fmt = GST_FORMAT_TIME;
360 MMHandleType MMHandle = (MMHandleType)handle;
362 switch (GST_MESSAGE_TYPE(message)) {
363 case GST_MESSAGE_ERROR:
367 gst_message_parse_error(message, &err, &debug);
370 LOGW("Fail to parse error message");
374 LOGE("[Source: %s] Error: %s", GST_OBJECT_NAME(GST_OBJECT_CAST(GST_ELEMENT(GST_MESSAGE_SRC(message)))), err->message);
376 ret = mm_transcode_cancel(MMHandle);
377 if (ret == MM_ERROR_NONE) {
378 LOGD("Success - Cancel Transcode");
380 LOGE("ERROR - Cancel Transcode");
387 TRANSCODE_FREE(debug);
388 TRANSCODE_FREE(handle->param);
389 /* g_main_loop_quit (handle->loop); */
393 case GST_MESSAGE_STATE_CHANGED:
395 if (GST_ELEMENT(GST_MESSAGE_SRC(message)) != handle->pipeline)
398 GstState State_Old, State_New, State_Pending;
399 gst_message_parse_state_changed(message, &State_Old, &State_New, &State_Pending);
401 LOGD("[Source: %s] [State: %d -> %d]", GST_OBJECT_NAME(GST_OBJECT_CAST(GST_ELEMENT(GST_MESSAGE_SRC(message)))), State_Old, State_New);
403 if (State_Old == GST_STATE_NULL && State_New == GST_STATE_READY) {
404 LOGD("[Set State: Pause]");
405 /* Pause Transcode */
406 ret = _mm_transcode_state_change(handle, GST_STATE_PAUSED);
407 if (ret == MM_ERROR_NONE) {
408 LOGD("Success - Pause pipeline");
410 LOGE("ERROR - Pause pipeline");
415 if (State_Old == GST_STATE_READY && State_New == GST_STATE_PAUSED) {
417 LOGD("[%s] Start New Segment pipeline", handle->param->outputfile);
418 ret = _mm_transcode_seek(handle);
420 if (ret == MM_ERROR_NONE) {
421 LOGD("Success - Set New Segment pipeline");
423 LOGD("[Null Trancode]");
424 if (_mm_transcode_state_change(handle, GST_STATE_NULL) != MM_ERROR_NONE) {
425 LOGE("ERROR -Null Pipeline");
428 g_mutex_lock(handle->property->thread_mutex);
429 LOGD("[g_mutex_lock]");
430 TRANSCODE_FREE(handle->param);
431 LOGD("g_free(param)");
432 g_cond_signal(handle->property->thread_cond);
433 LOGD("[g_cond_signal]");
434 g_mutex_unlock(handle->property->thread_mutex);
435 LOGD("[g_mutex_unlock]");
442 case GST_MESSAGE_ASYNC_DONE:
444 if (GST_ELEMENT(GST_MESSAGE_SRC(message)) != handle->pipeline)
447 if (gst_element_query_duration(handle->pipeline, fmt, &total_length) && handle->property->total_length == 0) {
448 LOGD("[GST_MESSAGE_ASYNC_DONE] Total Duration: %" G_GUINT64_FORMAT " ", total_length);
449 handle->property->total_length = (unsigned long)(GST_TIME_AS_MSECONDS(total_length));
452 handle->param->async_done = TRUE;
453 LOGD("GST_MESSAGE_ASYNC_DONE");
456 LOGD("[Play Trancode] [%lu ~ %lu]", handle->param->start_pos, handle->property->end_pos);
458 if (_mm_transcode_play(handle) != MM_ERROR_NONE) {
459 LOGE("ERROR - Play Pipeline");
465 case GST_MESSAGE_SEGMENT_DONE:
467 if (GST_ELEMENT(GST_MESSAGE_SRC(message)) != handle->pipeline)
470 handle->param->segment_done = TRUE;
471 LOGD("GST_MESSAGE_SEGMENT_DONE");
475 case GST_MESSAGE_EOS:
478 LOGD("[GST_MESSAGE_EOS] end-of-stream");
480 LOGD("[completed] %s (Transcode ID: %d)", handle->param->outputfile, handle->property->seek_idx++);
481 handle->property->AUDFLAG = 0;
482 handle->property->VIDFLAG = 0;
484 /* Null Transcode *//* Need to fresh filesink's property */
485 LOGD("[Null Trancode]");
486 if (_mm_transcode_state_change(handle, GST_STATE_NULL) != MM_ERROR_NONE) {
487 LOGE("ERROR -Null Pipeline");
490 /* checkpoint once more here (eos) and not unlink when Audio only */
491 if ((handle->param->start_pos > handle->property->total_length && handle->property->total_length != 0) && (handle->property->videoencoder != MM_VIDEOENCODER_NO_USE)) {
492 unlink(handle->param->outputfile);
493 LOGD("[unlink] %s %lu > %lu", handle->param->outputfile, handle->param->start_pos, handle->property->total_length);
495 g_mutex_lock(handle->property->thread_mutex);
496 handle->param->completed = TRUE;
497 handle->property->is_busy = FALSE;
498 g_cond_signal(handle->property->thread_cond);
499 LOGD("===> send completed signal");
500 g_mutex_unlock(handle->property->thread_mutex);
502 LOGD("[MMHandle] %p [msg_cb] %p [msg_cb_param] %p", MMHandle, handle->property->completed_cb, handle->property->completed_cb_param);
504 if (handle->property->progress_cb)
505 handle->property->progress_cb(handle->property->real_duration, handle->property->real_duration, handle->property->progress_cb_param);
507 if (handle->property->completed_cb)
508 handle->property->completed_cb(MM_ERROR_NONE, handle->property->completed_cb_param);
514 /*LOGD("unhandle message"); */
520 static void _mm_transcode_audio_capsfilter(GstCaps *caps, handle_s *handle)
525 LOGE("[ERROR] - handle");
529 if (!handle->encodebin) {
530 LOGE("[ERROR] - handle encodebin");
534 if (!handle->property) {
535 LOGE("[ERROR] - handle property");
540 LOGE("[ERROR] - caps");
544 if (!strcmp(handle->property->aenc, AMRENC))
545 caps = gst_caps_new_simple("audio/x-raw", "rate", G_TYPE_INT, 8000, "channels", G_TYPE_INT, 1, NULL);
546 else if (!strcmp(handle->property->aenc, AACENC))
547 caps = gst_caps_new_simple("audio/x-raw", "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 1, NULL);
549 TRANSCODE_FREE(handle->property->audiodecodename);
550 g_object_set(G_OBJECT(handle->decoder_audp->audflt), "caps", caps, NULL);
552 str = gst_caps_to_string(caps);
555 LOGD("audio decoder capsfilter audiocaps: %s", str);
563 int _mm_transcode_create(handle_s *handle)
565 int ret = MM_ERROR_NONE;
568 LOGE("[ERROR] - handle");
569 return MM_ERROR_INVALID_ARGUMENT;
572 ret = _mm_decodesrcbin_create(handle);
573 if (ret == MM_ERROR_NONE) {
574 LOGD("Success - Create decodesrcbin");
576 LOGE("ERROR - Create decodesrcbin");
580 ret = _mm_encodebin_set_venc_aenc(handle);
581 if (ret == MM_ERROR_NONE) {
582 LOGD("Success - Setup video and audio encoder of encodebin");
584 LOGE("ERROR -Setup video and audio encoder of encodebin ");
588 ret = _mm_encodebin_create(handle);
589 if (ret == MM_ERROR_NONE) {
590 LOGD("Success - Create encodebin");
592 LOGE("ERROR - Create encodebin");
597 ret = _mm_filesink_create(handle);
598 if (ret == MM_ERROR_NONE) {
599 LOGD("Success - Create Filesink");
601 LOGE("ERROR - Create Filesink");
608 static int _mm_transcode_exec(handle_s *handle, handle_param_s *param)
610 int ret = MM_ERROR_NONE;
613 LOGE("[ERROR] - handle");
614 return MM_ERROR_INVALID_ARGUMENT;
617 if (!handle->property) {
618 LOGE("[ERROR] - handle property");
619 return MM_ERROR_TRANSCODE_INTERNAL;
622 if (!param || !param->outputfile) {
623 LOGE("[ERROR] - param");
624 return MM_ERROR_TRANSCODE_INVALID_VALUE;
627 g_mutex_lock(handle->property->thread_mutex);
629 if (handle->property->repeat_thread_exit) {
630 g_mutex_unlock(handle->property->thread_mutex);
631 LOGD("unlock destory");
633 LOGD("start_pos: %lu, duration: %lu, seek_mode: %d output file name: %s\n", param->start_pos, param->duration, param->seek_mode, param->outputfile);
634 handle->param = g_new0(handle_param_s, 1);
635 /*g_value_init (handle->param, G_TYPE_INT); */
637 if (!handle->param) {
638 LOGE("[ERROR] - handle param");
639 return MM_ERROR_TRANSCODE_INTERNAL;
642 handle->param->resolution_width = param->resolution_width;
643 handle->param->resolution_height = param->resolution_height;
644 handle->param->fps_value = param->fps_value;
645 handle->param->start_pos = param->start_pos;
646 handle->param->duration = param->duration;
647 handle->param->seek_mode = param->seek_mode;
648 handle->param->outputfile = malloc(sizeof(gchar) * BUFFER_SIZE);
649 if (!handle->param->outputfile) {
650 TRANSCODE_FREE(handle->param);
651 return MM_ERROR_TRANSCODE_NO_FREE_SPACE;
654 memset(handle->param->outputfile, 0, BUFFER_SIZE);
655 g_strlcpy(handle->param->outputfile, param->outputfile, BUFFER_SIZE);
657 handle->param->seeking = param->seeking;
658 handle->param->async_done = FALSE;
659 handle->param->segment_done = FALSE;
660 handle->param->completed = FALSE;
661 handle->param->printed = 0;
662 LOGD("[SEEK: %d] width: %d height: %d fps_value: %d start_pos: %lu duration: %lu seek_mode: %d outputfile: %s",
663 handle->param->seeking, handle->param->resolution_width, handle->param->resolution_height,
664 handle->param->fps_value, handle->param->start_pos, handle->param->duration,
665 handle->param->seek_mode, handle->param->outputfile);
667 if (handle->property->total_length != 0 && handle->param->start_pos > handle->property->total_length) {
668 LOGD("[SKIP] [%s] because out of duration [%lu < %lu ~ %lu] ", handle->param->outputfile, handle->property->total_length, handle->param->start_pos, handle->param->duration);
669 g_mutex_unlock(handle->property->thread_mutex);
670 LOGD("[thread_mutex unlock]");
672 g_object_set(G_OBJECT(handle->filesink), "location", handle->param->outputfile, NULL);
673 LOGD("[%s] set filesink location", handle->param->outputfile);
675 /* Ready Transcode */
676 if (strlen(handle->param->outputfile) != 0) {
677 LOGD("[Set State: Ready]");
678 ret = _mm_transcode_state_change(handle, GST_STATE_READY);
679 if (ret == MM_ERROR_NONE) {
680 LOGD("Success - Ready pipeline");
682 LOGE("ERROR - Reay pipeline");
683 g_mutex_unlock(handle->property->thread_mutex);
688 if (0 == handle->property->seek_idx) {
689 LOGD("Link Filesink");
691 ret = _mm_filesink_link(handle);
692 if (ret == MM_ERROR_NONE) {
693 LOGD("Success - Link Filesink");
695 LOGE("ERROR - Link Filesink");
696 g_mutex_unlock(handle->property->thread_mutex);
701 g_cond_wait(handle->property->thread_cond, handle->property->thread_mutex);
702 LOGD("<=== get completed signal");
703 g_mutex_unlock(handle->property->thread_mutex);
710 int _mm_transcode_get_stream_info(handle_s *handle)
712 int ret = MM_ERROR_NONE;
715 LOGE("[ERROR] - handle");
716 return MM_ERROR_INVALID_ARGUMENT;
719 if (!handle->property) {
720 LOGE("[ERROR] - handle property");
721 return MM_ERROR_TRANSCODE_INTERNAL;
724 if (strlen(handle->property->sourcefile) == 0) {
725 LOGE("Invalid arguments [sourcefile size 0]\n");
726 return MM_ERROR_INVALID_ARGUMENT;
729 int audio_track_num = 0;
730 int video_track_num = 0;
732 ret = mm_file_get_stream_info(handle->property->sourcefile, &audio_track_num, &video_track_num);
733 if (ret == MM_ERROR_NONE) {
734 LOGD("Success - mm_file_get_stream_info");
736 LOGE("ERROR - mm_file_get_stream_info");
737 return MM_ERROR_TRANSCODE_INTERNAL;
741 handle->property->has_audio_stream = TRUE;
743 handle->property->has_audio_stream = FALSE;
745 LOGD("has_audio_stream: %d", handle->property->has_audio_stream);
748 handle->property->has_video_stream = TRUE;
750 handle->property->has_video_stream = FALSE;
752 LOGD("has_video_stream: %d", handle->property->has_video_stream);
754 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)) {
755 LOGE("No video || audio stream");
756 return MM_ERROR_INVALID_ARGUMENT;
762 int _mm_transcode_link(handle_s *handle)
764 int ret = MM_ERROR_NONE;
767 LOGE("[ERROR] - handle");
768 return MM_ERROR_INVALID_ARGUMENT;
771 ret = _mm_decodesrcbin_link(handle);
772 if (ret == MM_ERROR_NONE) {
773 LOGD("Success - decodesrcbin link");
775 LOGE("ERROR - decodesrcbin link");
779 ret = _mm_encodebin_link(handle);
780 if (ret == MM_ERROR_NONE) {
781 LOGD("Success - encodebin link");
783 LOGE("ERROR - encodebin link");
790 static void _mm_transcode_video_capsfilter(GstCaps *caps, handle_s *handle)
793 LOGE("[ERROR] - handle");
797 if (!handle->property) {
798 LOGE("[ERROR] - handle property");
803 LOGE("[ERROR] - caps");
804 TRANSCODE_FREE(handle->property->videodecodename);
808 LOGD("[First Video Buffer] Set CapsFilter Parameter");
809 _mm_transcode_video_capsfilter_set_parameter(caps, handle);
811 /* Not support enlarge video resolution */
812 LOGD("Execute Resize");
814 /* Not irrelevant to the ratio */
815 handle->param->resolution_height = handle->param->resolution_width * handle->in_height / handle->in_width;
818 LOGD("[Resize] resolution_width: %d, resolution_height: %d", handle->param->resolution_width, handle->param->resolution_height);
819 if (0 == handle->param->resolution_width || 0 == handle->param->resolution_height) {
820 LOGD("[Origin Resolution] Two resolutoin value = 0");
821 handle->param->resolution_width = handle->property->in_width;
822 handle->param->resolution_height = handle->property->in_height;
825 if (handle->param->resolution_width < VIDEO_RESOLUTION_WIDTH_SQCIF || handle->param->resolution_height < VIDEO_RESOLUTION_HEIGHT_SQCIF) {
826 LOGD("The Minimun resolution is SQCIF");
827 handle->param->resolution_width = VIDEO_RESOLUTION_WIDTH_SQCIF;
828 handle->param->resolution_height = VIDEO_RESOLUTION_HEIGHT_SQCIF;
831 if (handle->property->in_width < handle->param->resolution_width || handle->property->in_height < handle->param->resolution_height) {
832 LOGD("[Origin Resolution] resolutoin value > origin resolution");
833 handle->param->resolution_width = handle->property->in_width;
834 handle->param->resolution_height = handle->property->in_height;
837 LOGD("[Call CapsFilter] resolution_width: %d, resolution_height: %d", handle->param->resolution_width, handle->param->resolution_height);
838 _mm_transcode_video_capsfilter_call(handle);
839 TRANSCODE_FREE(handle->property->videodecodename);
842 static void _mm_transcode_video_capsfilter_call(handle_s *handle)
845 LOGE("[ERROR] - handle");
849 if (!handle->decoder_vidp) {
850 LOGE("[ERROR] - handle decoder video process bin");
854 if (!handle->property) {
855 LOGE("[ERROR] - handle property");
859 /* Configure videoscale to use 4-tap scaling for higher quality */
860 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);
862 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);
865 static void _mm_transcode_video_capsfilter_set_parameter(GstCaps *caps, handle_s *handle)
867 const GValue *par, *fps;
870 LOGE("[ERROR] - handle");
874 if (!handle->property) {
875 LOGE("[ERROR] - handle property");
880 LOGE("[ERROR] - caps");
884 GstStructure *_str = gst_caps_get_structure(caps, 0);
885 handle->property->mime = _mm_check_media_type(caps);
886 LOGD("mime: %s", handle->property->mime);
888 const gchar *format = gst_structure_get_string(_str, "format");
889 g_strlcpy(handle->property->format, format, sizeof(handle->property->format));
891 switch (gst_video_format_from_string(handle->property->format)) {
892 case GST_VIDEO_FORMAT_I420:
893 case GST_VIDEO_FORMAT_RGB:
894 case GST_VIDEO_FORMAT_NV12:
895 LOGD("format: %s", handle->property->format);
898 case GST_VIDEO_FORMAT_UNKNOWN:
899 if (strcmp(handle->property->format, "SN12") == 0 || strcmp(handle->property->format, "ST12") == 0)
900 LOGD("format: %s", handle->property->format);
908 if (!gst_structure_get_int(_str, "width", &handle->property->in_width) || !gst_structure_get_int(_str, "height", &handle->property->in_height))
909 LOGE("error gst_structure_get_int [width] [height]");
911 LOGD("Origin File's Width: [%u] Origin File's Hieght: [%u]", handle->property->in_width, handle->property->in_height);
913 fps = gst_structure_get_value(_str, "framerate");
916 handle->property->fps_n = gst_value_get_fraction_numerator(fps);
917 handle->property->fps_d = gst_value_get_fraction_denominator(fps);
918 LOGD("[Origin framerate] gst_value_get_fraction_numerator: %d, gst_value_get_fraction_denominator: %d", handle->property->fps_n, handle->property->fps_d);
921 if (handle->param->fps_value >= 5 && handle->param->fps_value <= 30 && handle->param->fps_value <= handle->property->fps_n) {
922 handle->property->fps_n = (gint) handle->param->fps_value;
923 handle->property->fps_d = 1;
925 LOGD("[framerate] gst_value_get_fraction_numerator: %d, gst_value_get_fraction_denominator: %d", handle->property->fps_n, handle->property->fps_d);
927 par = gst_structure_get_value(_str, "pixel-aspect-ratio");
929 handle->property->aspect_x = gst_value_get_fraction_numerator(par);
930 handle->property->aspect_y = gst_value_get_fraction_denominator(par);
932 handle->property->aspect_x = handle->property->aspect_y = 1;
934 LOGD("[pixel-aspect-ratio] gst_value_get_fraction_numerator: %d, gst_value_get_fraction_denominator: %d", handle->property->aspect_x, handle->property->aspect_y);
938 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)
940 int ret = MM_ERROR_NONE;
943 LOGE("[ERROR] - handle");
944 return MM_ERROR_INVALID_ARGUMENT;
947 if (!handle->property) {
948 LOGE("[ERROR] - handle property");
949 return MM_ERROR_TRANSCODE_INTERNAL;
952 if (in_Filename == NULL) {
953 LOGE("Invalid arguments [filename null]\n");
954 return MM_ERROR_INVALID_ARGUMENT;
957 if (strlen(in_Filename) == 0 || strlen(in_Filename) > BUFFER_SIZE) {
958 LOGE("Invalid arguments [filename size: %d]\n", strlen(in_Filename));
959 return MM_ERROR_INVALID_ARGUMENT;
962 handle->property->sourcefile = malloc(sizeof(char) * BUFFER_SIZE);
963 if (handle->property->sourcefile) {
964 memset(handle->property->sourcefile, 0, BUFFER_SIZE);
965 g_strlcpy(handle->property->sourcefile, in_Filename, BUFFER_SIZE);
966 LOGD("%s", handle->property->sourcefile);
968 LOGE("[ERROR] malloc fail of sourcefile");
969 return MM_ERROR_TRANSCODE_INTERNAL;
972 handle->property->containerformat = containerformat;
973 handle->property->videoencoder = videoencoder;
974 handle->property->audioencoder = audioencoder;
976 LOGD("container format: %d videoencoder:%d, audioencoder: %d", handle->property->containerformat, handle->property->videoencoder, handle->property->audioencoder);
981 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)
983 int ret = MM_ERROR_NONE;
987 return MM_ERROR_TRANSCODE_INTERNAL;
990 param->resolution_width = resolution_width;
991 param->resolution_height = resolution_height;
992 param->fps_value = fps_value;
994 param->start_pos = start_pos;
995 param->duration = duration;
996 param->seek_mode = seek_mode;
997 LOGD("resolution_width: %d, resolution_height: %d, fps_value: %d, start_pos: %lu, duration: %lu, seek_mode: %d \n",
998 param->resolution_width, param->resolution_height, fps_value, param->start_pos, param->duration, param->seek_mode);
1000 if (start_pos == 0 && duration == 0)
1001 param->seeking = FALSE;
1003 param->seeking = TRUE;
1006 param->outputfile = malloc(sizeof(gchar) * BUFFER_SIZE);
1007 if (!param->outputfile) {
1008 LOGE("[ERROR] outputfile");
1009 return MM_ERROR_TRANSCODE_NO_FREE_SPACE;
1011 memset(param->outputfile, 0, BUFFER_SIZE);
1012 g_strlcpy(param->outputfile, out_Filename, BUFFER_SIZE);
1013 LOGD("%s(%d)", param->outputfile, strlen(out_Filename));
1015 LOGE("out_Filename error");
1016 return MM_ERROR_INVALID_ARGUMENT;
1022 int _mm_transcode_state_change(handle_s *handle, GstState gst_state)
1024 int ret = MM_ERROR_NONE;
1025 GstStateChangeReturn ret_state;
1027 if (gst_state == GST_STATE_NULL)
1028 LOGD("Before - GST_STATE_NULL");
1029 else if (gst_state == GST_STATE_READY)
1030 LOGD("Before - GST_STATE_READY");
1031 else if (gst_state == GST_STATE_PAUSED)
1032 LOGD("Before - GST_STATE_PAUSED");
1033 else if (gst_state == GST_STATE_PLAYING)
1034 LOGD("Before - GST_STATE_PLAYING");
1036 ret_state = gst_element_set_state(handle->pipeline, gst_state);
1037 if (ret_state == GST_STATE_CHANGE_FAILURE) {
1038 if (gst_state == GST_STATE_NULL)
1039 LOGE("ERROR - SET GST_STATE_NULL");
1040 else if (gst_state == GST_STATE_READY)
1041 LOGE("ERROR - SET GST_STATE_READY");
1042 else if (gst_state == GST_STATE_PAUSED)
1043 LOGE("ERROR - SET GST_STATE_PAUSED");
1044 else if (gst_state == GST_STATE_PLAYING)
1045 LOGE("ERROR - SET GST_STATE_PLAYING");
1047 return MM_ERROR_TRANSCODE_INTERNAL;
1049 if (gst_state == GST_STATE_NULL)
1050 LOGD("Success - SET GST_STATE_NULL");
1051 else if (gst_state == GST_STATE_READY)
1052 LOGD("Success - SET GST_STATE_READY");
1053 else if (gst_state == GST_STATE_PAUSED)
1054 LOGD("Success - SET GST_STATE_PAUSED");
1055 else if (gst_state == GST_STATE_PLAYING)
1056 LOGD("Success - SET GST_STATE_PLAYING");
1059 ret_state = gst_element_get_state(handle->pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
1060 if (ret_state == GST_STATE_CHANGE_FAILURE) {
1061 if (gst_state == GST_STATE_NULL)
1062 LOGE("ERROR - GET GST_STATE_NULL");
1063 else if (gst_state == GST_STATE_READY)
1064 LOGE("ERROR - GET GST_STATE_READY");
1065 else if (gst_state == GST_STATE_PAUSED)
1066 LOGE("ERROR - GET GST_STATE_PAUSED");
1067 else if (gst_state == GST_STATE_PLAYING)
1068 LOGE("ERROR - GET GST_STATE_PLAYING");
1070 return MM_ERROR_TRANSCODE_INTERNAL;
1072 if (gst_state == GST_STATE_NULL)
1073 LOGD("Success - GET GST_STATE_NULL");
1074 else if (gst_state == GST_STATE_READY)
1075 LOGD("Success - GET GST_STATE_READY");
1076 else if (gst_state == GST_STATE_PAUSED)
1077 LOGD("Success - GET GST_STATE_PAUSED");
1078 else if (gst_state == GST_STATE_PLAYING)
1079 LOGD("Success - GET GST_STATE_PLAYING");
1085 int _mm_transcode_param_flush(handle_s *handle)
1087 int ret = MM_ERROR_NONE;
1090 LOGE("[ERROR] - handle");
1091 return MM_ERROR_INVALID_ARGUMENT;
1094 if (!handle->encodebin) {
1095 LOGE("[ERROR] - handle encodebin");
1096 return MM_ERROR_TRANSCODE_INTERNAL;
1099 if (!handle->property) {
1100 LOGE("[ERROR] - handle property");
1101 return MM_ERROR_TRANSCODE_INTERNAL;
1104 handle->property->linked_vidoutbin = FALSE;
1105 handle->property->linked_audoutbin = FALSE;
1106 handle->encodebin->encodebin_profile = 0;
1107 handle->property->AUDFLAG = 0;
1108 handle->property->VIDFLAG = 0;
1109 handle->encodebin->audio_event_probe_id = 0;
1110 handle->encodebin->video_event_probe_id = 0;
1112 handle->property->total_length = 0;
1113 handle->property->repeat_thread_exit = FALSE;
1114 handle->property->is_busy = FALSE;
1115 handle->property->audio_cb_probe_id = 0;
1116 handle->property->video_cb_probe_id = 0;
1117 handle->property->progress_event_id = 0;
1118 handle->property->seek_idx = 0;
1123 static int _mm_transcode_play(handle_s *handle)
1125 int ret = MM_ERROR_NONE;
1128 LOGE("[ERROR] - handle");
1129 return MM_ERROR_INVALID_ARGUMENT;
1132 if (!handle->property) {
1133 LOGE("[ERROR] - handle property");
1134 return MM_ERROR_TRANSCODE_INTERNAL;
1137 ret = _mm_transcode_state_change(handle, GST_STATE_PLAYING);
1138 if (ret != MM_ERROR_NONE) {
1139 LOGE("ERROR -Playing Pipeline");
1143 LOGD("[SEEK: %d] width: %d height: %d start_pos: %lu duration: %lu (%lu) seek_mode: %d outputfile: %s",
1144 handle->param->seeking, handle->param->resolution_width, handle->param->resolution_height,
1145 handle->param->start_pos, handle->param->duration, handle->property->end_pos,
1146 handle->param->seek_mode, handle->param->outputfile);
1148 if (!handle->property->progress_event_id) {
1149 handle->property->progress_event_id = g_timeout_add(LAZY_PAUSE_TIMEOUT_MSEC, (GSourceFunc) _mm_cb_print_position, handle);
1150 LOGD("Timer (id=[%d], timeout=[%d ms])\n", handle->property->progress_event_id, LAZY_PAUSE_TIMEOUT_MSEC);
1156 static int _mm_transcode_seek(handle_s *handle)
1158 int ret = MM_ERROR_NONE;
1161 LOGE("[ERROR] - handle");
1162 return MM_ERROR_INVALID_ARGUMENT;
1165 if (!handle->property) {
1166 LOGE("[ERROR] - handle property");
1167 return MM_ERROR_TRANSCODE_INTERNAL;
1170 GList *walk_element = handle->property->sink_elements;
1171 gint64 start_pos, end_pos;
1173 GstSeekFlags _Flags = GST_SEEK_FLAG_NONE;
1175 start_pos = handle->param->start_pos * G_GINT64_CONSTANT(1000000);
1176 handle->property->end_pos = handle->param->start_pos + handle->param->duration;
1178 if (handle->param->start_pos > handle->property->total_length && handle->property->seek_idx) {
1179 LOGE("[%lu ~ %lu] out of %lu",
1180 handle->param->start_pos, handle->property->end_pos, handle->property->total_length);
1181 return MM_ERROR_TRANSCODE_SEEK_FAILED;
1183 if (handle->param->duration != 0) {
1184 end_pos = start_pos + handle->param->duration * G_GINT64_CONSTANT(1000000);
1185 } else if (handle->param->duration == 0) {
1186 /* seek to origin file length */
1187 end_pos = handle->property->total_length * G_GINT64_CONSTANT(1000000);
1190 LOGD("seek time : [ (%lu msec) : (%lu msec) ]\n",
1191 handle->param->start_pos, handle->property->end_pos);
1193 while (walk_element) {
1194 GstElement *seekable_element = GST_ELEMENT(walk_element->data);
1196 if (!seekable_element) {
1197 LOGE("ERROR - seekable");
1198 return MM_ERROR_TRANSCODE_INTERNAL;
1201 if (handle->param->seek_mode == MM_SEEK_ACCURATE)
1202 _Flags = GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH;
1203 else if (handle->param->seek_mode == MM_SEEK_INACCURATE)
1204 _Flags = GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_FLUSH;
1206 if (!gst_element_seek(seekable_element, rate, GST_FORMAT_TIME, _Flags, GST_SEEK_TYPE_SET, start_pos, GST_SEEK_TYPE_SET, end_pos)) {
1207 LOGE("ERROR - gst_element_seek (on) event : %s", GST_OBJECT_NAME(GST_OBJECT_CAST(seekable_element)));
1208 return MM_ERROR_TRANSCODE_SEEK_FAILED;
1212 walk_element = g_list_next(walk_element);
1219 int _mm_transcode_thread(handle_s *handle)
1221 int ret = MM_ERROR_NONE;
1224 LOGE("[ERROR] - handle");
1225 return MM_ERROR_INVALID_ARGUMENT;
1228 if (!handle->property) {
1229 LOGE("[ERROR] - handle property");
1230 return MM_ERROR_TRANSCODE_INTERNAL;
1233 if (!handle->property->thread_mutex) {
1234 handle->property->thread_mutex = g_new(GMutex, 1);
1235 g_mutex_init(handle->property->thread_mutex);
1236 LOGD("create thread_mutex: %p", handle->property->thread_mutex);
1238 LOGE("ERROR - thread_mutex is already created");
1241 if (!handle->property->thread_exit_mutex) {
1242 handle->property->thread_exit_mutex = g_new(GMutex, 1);
1243 g_mutex_init(handle->property->thread_exit_mutex);
1244 LOGD("create exit mutex: %p", handle->property->thread_exit_mutex);
1246 LOGE("ERROR - thread_exit_mutex is already created");
1249 /* These are a communicator for thread */
1250 if (!handle->property->queue) {
1251 handle->property->queue = g_async_queue_new();
1252 LOGD("create async queue: %p", handle->property->queue);
1254 LOGE("ERROR - async queue is already created");
1257 if (!handle->property->thread_cond) {
1258 handle->property->thread_cond = g_new(GCond, 1);
1259 g_cond_init(handle->property->thread_cond);
1260 LOGD("create thread cond: %p", handle->property->thread_cond);
1262 LOGE("thread cond is already created");
1265 /* create threads */
1266 LOGD("create thread");
1267 handle->property->thread = g_thread_new(NULL, (GThreadFunc)_mm_transcode_thread_repeate, (gpointer)handle);
1268 if (!handle->property->thread) {
1269 LOGE("ERROR - create thread");
1270 return MM_ERROR_TRANSCODE_INTERNAL;
1272 LOGD("create thread: %p", handle->property->thread);
1278 static gpointer _mm_transcode_thread_repeate(gpointer data)
1280 handle_s *handle = (handle_s *) data;
1281 int ret = MM_ERROR_NONE;
1284 LOGE("[ERROR] - handle");
1288 if (!handle->property) {
1289 LOGE("[ERROR] - handle property");
1295 int length = g_async_queue_length(handle->property->queue);
1297 LOGD("[QUEUE #] %d", length);
1298 handle->property->is_busy = TRUE;
1301 handle_param_s *pop_data = (handle_param_s *) g_async_queue_pop(handle->property->queue);
1303 if (handle->property->repeat_thread_exit || !handle->property->is_busy) {
1304 LOGD("[Receive Last Queue]");
1308 LOGD("[pop queue] resolution_width: %d, resolution_height: %d, start_pos: %lu, duration: %lu, seek_mode: %d outputfile: %s\n",
1309 pop_data->resolution_width, pop_data->resolution_height, pop_data->start_pos,
1310 pop_data->duration, pop_data->seek_mode, pop_data->outputfile);
1313 ret = _mm_transcode_exec(handle, pop_data);
1314 if (ret == MM_ERROR_NONE) {
1315 LOGD("Success - transcode_exec");
1317 LOGD("Destroy - transcode_exec");
1318 LOGD("<=== get exit (%d) signal", handle->property->repeat_thread_exit);
1323 LOGD("exit thread");