1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "VideoEditorPropertiesMain"
18
19#include <dlfcn.h>
20#include <stdio.h>
21#include <unistd.h>
22#include <utils/Log.h>
23#include <utils/threads.h>
24#include <VideoEditorClasses.h>
25#include <VideoEditorJava.h>
26#include <VideoEditorOsal.h>
27#include <VideoEditorLogging.h>
28#include <VideoEditorOsal.h>
29#include <marker.h>
30
31extern "C" {
32#include <M4OSA_Clock.h>
33#include <M4OSA_CharStar.h>
34#include <M4OSA_Error.h>
35#include <M4OSA_FileCommon.h>
36#include <M4OSA_FileReader.h>
37#include <M4OSA_FileWriter.h>
38#include <M4OSA_Memory.h>
39#include <M4OSA_Thread.h>
40#include <M4VSS3GPP_API.h>
41#include <M4VSS3GPP_ErrorCodes.h>
42#include <M4MCS_API.h>
43#include <M4MCS_ErrorCodes.h>
44#include <M4READER_Common.h>
45#include <M4WRITER_common.h>
46#include <M4DECODER_Common.h>
47#include <M4AD_Common.h>
48};
49
50extern "C" M4OSA_ERR M4MCS_open_normalMode(
51                M4MCS_Context                       pContext,
52                M4OSA_Void*                         pFileIn,
53                M4VIDEOEDITING_FileType             InputFileType,
54                M4OSA_Void*                         pFileOut,
55                M4OSA_Void*                         pTempFile);
56
57jobject videoEditProp_getProperties(
58                JNIEnv*                             pEnv,
59                jobject                             thiz,
60                jstring                             file);
61
62static void
63getFileAndMediaTypeFromExtension (
64                M4OSA_Char* pExtension,
65                VideoEditClasses_FileType   *pFileType,
66                M4VIDEOEDITING_FileType       *pClipType);
67
68static M4OSA_ERR
69getClipProperties(  JNIEnv*                         pEnv,
70                    jobject                         thiz,
71                    M4OSA_Char*                     pFile,
72                    M4VIDEOEDITING_FileType         clipType,
73                    M4VIDEOEDITING_ClipProperties*  pClipProperties);
74
75M4OSA_UInt32
76VideoEdit_chrCompare(M4OSA_Char* pStrIn1,
77                     M4OSA_Char* pStrIn2,
78                     M4OSA_Int32* pCmpResult);
79
80jobject videoEditProp_getProperties(
81        JNIEnv* pEnv,
82        jobject thiz,
83        jstring file)
84{
85    bool                           gotten          = true;
86    M4OSA_Char*                    pFile           = M4OSA_NULL;
87    M4OSA_Char*                    pExtension      = M4OSA_NULL;
88    M4OSA_UInt32                   index           = 0;
89    M4OSA_Int32                    cmpResult       = 0;
90    VideoEditPropClass_Properties* pProperties     = M4OSA_NULL;
91    M4VIDEOEDITING_ClipProperties* pClipProperties = M4OSA_NULL;
92    M4OSA_ERR                      result          = M4NO_ERROR;
93    M4MCS_Context                  context         = M4OSA_NULL;
94    M4OSA_FilePosition             size            = 0;
95    M4OSA_UInt32                   width           = 0;
96    M4OSA_UInt32                   height          = 0;
97    jobject                        properties      = NULL;
98    M4OSA_Context                  pOMXContext     = M4OSA_NULL;
99    M4DECODER_VideoInterface*      pOMXVidDecoderInterface = M4OSA_NULL;
100    M4AD_Interface*                pOMXAudDecoderInterface = M4OSA_NULL;
101
102    bool  initialized = true;
103    VideoEditClasses_FileType fileType = VideoEditClasses_kFileType_Unsupported;
104    M4VIDEOEDITING_FileType clipType = M4VIDEOEDITING_kFileType_Unsupported;
105
106    VIDEOEDIT_LOG_API(
107            ANDROID_LOG_INFO, "VIDEO_EDITOR_PROPERTIES",
108            "videoEditProp_getProperties()");
109
110    // Add a text marker (the condition must always be true).
111    ADD_TEXT_MARKER_FUN(NULL != pEnv)
112
113    // Initialize the classes.
114    videoEditPropClass_init(&initialized, (JNIEnv*)pEnv);
115
116    // Validate the tempPath parameter.
117    videoEditJava_checkAndThrowIllegalArgumentException(
118            &gotten, pEnv, (NULL == file), "file is null");
119
120    // Get the file path.
121    pFile = (M4OSA_Char *)videoEditJava_getString(
122            &gotten, pEnv, file, NULL, M4OSA_NULL);
123
124    result = M4OSA_fileReadOpen(&context, (M4OSA_Void*)pFile, M4OSA_kFileRead);
125
126    if(M4NO_ERROR != result) {
127        // Free the file path.
128        videoEditOsal_free(pFile);
129        pFile = M4OSA_NULL;
130    }
131
132    videoEditJava_checkAndThrowIllegalArgumentException(&gotten, pEnv,
133        (M4NO_ERROR != result), "file not found");
134
135    // Close the file and free the file context
136    if (context != NULL) {
137        result = M4OSA_fileReadClose(context);
138        context = M4OSA_NULL;
139    }
140
141    // Return if Error
142    if (M4NO_ERROR != result) {
143        return (properties); // NULL
144    }
145
146    // Check if the file path is valid.
147    if (gotten)
148    {
149        // Retrieve the extension.
150        pExtension = (M4OSA_Char *)strrchr((const char *)pFile, (int)'.');
151        if (M4OSA_NULL != pExtension)
152        {
153            // Skip the dot.
154            pExtension++;
155
156            // Get the file type and Media type from extension
157            getFileAndMediaTypeFromExtension(
158                    pExtension ,&fileType, &clipType);
159        }
160    }
161
162    // Check if the file type could be determined.
163    videoEditJava_checkAndThrowIllegalArgumentException(
164            &gotten, pEnv,
165            (VideoEditClasses_kFileType_Unsupported == fileType),
166            "file type is not supported");
167
168    // Allocate a new properties structure.
169    pProperties = (VideoEditPropClass_Properties*)videoEditOsal_alloc(
170            &gotten, pEnv,
171            sizeof(VideoEditPropClass_Properties), "Properties");
172
173    // Check if the context is valid and allocation succeeded
174    // (required because of dereferencing of pProperties).
175    if (gotten)
176    {
177        // Check if this type of file needs to be analyzed using MCS.
178        if ((VideoEditClasses_kFileType_MP3  == fileType) ||
179            (VideoEditClasses_kFileType_MP4  == fileType) ||
180            (VideoEditClasses_kFileType_3GPP == fileType) ||
181            (VideoEditClasses_kFileType_AMR  == fileType) ||
182            (VideoEditClasses_kFileType_PCM  == fileType) ||
183            (VideoEditClasses_kFileType_M4V  == fileType))
184        {
185            // Allocate a new clip properties structure.
186            pClipProperties =
187                (M4VIDEOEDITING_ClipProperties*)videoEditOsal_alloc(
188                    &gotten, pEnv,
189                    sizeof(M4VIDEOEDITING_ClipProperties), "ClipProperties");
190
191            // Check if allocation succeeded (required because of
192            // dereferencing of pClipProperties).
193            if (gotten)
194            {
195                // Add a code marker (the condition must always be true).
196                ADD_CODE_MARKER_FUN(NULL != pClipProperties)
197
198                // Log the API call.
199                VIDEOEDIT_LOG_API(
200                        ANDROID_LOG_INFO, "VIDEO_EDITOR_PROPERTIES",
201                        "getClipProperties");
202
203                // Get Video clip properties
204                result = getClipProperties(
205                        pEnv, thiz, pFile, clipType, pClipProperties);
206
207                if (M4MCS_ERR_FILE_DRM_PROTECTED == result) {
208                    // Check if the creation succeeded.
209                    videoEditJava_checkAndThrowIllegalArgumentException(
210                            &gotten, pEnv,(M4NO_ERROR != result),
211                            "Invalid File - DRM Protected ");
212                } else {
213                    // Check if the creation succeeded.
214                    videoEditJava_checkAndThrowIllegalArgumentException(
215                            &gotten, pEnv,(M4NO_ERROR != result),
216                            "Invalid File or File not found ");
217                }
218
219#ifdef USE_SOFTWARE_DECODER
220                /**
221                 * Input clip with non-multiples of 16 is not supported.
222                 */
223                if ( (pClipProperties->uiVideoWidth %16)
224                    || (pClipProperties->uiVideoHeight %16) )
225                {
226                    result = M4MCS_ERR_INPUT_VIDEO_SIZE_NON_X16;
227                    videoEditJava_checkAndThrowIllegalArgumentException(
228                            &gotten, pEnv, (M4NO_ERROR != result),
229                            "non x16 input video frame size is not supported");
230                }
231#endif /* USE_SOFTWARE_DECODER */
232            }
233
234            // Check if the properties could be retrieved.
235            if (gotten)
236            {
237                // Set the properties.
238                pProperties->uiClipDuration = pClipProperties->uiClipDuration;
239                if (M4VIDEOEDITING_kFileType_Unsupported == pClipProperties->FileType)
240                {
241                    pProperties->FileType        = VideoEditClasses_kFileType_Unsupported;
242                }
243                else
244                {
245                    pProperties->FileType        = fileType;
246                }
247                pProperties->VideoStreamType     = pClipProperties->VideoStreamType;
248                pProperties->uiClipVideoDuration = pClipProperties->uiClipVideoDuration;
249                pProperties->uiVideoBitrate      = pClipProperties->uiVideoBitrate;
250                pProperties->uiVideoWidth        = pClipProperties->uiVideoWidth;
251                pProperties->uiVideoHeight       = pClipProperties->uiVideoHeight;
252                pProperties->fAverageFrameRate   = pClipProperties->fAverageFrameRate;
253                pProperties->uiVideoProfile      = pClipProperties->uiVideoProfile;
254                pProperties->uiVideoLevel        = pClipProperties->uiVideoLevel;
255                // Set profile and level support to TRUE, pending check
256                pProperties->bProfileSupported   = M4OSA_TRUE;
257                pProperties->bLevelSupported     = M4OSA_TRUE;
258                pProperties->AudioStreamType     = pClipProperties->AudioStreamType;
259                pProperties->uiClipAudioDuration = pClipProperties->uiClipAudioDuration;
260                pProperties->uiAudioBitrate      = pClipProperties->uiAudioBitrate;
261                pProperties->uiNbChannels        = pClipProperties->uiNbChannels;
262                pProperties->uiSamplingFrequency = pClipProperties->uiSamplingFrequency;
263                pProperties->uiRotation          = pClipProperties->videoRotationDegrees;
264
265            }
266
267            // Free the clip properties.
268            videoEditOsal_free(pClipProperties);
269            pClipProperties = M4OSA_NULL;
270        }
271        else if ((VideoEditClasses_kFileType_JPG == fileType) ||
272            (VideoEditClasses_kFileType_GIF == fileType) ||
273            (VideoEditClasses_kFileType_PNG == fileType))
274        {
275            pProperties->uiClipDuration      = 0;
276            pProperties->FileType            = fileType;
277            pProperties->VideoStreamType     = M4VIDEOEDITING_kNoneVideo;
278            pProperties->uiClipVideoDuration = 0;
279            pProperties->uiVideoBitrate      = 0;
280            pProperties->uiVideoWidth        = width;
281            pProperties->uiVideoHeight       = height;
282            pProperties->fAverageFrameRate   = 0.0f;
283            pProperties->uiVideoProfile = M4VIDEOEDITING_VIDEO_UNKNOWN_PROFILE;
284            pProperties->uiVideoLevel = M4VIDEOEDITING_VIDEO_UNKNOWN_LEVEL;
285            pProperties->AudioStreamType     = M4VIDEOEDITING_kNoneAudio;
286            pProperties->uiClipAudioDuration = 0;
287            pProperties->uiAudioBitrate      = 0;
288            pProperties->uiNbChannels        = 0;
289            pProperties->uiSamplingFrequency = 0;
290
291            // Added for Handling invalid paths and non existent image files
292            // Open the file for reading.
293            result = M4OSA_fileReadOpen(&context, (M4OSA_Void*)pFile, M4OSA_kFileRead);
294            if (M4NO_ERROR != result)
295            {
296                pProperties->FileType = VideoEditClasses_kFileType_Unsupported;
297            }
298            result = M4OSA_fileReadClose(context);
299            context = M4OSA_NULL;
300        }
301    }
302
303    if (M4NO_ERROR == result) {
304        // Create a properties object.
305        videoEditPropClass_createProperties(&gotten, pEnv, pProperties, &properties);
306
307        // Log the properties.
308        VIDEOEDIT_PROP_LOG_PROPERTIES(pProperties);
309    }
310
311    // Free the properties.
312    videoEditOsal_free(pProperties);
313    pProperties = M4OSA_NULL;
314
315    // Free the file path.
316    videoEditOsal_free(pFile);
317    pFile = M4OSA_NULL;
318
319    // Add a text marker (the condition must always be true).
320    ADD_TEXT_MARKER_FUN(NULL != pEnv)
321
322    // Return the Properties object.
323    return(properties);
324}
325
326static void getFileAndMediaTypeFromExtension (
327        M4OSA_Char *pExtension,
328        VideoEditClasses_FileType *pFileType,
329        M4VIDEOEDITING_FileType *pClipType)
330{
331    M4OSA_Char extension[5] = {0, 0, 0, 0, 0};
332    VideoEditClasses_FileType fileType =
333            VideoEditClasses_kFileType_Unsupported;
334
335    M4VIDEOEDITING_FileType clipType =
336            M4VIDEOEDITING_kFileType_Unsupported;
337
338    M4OSA_UInt32 index = 0;
339    M4OSA_ERR result = M4NO_ERROR;
340    M4OSA_Int32 cmpResult = 0;
341    M4OSA_UInt32  extLength = strlen((const char *)pExtension);
342
343    // Assign default
344    *pFileType = VideoEditClasses_kFileType_Unsupported;
345    *pClipType = M4VIDEOEDITING_kFileType_Unsupported;
346
347    // Check if the length of the extension is valid.
348    if ((3 == extLength) || (4 == extLength))
349    {
350        // Convert the extension to lowercase.
351        for (index = 0; index < extLength ; index++)
352        {
353            extension[index] = tolower((int)pExtension[index]);
354        }
355
356        // Check if the extension is ".mp3".
357        if (!(VideoEdit_chrCompare(extension, (M4OSA_Char*)"mp3", &cmpResult)))
358        {
359            *pFileType = VideoEditClasses_kFileType_MP3;
360            *pClipType = M4VIDEOEDITING_kFileType_MP3;
361        }
362        // Check if the extension is ".mp4".
363        else if (!(VideoEdit_chrCompare(extension, (M4OSA_Char*)"mp4", &cmpResult)))
364        {
365            *pFileType = VideoEditClasses_kFileType_MP4;
366            *pClipType = M4VIDEOEDITING_kFileType_MP4;
367        }
368        // Check if the extension is ".3gp".
369        else if (!(VideoEdit_chrCompare(extension, (M4OSA_Char*)"3gp", &cmpResult)))
370        {
371            *pFileType = VideoEditClasses_kFileType_3GPP;
372            *pClipType = M4VIDEOEDITING_kFileType_3GPP;
373        }
374        // Check if the extension is ".m4a".
375        else if (!(VideoEdit_chrCompare(extension, (M4OSA_Char*)"m4a", &cmpResult)))
376        {
377            *pFileType = VideoEditClasses_kFileType_3GPP;
378            *pClipType = M4VIDEOEDITING_kFileType_3GPP;
379        }
380        // Check if the extension is ".3gpp".
381        else if (!(VideoEdit_chrCompare(extension, (M4OSA_Char*)"3gpp", &cmpResult)))
382        {
383            *pFileType = VideoEditClasses_kFileType_3GPP;
384            *pClipType = M4VIDEOEDITING_kFileType_3GPP;
385        }
386        // Check if the extension is ".amr".
387        else if (!(VideoEdit_chrCompare(extension, (M4OSA_Char*)"amr", &cmpResult)))
388        {
389            *pFileType = VideoEditClasses_kFileType_AMR;
390            *pClipType = M4VIDEOEDITING_kFileType_AMR;
391        }
392        // Check if the extension is ".pcm".
393        else if (!(VideoEdit_chrCompare(extension, (M4OSA_Char*)"pcm", &cmpResult)))
394        {
395            *pFileType = VideoEditClasses_kFileType_PCM;
396            *pClipType = M4VIDEOEDITING_kFileType_PCM;
397        }
398        // Check if the extension is ".jpg".
399        else if (!(VideoEdit_chrCompare(extension, (M4OSA_Char*)"jpg", &cmpResult)))
400        {
401            *pFileType = VideoEditClasses_kFileType_JPG;
402        }
403        // Check if the extension is ".jpeg".
404        else if (!(VideoEdit_chrCompare(extension, (M4OSA_Char*)"jpeg", &cmpResult)))
405        {
406            *pFileType = VideoEditClasses_kFileType_JPG;
407        }
408        // Check if the extension is ".gif".
409        else if (!(VideoEdit_chrCompare(extension, (M4OSA_Char*)"gif", &cmpResult)))
410        {
411            *pFileType = VideoEditClasses_kFileType_GIF;
412        }
413        // Check if the extension is ".png".
414        else if (!(VideoEdit_chrCompare(extension, (M4OSA_Char*)"png", &cmpResult)))
415        {
416            *pFileType = VideoEditClasses_kFileType_PNG;
417        }
418        // Check if the extension is ".m4v".
419        else if (!(VideoEdit_chrCompare(extension, (M4OSA_Char*)"m4v", &cmpResult)))
420        {
421            *pFileType = VideoEditClasses_kFileType_M4V;
422            *pClipType = M4VIDEOEDITING_kFileType_M4V;
423        }
424    }
425}
426
427static M4OSA_ERR getClipProperties(
428        JNIEnv* pEnv,
429        jobject thiz,
430        M4OSA_Char* pFile,
431        M4VIDEOEDITING_FileType clipType,
432        M4VIDEOEDITING_ClipProperties* pClipProperties)
433{
434    bool                      gotten          = true;
435    M4OSA_ERR                 result          = M4NO_ERROR;
436    M4OSA_ERR                 resultAbort     = M4NO_ERROR;
437    M4MCS_Context             context         = M4OSA_NULL;
438
439    M4OSA_FileReadPointer fileReadPtr =
440            { M4OSA_NULL, M4OSA_NULL, M4OSA_NULL,
441              M4OSA_NULL, M4OSA_NULL, M4OSA_NULL };
442
443    M4OSA_FileWriterPointer fileWritePtr =
444            { M4OSA_NULL, M4OSA_NULL, M4OSA_NULL,
445              M4OSA_NULL, M4OSA_NULL, M4OSA_NULL, M4OSA_NULL };
446
447    // Initialize the OSAL file system function pointers.
448    videoEditOsal_getFilePointers(&fileReadPtr , &fileWritePtr);
449
450    // Log the API call.
451    VIDEOEDIT_LOG_API(
452            ANDROID_LOG_INFO, "VIDEO_EDITOR_PROPERTIES",\
453            "getClipProperties - M4MCS_init()");
454
455    // Initialize the MCS context.
456    result = M4MCS_init(&context, &fileReadPtr, &fileWritePtr);
457
458    // Log the result.
459    VIDEOEDIT_PROP_LOG_RESULT(
460            ANDROID_LOG_INFO, "VIDEO_EDITOR_PROPERTIES", "%s",
461            videoEditOsal_getResultString(result));
462
463    // Check if the creation succeeded.
464    videoEditJava_checkAndThrowRuntimeException(
465            &gotten, pEnv, (M4NO_ERROR != result), result);
466
467    // Check if opening the MCS context succeeded.
468    if (gotten)
469    {
470        // Log the API call.
471        VIDEOEDIT_LOG_API(
472                ANDROID_LOG_INFO, "VIDEO_EDITOR_PROPERTIES",
473                "getClipProperties - M4MCS_open_normalMode()");
474
475        // Open the MCS in the normal opening mode to
476        // retrieve the exact duration
477        result = M4MCS_open_normalMode(
478                context, pFile, clipType, M4OSA_NULL, M4OSA_NULL);
479
480        // Log the result.
481        VIDEOEDIT_PROP_LOG_RESULT(
482                ANDROID_LOG_INFO, "VIDEO_EDITOR_PROPERTIES", "%s",
483                videoEditOsal_getResultString(result));
484
485        // Check if the creation succeeded.
486        videoEditJava_checkAndThrowRuntimeException(
487                &gotten, pEnv, (M4NO_ERROR != result), result);
488
489        // Check if the MCS could be opened.
490        if (gotten)
491        {
492            // Log the API call.
493            VIDEOEDIT_LOG_API(
494                    ANDROID_LOG_INFO, "VIDEO_EDITOR_PROPERTIES",
495                    "getClipProperties - M4MCS_getInputFileProperties()");
496
497            // Get the properties.
498            result = M4MCS_getInputFileProperties(context, pClipProperties);
499
500            // Log the result.
501            VIDEOEDIT_PROP_LOG_RESULT(
502                    ANDROID_LOG_INFO, "VIDEO_EDITOR_PROPERTIES", "%s",
503                    videoEditOsal_getResultString(result));
504
505            // Check if the creation succeeded.
506            videoEditJava_checkAndThrowRuntimeException(
507                    &gotten, pEnv, (M4NO_ERROR != result), result);
508        }
509
510        // Log the API call.
511        VIDEOEDIT_LOG_API(
512                ANDROID_LOG_INFO, "VIDEO_EDITOR_PROPERTIES",
513                "getClipProperties - M4MCS_abort()");
514
515        // Close the MCS session.
516        resultAbort = M4MCS_abort(context);
517
518       if (result == M4NO_ERROR) {
519            // Log the result.
520            VIDEOEDIT_PROP_LOG_RESULT(
521                    ANDROID_LOG_INFO, "VIDEO_EDITOR_PROPERTIES", "%s",
522                    videoEditOsal_getResultString(resultAbort));
523
524            // Check if the abort succeeded.
525            videoEditJava_checkAndThrowRuntimeException(
526                    &gotten, pEnv, (M4NO_ERROR != resultAbort), resultAbort);
527            result = resultAbort;
528        }
529    }
530
531    return result;
532}
533
534M4OSA_UInt32
535VideoEdit_chrCompare(M4OSA_Char* pStrIn1,
536                     M4OSA_Char* pStrIn2,
537                      M4OSA_Int32* pCmpResult)
538{
539    *pCmpResult = strcmp((const char *)pStrIn1, (const char *)pStrIn2);
540    return *pCmpResult;
541}
542
543
544