/* ******************************************************************************** * eyesee framework module * * (c) Copyright 2010-2018, Allwinner Microelectronic Co., Ltd. * All Rights Reserved * * File : EyeseeMotion.cpp * Version: V1.0 * By : zing huang * Date : 2018-06-28 * Description: ******************************************************************************** */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace EyeseeLinux { EyeseeMotion::EyeseeMotion(): mMotionTrailRet(-1) { mMotionHandle = NULL; memset(&mImageYUV, 0, sizeof mImageYUV); memset(&mImageBGR, 0, sizeof mImageBGR); ClearConfig(); } EyeseeMotion::~EyeseeMotion() { reset(); } status_t EyeseeMotion::setVideoFileSource(const char * url) { status_t opStatus; if(0 == access(url, F_OK)) { int fd = open(url, O_RDONLY); if(fd < 0) { aloge("Failed to open file %s(%s)", url, strerror(errno)); return UNKNOWN_ERROR; } opStatus = setVideoFileSource(fd, 0, 0x7ffffffffffffffL); close(fd); } else { aloge("fatal error! open file path[%s] fail!", url); opStatus = INVALID_OPERATION; } return opStatus; } status_t EyeseeMotion::setVideoFileSource(int fd, int64_t offset, int64_t length) { Mutex::Autolock _l(mLock); if(!(mCurrentState & MEDIA_MOTION_IDLE || mCurrentState == MEDIA_MOTION_STATE_ERROR)) { aloge("called in wrong state %d", mCurrentState); return INVALID_OPERATION; } ERRORTYPE ret; bool nSuccessFlag = false; //mDmxChnAttr.mStreamType = STREAMTYPE_LOCALFILE; mDmxChnAttr.mSourceType = SOURCETYPE_FD; mDmxChnAttr.mSourceUrl = NULL; mDmxChnAttr.mFd = dup(fd); mDmxChnAttr.mDemuxDisableTrack |= DEMUX_DISABLE_AUDIO_TRACK; mDmxChnAttr.mDemuxDisableTrack |= DEMUX_DISABLE_SUBTITLE_TRACK; mDmxChn = 0; while(mDmxChn < DEMUX_MAX_CHN_NUM) { ret = AW_MPI_DEMUX_CreateChn(mDmxChn, &mDmxChnAttr); if(SUCCESS == ret) { nSuccessFlag = TRUE; alogd("create demux channel[%d] success!", mDmxChn); break; } else if(ERR_DEMUX_EXIST == ret) { alogd("demux channel[%d] is exist, find next!", mDmxChn); mDmxChn++; } else if(ERR_DEMUX_FILE_EXCEPTION == ret) { aloge("demux detect media file exception!"); return BAD_FILE; } else { alogd("create demux channel[%d] ret[0x%x]!", mDmxChn, ret); break; } } if(false == nSuccessFlag) { mDmxChn = MM_INVALID_CHN; aloge("fatal error! create demux channel fail!"); return UNKNOWN_ERROR; } ret = AW_MPI_DEMUX_GetMediaInfo(mDmxChn, &mMediaInfo); if(SUCCESS == ret) { if( (mMediaInfo.mVideoNum > 0 && mMediaInfo.mVideoIndex >= mMediaInfo.mVideoNum) || (mMediaInfo.mAudioNum > 0 && mMediaInfo.mAudioIndex >= mMediaInfo.mAudioNum) || (mMediaInfo.mSubtitleNum > 0 && mMediaInfo.mSubtitleIndex >= mMediaInfo.mSubtitleNum) ) { alogd("fatal error, trackIndex wrong! [%d][%d],[%d][%d],[%d][%d]", mMediaInfo.mVideoNum, mMediaInfo.mVideoIndex, mMediaInfo.mAudioNum, mMediaInfo.mAudioIndex, mMediaInfo.mSubtitleNum, mMediaInfo.mSubtitleIndex); return UNKNOWN_ERROR; } } else { return UNKNOWN_ERROR; } mCurrentState = MEDIA_MOTION_INITIALIZED; return NO_ERROR; } std::shared_ptr EyeseeMotion::getMotionJpeg(const MotionPictureParam &jpegParam) { Mutex::Autolock _l(mLock); if(mCurrentState != MEDIA_MOTION_INITIALIZED) { aloge("called in error state 0x%x", mCurrentState); return NULL; } mPictureWidth = jpegParam.mPicWidth; mPictureHeight = jpegParam.mPicHeight; mStartTimeMs = jpegParam.mStartTimeUs / 1000; mSourceFrameNums = jpegParam.mSourceFrameNums; mSourceFrameStep = jpegParam.mSourceFrameStep; mObjectNums = jpegParam.mObjectNums; if(!(mPictureWidth > 0 && mPictureHeight > 0)) { aloge("fatal error! picturesize[%d, %d]", mPictureWidth, mPictureHeight); return NULL; } if(mStartTimeMs > static_cast(mMediaInfo.mDuration) || mStartTimeMs < 0) { aloge("fatal error, starttime[%d] ms!", mStartTimeMs); return NULL; } if(mSourceFrameNums < 1 || mSourceFrameStep < 0) { aloge("fatal error, SourceFrameNums[%d], SourceFrameStep[%d]", mSourceFrameNums, mSourceFrameStep); return NULL; } if(jpegParam.mTempBMPFileBasePath.empty()) { mTempBMPFileBasePath = "./tmp_picture/"; } else { mTempBMPFileBasePath = jpegParam.mTempBMPFileBasePath; if(jpegParam.mTempBMPFileBasePath[jpegParam.mTempBMPFileBasePath.size() - 1] != '/') { mTempBMPFileBasePath += "/"; } } prepare(); seekTo(); start(); getMotionTrail(); stop(); cvtBGR2YUV(&mImageBGR, &mImageYUV, PIXEL_FORMAT_YUV_PLANAR_420); return PictureAsJpeg(mImageYUV); } std::shared_ptr EyeseeMotion::getMotionBMP(const MotionPictureParam &jpegParam) { #if 0 Mutex::Autolock _l(mLock); if(mCurrentState != MEDIA_MOTION_INITIALIZED) { aloge("called in error state 0x%x", mCurrentState); return INVALID_OPERATION; } mPictureWidth = picWidth; mPictureHeight = picHeight; mStartTimeMs = startTimeUs / 1000; mSourceFrameNums = sourceFrameNums; mSourceFrameStep = sourceFrameStep; if(!(mPictureWidth > 0 && mPictureHeight > 0)) { aloge("fatal error! picturesize[%d, %d]", mPictureWidth, mPictureHeight); return NULL; } if(mStartTimeMs > mMediaInfo.mDuration || mStartTimeMs < 0) { aloge("fatal error, starttime[%d] ms!", mStartTimeMs); return NULL; } if(mSourceFrameNums < 1 || mSourceFrameStep < 0) { aloge("fatal error, SourceFrameNums[%d], SourceFrameStep[%d]", mSourceFrameNums, mSourceFrameStep); return NULL; } prepare(); seekTo(); start(); getMotionTrail(); stop(); return PictureAsBMP(mImageBGR); #else aloge("sorry, this version no realize func!"); return NULL; #endif } status_t EyeseeMotion::reset() { Mutex::Autolock _l(mLock); status_t result = NO_ERROR; if(mCurrentState == MEDIA_MOTION_INITIALIZED) { if(mDmxChn >= 0) { AW_MPI_DEMUX_DestroyChn(mDmxChn); mDmxChn = MM_INVALID_CHN; } } ClearConfig(); //mCurrentState = MEDIA_MOTION_IDLE; return result; } void EyeseeMotion::ClearConfig() { memset(&mDmxChnAttr, 0, sizeof mDmxChnAttr); memset(&mMediaInfo, 0, sizeof mMediaInfo); memset(&mVdecChnAttr, 0, sizeof mVdecChnAttr); memset(&mClockChnAttr, 0, sizeof mClockChnAttr); mDmxChn = MM_INVALID_CHN; mVdecChn = MM_INVALID_CHN; mClockChn = MM_INVALID_CHN; if(mMotionHandle) { AW_AI_MT_Reset(mMotionHandle); AW_AI_MT_UnInit(mMotionHandle); mMotionHandle = NULL; } DestroyImage(&mImageYUV); DestroyImage(&mImageBGR); mPictureWidth = 0; mPictureHeight = 0; mStartTimeMs = 0; mSourceFrameNums = 0; mSourceFrameStep = 0; mObjectNums = 0; mCurrentState = MEDIA_MOTION_IDLE; } status_t EyeseeMotion::prepare() { status_t result = NO_ERROR; ERRORTYPE ret; //basepath if(access(mTempBMPFileBasePath.c_str(), 0) != 0) { if(mkdir(mTempBMPFileBasePath.c_str(), 0) != 0) { aloge("fatal error, can not creat directory[%s], %s", mTempBMPFileBasePath.c_str(), strerror(errno)); return UNKNOWN_ERROR; } } if(mMediaInfo.mVideoNum > 0 && !(mDmxChnAttr.mDemuxDisableTrack & DEMUX_DISABLE_VIDEO_TRACK)) { DEMUX_VIDEO_STREAM_INFO_S *pStreamInfo = &mMediaInfo.mVideoStreamInfo[mMediaInfo.mVideoIndex]; mVdecChnAttr.mType = pStreamInfo->mCodecType; mVdecChnAttr.mPicWidth = mPictureWidth; mVdecChnAttr.mPicHeight = mPictureHeight; mVdecChnAttr.mInitRotation = (ROTATE_E)0; mVdecChnAttr.mOutputPixelFormat = MM_PIXEL_FORMAT_YVU_PLANAR_420; mVdecChnAttr.mVdecVideoAttr.mMode = VIDEO_MODE_FRAME; mVdecChnAttr.mVdecVideoAttr.mSupportBFrame = 1; bool bSuccessFlag = false; mVdecChn = 0; while(mVdecChn < VDEC_MAX_CHN_NUM) { ret = AW_MPI_VDEC_CreateChn(mVdecChn, &mVdecChnAttr); if(SUCCESS == ret) { bSuccessFlag = true; alogd("create vdec channel[%d] success!", mVdecChn); break; } else if(ERR_VDEC_EXIST == ret) { alogd("vdec channel[%d] is exist, find next!", mVdecChn); mVdecChn++; } else { alogd("error, create vdec channel[%d] ret[0x%x]!", mVdecChn, ret); break; } } if(!bSuccessFlag) { mVdecChn = MM_INVALID_CHN; aloge("fatal error! create vdec channel failed"); result = UNKNOWN_ERROR; goto _err0; } MPP_CHN_S DmxChn{MOD_ID_DEMUX, 0, mDmxChn}; MPP_CHN_S VdecChn{MOD_ID_VDEC, 0, mVdecChn}; AW_MPI_SYS_Bind(&DmxChn, &VdecChn); mClockChnAttr.nWaitMask |= 1<= 0) { bool bSuccessFlag = false; mClockChn = 0; while(mClockChn < CLOCK_MAX_CHN_NUM) { ret = AW_MPI_CLOCK_CreateChn(mClockChn, &mClockChnAttr); if(SUCCESS == ret) { bSuccessFlag = true; alogd("creat clock channel[%d] success!", mClockChn); break; } else if(ERR_CLOCK_EXIST == ret) { alogd("clock channel[%d] is exist, find next!", mClockChn); ++mClockChn; } else { alogd("creat clock channel[%d] ret[0x%x]", mClockChn, ret); break; } } if(!bSuccessFlag) { mClockChn = MM_INVALID_CHN; aloge("fatal error! create clock channel fail!"); result = UNKNOWN_ERROR; goto _err0; } MPP_CHN_S ClockChn{MOD_ID_CLOCK, 0, mClockChn}; MPP_CHN_S DmxChn{MOD_ID_DEMUX, 0, mDmxChn}; AW_MPI_SYS_Bind(&ClockChn, &DmxChn); } mLpMtParam.Width = mPictureWidth; mLpMtParam.Height = mPictureHeight; mLpMtParam.bAntishakeEnable = true; mMotionHandle = AW_AI_MT_Init(&mLpMtParam); if(!mMotionHandle) { aloge("init mt lib fail"); result = UNKNOWN_ERROR; goto _err0; } CreateImage(&mImageYUV, mLpMtParam.Width, mLpMtParam.Height, 3, PIXEL_FORMAT_YVU_PLANAR_420); CreateImage(&mImageBGR, mLpMtParam.Width, mLpMtParam.Height, 3, PIXEL_FORMAT_BGR_888); mCurrentState = MEDIA_MOTION_PREPARED; _err0: return result; } status_t EyeseeMotion::start() { status_t opStatus = NO_ERROR; if(mVdecChn >= 0) { //VDEC_DECODE_FRAME_PARAM_S DecodeParam; //DecodeParam.mDecodeFrameNum = (mSourceFrameNums -1) * mSourceFrameStep + mSourceFrameNums; //AW_MPI_VDEC_StartRecvStreamEx(mVdecChn, &DecodeParam); AW_MPI_VDEC_StartRecvStream(mVdecChn); } if(mDmxChn >= 0) { AW_MPI_DEMUX_Start(mDmxChn); } if(mClockChn >= 0) { AW_MPI_CLOCK_Start(mClockChn); } mCurrentState = MEDIA_MOTION_STARTED; return opStatus; } status_t EyeseeMotion::stop() { status_t opStatus = NO_ERROR; if(mClockChn >= 0) { AW_MPI_CLOCK_Stop(mClockChn); } if(mDmxChn >= 0) { AW_MPI_DEMUX_Stop(mDmxChn); } if(mVdecChn >= 0) { AW_MPI_VDEC_StopRecvStream(mVdecChn); } if(mVdecChn >= 0) { AW_MPI_VDEC_DestroyChn(mVdecChn); mVdecChn = MM_INVALID_CHN; } if(mDmxChn >= 0) { AW_MPI_DEMUX_DestroyChn(mDmxChn); mDmxChn = MM_INVALID_CHN; } if(mClockChn >= 0) { AW_MPI_CLOCK_DestroyChn(mClockChn); mClockChn = MM_INVALID_CHN; } mCurrentState = MEDIA_MOTION_STOPPED; return opStatus; } status_t EyeseeMotion::seekTo() { status_t opStatus = NO_ERROR; if(mDmxChn >= 0 && mStartTimeMs > 0) { AW_MPI_DEMUX_Seek(mDmxChn, mStartTimeMs); alogd("the demux had set %d ms!", mStartTimeMs); } return opStatus; } status_t EyeseeMotion::getMotionTrail() { mMotionTrailRet = -1; VIDEO_FRAME_INFO_S tVideoFrameInfo; const int nFramSum = (mSourceFrameNums -1) * mSourceFrameStep + mSourceFrameNums; int nSendFrameCounts = 0; if(nFramSum <= 20){ aloge("The voide is too short,video frame number nFramSum %d <= 20,",nFramSum); mMotionTrailRet = -1; return NO_ERROR; } for(int i = 0; i < nFramSum; i++) { ERRORTYPE ret = AW_MPI_VDEC_GetImage(mVdecChn, &tVideoFrameInfo, 500); if(SUCCESS == ret) { if(i > nFramSum / 10 && i < (nFramSum - nFramSum / 10) ) { if(0 == mSourceFrameStep || 0 == i % mSourceFrameStep) { assert(MM_PIXEL_FORMAT_YVU_PLANAR_420 == tVideoFrameInfo.VFrame.mPixelFormat); const int size = mLpMtParam.Width * mLpMtParam.Height; memcpy(mImageYUV.mpData, tVideoFrameInfo.VFrame.mpVirAddr[0], size); memcpy(mImageYUV.mpData + size, tVideoFrameInfo.VFrame.mpVirAddr[1], size / 4); memcpy(mImageYUV.mpData + size + size / 4, tVideoFrameInfo.VFrame.mpVirAddr[2], size / 4); cvtYUV2BGR(&mImageYUV, &mImageBGR); //AW_AI_MT_SendFrame(mMotionHandle, &mImageBGR); char tmpFileName[32] = {0}; sprintf(tmpFileName, "%d.bmp", nSendFrameCounts++); std::string TempFilePathName = mTempBMPFileBasePath + tmpFileName; SaveBMP(&mImageBGR, const_cast(TempFilePathName.c_str())); AW_AI_MT_SetFrame(mMotionHandle, TempFilePathName.c_str()); } } AW_MPI_VDEC_ReleaseImage(mVdecChn, &tVideoFrameInfo); } else { aloge("why not get frame from vdec, maybe EOF [0x%x]", ret); } } alogd("__send frame to MT, count[%d]", nSendFrameCounts); mMotionTrailRet = AW_AI_MT_GenMotionTrail(mMotionHandle, &mImageBGR); return NO_ERROR; } int EyeseeMotion::GetmMotionTrailRet() { return mMotionTrailRet; } std::shared_ptr EyeseeMotion::PictureAsJpeg(IMAGE_DATA_I &ImageYUV) { VENC_CHN VecChn = 0; VENC_CHN_ATTR_S VecChnAttr; memset(&VecChnAttr, 0, sizeof VecChnAttr); VecChnAttr.VeAttr.Type = PT_JPEG; VecChnAttr.VeAttr.AttrJpeg.MaxPicWidth = 0; VecChnAttr.VeAttr.AttrJpeg.MaxPicHeight = 0; VecChnAttr.VeAttr.AttrJpeg.BufSize = ((((ImageYUV.mWidth * ImageYUV.mHeight * 3) >> 2) + 1023) >> 10) << 10; VecChnAttr.VeAttr.AttrJpeg.bByFrame = TRUE; VecChnAttr.VeAttr.AttrJpeg.PicWidth = ImageYUV.mWidth; VecChnAttr.VeAttr.AttrJpeg.PicHeight = ImageYUV.mHeight; VecChnAttr.VeAttr.AttrJpeg.bSupportDCF = FALSE; VecChnAttr.VeAttr.MaxKeyInterval = 1; VecChnAttr.VeAttr.SrcPicWidth = ImageYUV.mWidth; VecChnAttr.VeAttr.SrcPicHeight = ImageYUV.mHeight; VecChnAttr.VeAttr.Field = VIDEO_FIELD_FRAME; VecChnAttr.VeAttr.PixelFormat = MM_PIXEL_FORMAT_YVU_SEMIPLANAR_420; ERRORTYPE ret; bool bSuccessFlag = false; while(VecChn < VENC_MAX_CHN_NUM) { ret = AW_MPI_VENC_CreateChn(VecChn, &VecChnAttr); if(SUCCESS == ret) { bSuccessFlag = true; alogd("create venc channel[%d] success!", VecChn); break; } else if(ERR_VENC_EXIST == ret) { alogd("venc channel[%d] is exist, find next!", VecChn); VecChn++; } else { alogd("create venc channel[%d] ret[0x%x], find next!", VecChn, ret); break; } } if(!bSuccessFlag) { aloge("fatal error! create venc channel fail!"); return NULL; } VIDEO_FRAME_INFO_S tVideoFrameInfo; memset(&tVideoFrameInfo, 0, sizeof tVideoFrameInfo); tVideoFrameInfo.mId = 0; tVideoFrameInfo.VFrame.mWidth = ImageYUV.mWidth; tVideoFrameInfo.VFrame.mHeight = ImageYUV.mHeight; tVideoFrameInfo.VFrame.mPixelFormat = MM_PIXEL_FORMAT_YVU_SEMIPLANAR_420; tVideoFrameInfo.VFrame.mOffsetLeft = 0; tVideoFrameInfo.VFrame.mOffsetRight = ImageYUV.mWidth; tVideoFrameInfo.VFrame.mOffsetTop = 0; tVideoFrameInfo.VFrame.mOffsetBottom = ImageYUV.mHeight; const int size = ImageYUV.mWidth * ImageYUV.mHeight; for(int i =0 ; i < 2; i++) { tVideoFrameInfo.VFrame.mpVirAddr[i] = ion_allocMem(size); tVideoFrameInfo.VFrame.mPhyAddr[i] = ion_getMemPhyAddr(tVideoFrameInfo.VFrame.mpVirAddr[i]); } memcpy(tVideoFrameInfo.VFrame.mpVirAddr[0], ImageYUV.mpData, size); ion_flushCache(tVideoFrameInfo.VFrame.mpVirAddr[0], size); unsigned char *ptr_uv = static_cast(tVideoFrameInfo.VFrame.mpVirAddr[1]); unsigned char *ptr_u = ImageYUV.mpData + size; unsigned char *ptr_v = ImageYUV.mpData + size + size / 4; for(int i = 0; i < size / 4; i++) { *ptr_uv++ = *ptr_v++; *ptr_uv++ = *ptr_u++; } ion_flushCache(tVideoFrameInfo.VFrame.mpVirAddr[1], size); VENC_RECV_PIC_PARAM_S RecvParam; RecvParam.mRecvPicNum = 1; AW_MPI_VENC_StartRecvPicEx(VecChn, &RecvParam); AW_MPI_VENC_SendFrame(VecChn, &tVideoFrameInfo, 500); std::shared_ptr spJpegBuf; VENC_STREAM_S JpegStream; VENC_PACK_S Jpeg_pack; JpegStream.mPackCount = 1; JpegStream.mpPack = &Jpeg_pack; if(SUCCESS == AW_MPI_VENC_GetStream(VecChn, &JpegStream, 500)) { const int JpegSize = JpegStream.mpPack[0].mLen0 + JpegStream.mpPack[0].mLen1; spJpegBuf = std::make_shared(JpegSize); char *ptr = (char *)spJpegBuf->getPointer(); memcpy(ptr, JpegStream.mpPack[0].mpAddr0, JpegStream.mpPack[0].mLen0); ptr += JpegStream.mpPack[0].mLen0; memcpy(ptr, JpegStream.mpPack[0].mpAddr1, JpegStream.mpPack[0].mLen1); if(AW_MPI_VENC_ReleaseStream(VecChn, &JpegStream) != SUCCESS) { aloge("jpeg stream data return fail!"); } } else { aloge("get jpeg stream data fail!"); } AW_MPI_VENC_StopRecvPic(VecChn); for(int i = 0; i < 2; i++) { ion_freeMem(tVideoFrameInfo.VFrame.mpVirAddr[i]); } AW_MPI_VENC_DestroyChn(VecChn); return spJpegBuf; } std::shared_ptr EyeseeMotion::PictureAsBMP(IMAGE_DATA_I &ImageBGR) { aloge("sorry, this version no realize func!"); return NULL; } };