/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "VideoEditorVideoDecoder.h" #include "VideoEditor3gpReader.h" #include #include "VideoBrowserInternal.h" #include "LVOSA_FileReader_optim.h" //#define M4OSA_TRACE_LEVEL 1 #if (M4OSA_TRACE_LEVEL >= 1) #undef M4OSA_TRACE1_0 #undef M4OSA_TRACE1_1 #undef M4OSA_TRACE1_2 #undef M4OSA_TRACE1_3 #define M4OSA_TRACE1_0(a) __android_log_print(ANDROID_LOG_INFO, "Thumbnail", a); #define M4OSA_TRACE1_1(a,b) __android_log_print(ANDROID_LOG_INFO, "Thumbnail", a,b); #define M4OSA_TRACE1_2(a,b,c) __android_log_print(ANDROID_LOG_INFO, "Thumbnail", a,b,c); #define M4OSA_TRACE1_3(a,b,c,d) __android_log_print(ANDROID_LOG_INFO, "Thumbnail", a,b,c,d); #endif /****************************************************************************** * M4OSA_ERR videoBrowserSetWindow( * M4OSA_Context pContext, M4OSA_UInt32 x, * M4OSA_UInt32 y, M4OSA_UInt32 dx, M4OSA_UInt32 dy); * @brief This function sets the size and the position of the display. * @param pContext (IN) : Video Browser context * @param pPixelArray (IN) : Array to hold the video frame. * @param x (IN) : Horizontal position of the top left * corner * @param y (IN) : Vertical position of the top left corner * @param dx (IN) : Width of the display window * @param dy (IN) : Height of the video window * @return M4NO_ERROR / M4ERR_PARAMETER / M4ERR_STATE / M4ERR_ALLOC ******************************************************************************/ M4OSA_ERR videoBrowserSetWindow( M4OSA_Context pContext, M4OSA_Int32 *pPixelArray, M4OSA_UInt32 x, M4OSA_UInt32 y, M4OSA_UInt32 dx, M4OSA_UInt32 dy) { VideoBrowserContext* pC = (VideoBrowserContext*)pContext; M4OSA_ERR err = M4NO_ERROR; M4OSA_TRACE2_5("videoBrowserSetWindow: entering with 0x%x %d %d %d %d ", pContext, x, y, dx, dy); /*--- Sanity checks ---*/ CHECK_PTR(videoBrowserSetWindow, pContext, err, M4ERR_PARAMETER); CHECK_PTR(videoBrowserSetWindow, pPixelArray, err, M4ERR_PARAMETER); CHECK_STATE(videoBrowserSetWindow, VideoBrowser_kVBOpened, pC); pC->m_outputPlane[0].u_topleft = 0; pC->m_outputPlane[0].u_height = dy; pC->m_outputPlane[0].u_width = dx; pC->m_x = x; pC->m_y = y; if (pC->m_frameColorType == VideoBrowser_kGB565) { pC->m_outputPlane[0].u_stride = pC->m_outputPlane[0].u_width << 1; pC->m_outputPlane[0].pac_data = (M4OSA_UInt8*)M4OSA_malloc( pC->m_outputPlane[0].u_stride * pC->m_outputPlane[0].u_height, VIDEOBROWSER, (M4OSA_Char *)"output plane"); CHECK_PTR(videoBrowserSetWindow, pC->m_outputPlane[0].pac_data, err, M4ERR_ALLOC); } else if (pC->m_frameColorType == VideoBrowser_kYUV420) { pC->m_outputPlane[0].u_stride = pC->m_outputPlane[0].u_width; pC->m_outputPlane[1].u_height = pC->m_outputPlane[0].u_height >> 1; pC->m_outputPlane[1].u_width = pC->m_outputPlane[0].u_width >> 1; pC->m_outputPlane[1].u_topleft = 0; pC->m_outputPlane[1].u_stride = pC->m_outputPlane[1].u_width; pC->m_outputPlane[2].u_height = pC->m_outputPlane[0].u_height >> 1; pC->m_outputPlane[2].u_width = pC->m_outputPlane[0].u_width >> 1; pC->m_outputPlane[2].u_topleft = 0; pC->m_outputPlane[2].u_stride = pC->m_outputPlane[2].u_width; pC->m_outputPlane[0].pac_data = (M4OSA_UInt8*)pPixelArray; CHECK_PTR(videoBrowserSetWindow, pC->m_outputPlane[0].pac_data, err, M4ERR_ALLOC); pC->m_outputPlane[1].pac_data = pC->m_outputPlane[0].pac_data + (pC->m_outputPlane[0].u_stride * pC->m_outputPlane[0].u_height); pC->m_outputPlane[2].pac_data = pC->m_outputPlane[1].pac_data + (pC->m_outputPlane[1].u_stride * pC->m_outputPlane[1].u_height); } M4OSA_TRACE2_0("videoBrowserSetWindow returned NO ERROR"); return M4NO_ERROR; videoBrowserSetWindow_cleanUp: M4OSA_TRACE2_1("videoBrowserSetWindow returned 0x%x", err); return err; } /****************************************************************************** * @brief This function allocates the resources needed for browsing a video file * @param ppContext (OUT): Pointer on a context filled by this function. * @param pURL (IN) : Path of File to browse * @param DrawMode (IN) : Indicate which method is used to draw (Direct draw etc...) * @param pfCallback (IN) : Callback function to be called when a frame must be displayed * @param pCallbackData (IN) : User defined data that will be passed as parameter of the callback * @param clrType (IN) : Required color type. * @return M4NO_ERROR / M4ERR_PARAMETER / M4ERR_STATE / M4ERR_ALLOC ******************************************************************************/ M4OSA_ERR videoBrowserCreate( M4OSA_Context* ppContext, M4OSA_Char* pURL, M4OSA_UInt32 DrawMode, M4OSA_FileReadPointer* ptrF, videoBrowser_Callback pfCallback, M4OSA_Void* pCallbackData, VideoBrowser_VideoColorType clrType) { VideoBrowserContext* pContext = M4OSA_NULL; M4READER_MediaFamily mediaFamily = M4READER_kMediaFamilyUnknown; M4_StreamHandler* pStreamHandler = M4OSA_NULL; M4_VideoStreamHandler* pVideoStreamHandler = M4OSA_NULL; M4DECODER_VideoType decoderType; M4DECODER_OutputFilter FilterOption; M4OSA_Bool deb = M4OSA_TRUE; M4OSA_ERR err = M4NO_ERROR; M4OSA_TRACE1_2( "videoBrowserCreate: entering with 0x%x 0x%x", ppContext, pURL); /*--- Sanity checks ---*/ CHECK_PTR(videoBrowserCreate, ppContext, err, M4ERR_PARAMETER); *ppContext = M4OSA_NULL ; CHECK_PTR(videoBrowserCreate, pURL, err, M4ERR_PARAMETER); /*--- Create context ---*/ pContext = (VideoBrowserContext*)M4OSA_malloc( sizeof(VideoBrowserContext), VIDEOBROWSER, (M4OSA_Char*)"Video browser context"); CHECK_PTR(videoBrowserCreate, pContext,err, M4ERR_ALLOC); M4OSA_memset((M4OSA_MemAddr8)pContext, sizeof(VideoBrowserContext), 0); /*--- Initialize the context parameters ---*/ pContext->m_state = VideoBrowser_kVBCreating ; pContext->m_frameColorType = clrType; /*--- Copy the file reader functions ---*/ M4OSA_memcpy((M4OSA_MemAddr8)&pContext->m_fileReadPtr, (M4OSA_MemAddr8)ptrF, sizeof(M4OSA_FileReadPointer)) ; /* PR#SP00013 DGR bug 13 : first frame is not visible */ pContext->m_drawmode = DrawMode; /* Retrieve the 3gp reader interface */ VideoEditor3gpReader_getInterface(&pContext->m_mediaType, &pContext->m_3gpReader, &pContext->m_3gpData); CHECK_PTR(videoBrowserCreate, pContext->m_3gpReader, err, M4ERR_ALLOC); CHECK_PTR(videoBrowserCreate, pContext->m_3gpData, err, M4ERR_ALLOC); /*--- Create the file reader ---*/ err = pContext->m_3gpReader->m_pFctCreate(&pContext->m_pReaderCtx); CHECK_ERR(videoBrowserCreate, err); CHECK_PTR(videoBrowserCreate, pContext->m_pReaderCtx, err, M4ERR_ALLOC); pContext->m_3gpData->m_readerContext = pContext->m_pReaderCtx; /*--- Set the OSAL file reader functions ---*/ err = pContext->m_3gpReader->m_pFctSetOption( pContext->m_pReaderCtx, M4READER_kOptionID_SetOsaFileReaderFctsPtr, (M4OSA_DataOption)(&pContext->m_fileReadPtr)); CHECK_ERR(videoBrowserCreate, err) ; /*--- Open the file ---*/ err = pContext->m_3gpReader->m_pFctOpen(pContext->m_pReaderCtx, pURL); CHECK_ERR(videoBrowserCreate, err) ; /*--- Try to find a video stream ---*/ while (err == M4NO_ERROR) { err = pContext->m_3gpReader->m_pFctGetNextStream( pContext->m_pReaderCtx, &mediaFamily, &pStreamHandler); /*in case we found a bifs stream or something else...*/ if ((err == (M4OSA_UInt32)M4ERR_READER_UNKNOWN_STREAM_TYPE) || (err == (M4OSA_UInt32)M4WAR_TOO_MUCH_STREAMS)) { err = M4NO_ERROR; continue; } if (err != M4WAR_NO_MORE_STREAM) { if (M4READER_kMediaFamilyVideo != mediaFamily) { err = M4NO_ERROR; continue; } pContext->m_pStreamHandler = pStreamHandler; err = pContext->m_3gpReader->m_pFctReset( pContext->m_pReaderCtx, pContext->m_pStreamHandler); CHECK_ERR(videoBrowserCreate, err); err = pContext->m_3gpReader->m_pFctFillAuStruct( pContext->m_pReaderCtx, pContext->m_pStreamHandler, &pContext->m_accessUnit); CHECK_ERR(videoBrowserCreate, err); pVideoStreamHandler = (M4_VideoStreamHandler*)pContext->m_pStreamHandler; switch (pContext->m_pStreamHandler->m_streamType) { case M4DA_StreamTypeVideoMpeg4: case M4DA_StreamTypeVideoH263: { pContext->m_pCodecLoaderContext = M4OSA_NULL; decoderType = M4DECODER_kVideoTypeMPEG4; #ifdef USE_SOFTWARE_DECODER err = VideoEditorVideoDecoder_getSoftwareInterface_MPEG4( &decoderType, &pContext->m_pDecoder); #else err = VideoEditorVideoDecoder_getInterface_MPEG4( &decoderType, (void **)&pContext->m_pDecoder); #endif CHECK_ERR(videoBrowserCreate, err) ; err = pContext->m_pDecoder->m_pFctCreate( &pContext->m_pDecoderCtx, pContext->m_pStreamHandler, pContext->m_3gpData, &pContext->m_accessUnit, pContext->m_pCodecLoaderContext) ; CHECK_ERR(videoBrowserCreate, err) ; } break; case M4DA_StreamTypeVideoMpeg4Avc: { pContext->m_pCodecLoaderContext = M4OSA_NULL; decoderType = M4DECODER_kVideoTypeAVC; #ifdef USE_SOFTWARE_DECODER err = VideoEditorVideoDecoder_getSoftwareInterface_H264( &decoderType, &pContext->m_pDecoder); #else err = VideoEditorVideoDecoder_getInterface_H264( &decoderType, (void **)&pContext->m_pDecoder); #endif CHECK_ERR(videoBrowserCreate, err) ; err = pContext->m_pDecoder->m_pFctCreate( &pContext->m_pDecoderCtx, pContext->m_pStreamHandler, pContext->m_3gpData, &pContext->m_accessUnit, pContext->m_pCodecLoaderContext) ; CHECK_ERR(videoBrowserCreate, err) ; } break; default: err = M4ERR_VB_MEDIATYPE_NOT_SUPPORTED; goto videoBrowserCreate_cleanUp; } } } if (err == M4WAR_NO_MORE_STREAM) { err = M4NO_ERROR ; } if (M4OSA_NULL == pContext->m_pStreamHandler) { err = M4ERR_VB_NO_VIDEO ; goto videoBrowserCreate_cleanUp ; } err = pContext->m_pDecoder->m_pFctSetOption( pContext->m_pDecoderCtx, M4DECODER_kOptionID_DeblockingFilter, (M4OSA_DataOption)&deb); if (err == M4WAR_DEBLOCKING_FILTER_NOT_IMPLEMENTED) { err = M4NO_ERROR; } CHECK_ERR(videoBrowserCreate, err); FilterOption.m_pFilterUserData = M4OSA_NULL; if (pContext->m_frameColorType == VideoBrowser_kGB565) { FilterOption.m_pFilterFunction = (M4OSA_Void*)M4VIFI_ResizeBilinearYUV420toBGR565; } else if (pContext->m_frameColorType == VideoBrowser_kYUV420) { FilterOption.m_pFilterFunction = (M4OSA_Void*)M4VIFI_ResizeBilinearYUV420toYUV420; } else { err = M4ERR_PARAMETER; goto videoBrowserCreate_cleanUp; } err = pContext->m_pDecoder->m_pFctSetOption( pContext->m_pDecoderCtx, M4DECODER_kOptionID_OutputFilter, (M4OSA_DataOption)&FilterOption); CHECK_ERR(videoBrowserCreate, err); /* store the callback details */ pContext->m_pfCallback = pfCallback; pContext->m_pCallbackUserData = pCallbackData; /* store the callback details */ pContext->m_state = VideoBrowser_kVBOpened; *ppContext = pContext; M4OSA_TRACE1_0("videoBrowserCreate returned NO ERROR"); return M4NO_ERROR; videoBrowserCreate_cleanUp: if (M4OSA_NULL != pContext) { if (M4OSA_NULL != pContext->m_pDecoderCtx) { pContext->m_pDecoder->m_pFctDestroy(pContext->m_pDecoderCtx); pContext->m_pDecoderCtx = M4OSA_NULL; } if (M4OSA_NULL != pContext->m_pReaderCtx) { pContext->m_3gpReader->m_pFctClose(pContext->m_pReaderCtx); pContext->m_3gpReader->m_pFctDestroy(pContext->m_pReaderCtx); pContext->m_pReaderCtx = M4OSA_NULL; } SAFE_FREE(pContext->m_pDecoder); SAFE_FREE(pContext->m_3gpReader); SAFE_FREE(pContext->m_3gpData); SAFE_FREE(pContext); } M4OSA_TRACE2_1("videoBrowserCreate returned 0x%x", err); return err; } /****************************************************************************** * M4OSA_ERR videoBrowserCleanUp(M4OSA_Context pContext); * @brief This function frees the resources needed for browsing a * video file. * @param pContext (IN) : Video browser context * @return M4NO_ERROR / M4ERR_PARAMETER / M4ERR_STATE ******************************************************************************/ M4OSA_ERR videoBrowserCleanUp(M4OSA_Context pContext) { VideoBrowserContext* pC = (VideoBrowserContext*)pContext; M4OSA_ERR err = M4NO_ERROR; M4OSA_TRACE2_1("videoBrowserCleanUp: entering with 0x%x", pContext); /*--- Sanity checks ---*/ CHECK_PTR(videoBrowserCleanUp, pContext, err, M4ERR_PARAMETER); if (M4OSA_NULL != pC->m_pDecoderCtx) { pC->m_pDecoder->m_pFctDestroy(pC->m_pDecoderCtx); pC->m_pDecoderCtx = M4OSA_NULL ; } if (M4OSA_NULL != pC->m_pReaderCtx) { pC->m_3gpReader->m_pFctClose(pC->m_pReaderCtx) ; pC->m_3gpReader->m_pFctDestroy(pC->m_pReaderCtx); pC->m_pReaderCtx = M4OSA_NULL; } SAFE_FREE(pC->m_pDecoder); SAFE_FREE(pC->m_3gpReader); SAFE_FREE(pC->m_3gpData); if (pC->m_frameColorType != VideoBrowser_kYUV420) { SAFE_FREE(pC->m_outputPlane[0].pac_data); } SAFE_FREE(pC); M4OSA_TRACE2_0("videoBrowserCleanUp returned NO ERROR"); return M4NO_ERROR; videoBrowserCleanUp_cleanUp: M4OSA_TRACE2_1("videoBrowserCleanUp returned 0x%x", err); return err; } /****************************************************************************** * M4OSA_ERR videoBrowserPrepareFrame( * M4OSA_Context pContext, M4OSA_UInt32* pTime); * @brief This function prepares the frame. * @param pContext (IN) : Video browser context * @param pTime (IN/OUT) : Pointer on the time to reach. Updated * by this function with the reached time * @return M4NO_ERROR / M4ERR_PARAMETER / M4ERR_STATE / M4ERR_ALLOC ******************************************************************************/ M4OSA_ERR videoBrowserPrepareFrame(M4OSA_Context pContext, M4OSA_UInt32* pTime) { VideoBrowserContext* pC = (VideoBrowserContext*)pContext; M4OSA_ERR err = M4NO_ERROR; M4OSA_UInt32 targetTime = 0; M4OSA_UInt32 jumpTime = 0; M4_MediaTime timeMS = 0; M4OSA_Int32 rapTime = 0; M4OSA_Bool isBackward = M4OSA_FALSE; M4OSA_Bool bJumpNeeded = M4OSA_FALSE; /*--- Sanity checks ---*/ CHECK_PTR(videoBrowserPrepareFrame, pContext, err, M4ERR_PARAMETER); CHECK_PTR(videoBrowserPrepareFrame, pTime, err, M4ERR_PARAMETER); targetTime = *pTime ; /*--- Check the state, if this is the first call to this function we move to the state "browsing" ---*/ if (VideoBrowser_kVBOpened == pC->m_state) { pC->m_state = VideoBrowser_kVBBrowsing; } else if (VideoBrowser_kVBBrowsing != pC->m_state) { err = M4ERR_STATE ; goto videoBrowserPrepareFrame_cleanUp; } /*--- Check the duration ---*/ /*--- If we jump backward, we need to jump ---*/ if (targetTime < pC->m_currentCTS) { isBackward = M4OSA_TRUE; bJumpNeeded = M4OSA_TRUE; } /*--- If we jumpt to a time greater than "currentTime" + "predecodeTime" we need to jump ---*/ else if (targetTime > (pC->m_currentCTS + VIDEO_BROWSER_PREDECODE_TIME)) { bJumpNeeded = M4OSA_TRUE; } if (M4OSA_TRUE == bJumpNeeded) { rapTime = targetTime; /*--- Retrieve the previous RAP time ---*/ err = pC->m_3gpReader->m_pFctGetPrevRapTime( pC->m_pReaderCtx, pC->m_pStreamHandler, &rapTime); CHECK_ERR(videoBrowserPrepareFrame, err); jumpTime = rapTime; err = pC->m_3gpReader->m_pFctJump(pC->m_pReaderCtx, pC->m_pStreamHandler, (M4OSA_Int32*)&jumpTime); CHECK_ERR(videoBrowserPrepareFrame, err); } timeMS = (M4_MediaTime)targetTime; err = pC->m_pDecoder->m_pFctDecode( pC->m_pDecoderCtx, &timeMS, bJumpNeeded); if ((err != M4NO_ERROR) && (err != M4WAR_NO_MORE_AU)) { return err; } // FIXME: // Not sure that I understand why we need a second jump logic here if ((timeMS >= pC->m_currentCTS) && (M4OSA_TRUE == isBackward)) { jumpTime = rapTime; err = pC->m_3gpReader->m_pFctJump( pC->m_pReaderCtx, pC->m_pStreamHandler, (M4OSA_Int32*)&jumpTime); CHECK_ERR(videoBrowserPrepareFrame, err); timeMS = (M4_MediaTime)rapTime; err = pC->m_pDecoder->m_pFctDecode( pC->m_pDecoderCtx, &timeMS, M4OSA_TRUE); if ((err != M4NO_ERROR) && (err != M4WAR_NO_MORE_AU)) { return err; } } err = pC->m_pDecoder->m_pFctRender( pC->m_pDecoderCtx, &timeMS, pC->m_outputPlane, M4OSA_TRUE); if (M4WAR_VIDEORENDERER_NO_NEW_FRAME == err) { return err; } CHECK_ERR(videoBrowserPrepareFrame, err) ; pC->m_currentCTS = (M4OSA_UInt32)timeMS; *pTime = pC->m_currentCTS; return M4NO_ERROR; videoBrowserPrepareFrame_cleanUp: if ((M4WAR_INVALID_TIME == err) || (M4WAR_NO_MORE_AU == err)) { err = M4NO_ERROR; } else if (M4OSA_NULL != pC) { pC->m_currentCTS = 0; } M4OSA_TRACE2_1("videoBrowserPrepareFrame returned 0x%x", err); return err; } /****************************************************************************** * M4OSA_ERR videoBrowserDisplayCurrentFrame(M4OSA_Context pContext); * @brief This function displays the current frame. * @param pContext (IN) : Video browser context * @return M4NO_ERROR / M4ERR_PARAMETER / M4ERR_STATE / M4ERR_ALLOC ******************************************************************************/ M4OSA_ERR videoBrowserDisplayCurrentFrame(M4OSA_Context pContext) { VideoBrowserContext* pC = (VideoBrowserContext*)pContext ; M4OSA_ERR err = M4NO_ERROR ; /*--- Sanity checks ---*/ CHECK_PTR(videoBrowserDisplayCurrentFrame, pContext, err, M4ERR_PARAMETER); // Request display of the frame pC->m_pfCallback((M4OSA_Context) pC, // VB context VIDEOBROWSER_DISPLAY_FRAME, // action requested M4NO_ERROR, // error code (M4OSA_Void*) &(pC->m_outputPlane[0]), // image to be displayed (M4OSA_Void*) pC->m_pCallbackUserData); // user-provided data #ifdef DUMPTOFILE { M4OSA_Context fileContext; M4OSA_Char* fileName = "/sdcard/textBuffer_RGB565.rgb"; M4OSA_fileWriteOpen(&fileContext, (M4OSA_Void*) fileName, M4OSA_kFileWrite | M4OSA_kFileCreate); M4OSA_fileWriteData(fileContext, (M4OSA_MemAddr8) pC->m_outputPlane[0].pac_data, pC->m_outputPlane[0].u_height*pC->m_outputPlane[0].u_width*2); M4OSA_fileWriteClose(fileContext); } #endif M4OSA_TRACE2_0("videoBrowserDisplayCurrentFrame returned NO ERROR") ; return M4NO_ERROR; videoBrowserDisplayCurrentFrame_cleanUp: M4OSA_TRACE2_1("videoBrowserDisplayCurrentFrame returned 0x%x", err) ; return err; }