/*---------------------------------------------------------------------------- * Copyright (c) TJD Technologies Co., Ltd. 2024. All rights reserved. * * Description: * * Author: huangshuyi * * Create: 2024-8 *--------------------------------------------------------------------------*/ //brandy-native-tjd -nhso -release #include "TjdUiAppOnlineWfVideoPage.h" #include "wearable_log.h" #include "common/image_cache_manager.h" #include #include #include "gfx_utils/image_info.h" #include "gfx_utils/heap_base.h" #include "TjdUiImageIds.h" #include "power_display_service.h" #include "TjdUiMemManage.h" #include "sys_config.h" #include "rtc_api.h" #include "product_evb4_standard.h" #include "common/key_code.h" #include "common/image_cache_manager.h" #include "sql_setting.h" using namespace OHOS; using namespace OHOS::Media; using OHOS::Media::Player; namespace TJD { #define ENABLE_PRINT_INFO 1 #if ENABLE_PRINT_INFO #define static_print_info(...) sys_ui_log_i(__VA_ARGS__) //一般信息打印宏控制 #define static_print_warn(...) sys_ui_log_w(__VA_ARGS__) //警告信息打印一般常开 #define static_print_error(...) sys_ui_log_e(__VA_ARGS__) //错误信息打印一般常开 #define static_print_debug(...) sys_ui_log_d(__VA_ARGS__) #else #define static_print_info(...) #define static_print_warn(...) #define static_print_error(...) #define static_print_debug(...) #endif static constexpr int16_t VIDEO_WIDTH = HORIZONTAL_RESOLUTION; static constexpr int16_t VIDEO_HEIGHT = VERTICAL_RESOLUTION; static constexpr int16_t STRIDE_ALIGNMENT_VALUE = 128; static constexpr int16_t BUFFER_QUEUE_SIZE = 3; static constexpr int16_t JPEG_HEIGHT_DIVISOR = 2; static constexpr int16_t JPEG_WIDTH_BYTE_ALIGNMENT = 128; static constexpr int16_t JPEG_HEIGHT_BYTE_ALIGNMENT = 16; static const int32_t WAIT_VIDEO_EXIT_SUCCESS_MS = 10; static const int32_t REQUEST_BUFFER_REPET_COUNT = 10; static const uint32_t WAIT_VIDEO_EXIT_MAX_RETRY_COUNT = 100; static AudioManager &g_amInstance = AudioManager::GetInstance(); #define MEDIA_OK 0 #define MEDIA_INVALID_PARAM (-1) #define MEDIA_INIT_FAIL (-2) #define MEDIA_ERR (-3) #define MEDIA_PERMISSION_DENIED (-4) TjdUiAppOnlineWfVideoPage::TjdUiAppOnlineWfVideoPage(UIView* parent, std::string uri, std::string previewPath) : Parent_(parent),uri_(uri),previewPath_(previewPath) { InitView(); } TjdUiAppOnlineWfVideoPage::~TjdUiAppOnlineWfVideoPage() { if (videoPlay_ != nullptr) { delete videoPlay_; videoPlay_ = nullptr; static_print_debug("delete videoPlay\n"); } ImageCacheManager::GetInstance().UnloadSingleRes(previewPath_.c_str()); } UIView *TjdUiAppOnlineWfVideoPage::GetView(void) { return surfaceView_; } bool TjdUiAppOnlineWfVideoPage::InitView(void) { surfaceView_ = new UILiteSurfaceView(); if (surfaceView_ == nullptr) { GRAPHIC_LOGE("surfaceView is nullptr\n"); return false; } UIViewGroup *p = (UIViewGroup *)Parent_; p->Add(surfaceView_); ImageInfo* imgInfo; imgInfo = ImageCacheManager::GetInstance().LoadSingleRes(previewPath_.c_str()); surfaceView_->SetPreview(imgInfo); colorKey_.full = 0xff202020; surfaceView_->SetViewId("sur"); surfaceView_->SetPosition(0, 0, VIDEO_WIDTH, VIDEO_HEIGHT); surfaceView_->SetStyle(STYLE_BACKGROUND_COLOR, Color::Black().full); surfaceView_->SetSurfaceColorkey(colorKey_); Surface *surface = surfaceView_->GetSurface(); surface->SetStrideAlignment(STRIDE_ALIGNMENT_VALUE); surface->SetWidthAndHeight(VIDEO_WIDTH, VIDEO_HEIGHT); surface->SetQueueSize(BUFFER_QUEUE_SIZE); surface->SetFormat(PIXEL_FMT_YCBCR_420_SP); // jpeg decoder output buffer size calculation formula int32_t ySize = ALIGN_BYTE(VIDEO_WIDTH, JPEG_WIDTH_BYTE_ALIGNMENT) * VIDEO_HEIGHT; int32_t uvSize = ALIGN_BYTE(VIDEO_WIDTH, JPEG_WIDTH_BYTE_ALIGNMENT) * ALIGN_BYTE(VIDEO_HEIGHT, JPEG_HEIGHT_BYTE_ALIGNMENT) / JPEG_HEIGHT_DIVISOR; int32_t bufferSize = ySize + uvSize; surface->SetSize(bufferSize); if (videoPlay_ == nullptr) { videoPlay_ = new TjdUiVideoCommon(surface, uri_, false); videoPlay_->SetSyncExitMode(true); } //videoPlay_->StartVideoPlay(); return true; } static bool InitPlayerResources(TjdUiVideoCommon *videoPlay) { videoPlay->mutex_ = MediaMutexCreate(nullptr); if (videoPlay->mutex_ == nullptr) { static_print_error("create mutex failed\n"); return false; } videoPlay->cond_ = MediaThreadCondCreate(); if (videoPlay->cond_ == nullptr) { static_print_error("create thread cond failed\n"); MediaMutexDestroy(&videoPlay->mutex_); return false; } shared_ptr player = std::make_shared(); if (player == nullptr || player.get() == nullptr) { static_print_error("player is nullptr\n"); MediaMutexDestroy(&videoPlay->mutex_); MediaThreadCondDestroy(&videoPlay->cond_); return false; } videoPlay->player_ = player; videoPlay->isPlaybackCompleted_ = false; videoPlay->playError_ = false; videoPlay->interruptHintStop_ = false; return true; } static void DeinitPlayerResources(TjdUiVideoCommon *videoPlay) { (void)videoPlay->player_.reset(); videoPlay->isExited_ = true; videoPlay->isEntered_ = false; (void)MediaThreadCondDestroy(&videoPlay->cond_); (void)MediaMutexDestroy(&videoPlay->mutex_); } static int32_t VideoPlay(TjdUiVideoCommon *videoPlay) { std::shared_ptr callback = std::make_shared(videoPlay); if (callback == nullptr || callback.get() == nullptr) { static_print_error("AlbumVideoPlayer::%s: callback is nullptr", __func__); return MEDIA_ERR; } videoPlay->player_->SetPlayerCallback(callback); int32_t ret = videoPlay->player_->Play(); if (ret != 0) { static_print_error("play failed\n"); (void)videoPlay->player_->Reset(); (void)videoPlay->player_->Release(); return MEDIA_ERR; } videoPlay->player_->EnableSingleLooping(videoPlay->isLoop_); MediaMutexLock(videoPlay->mutex_); videoPlay->isPlayed_ = true; /*若有自定义组件在开始时需要改变,在这里修改*/ //videoPlay->button_->SetText("Pause"); while (!videoPlay->isPlaybackCompleted_ && !videoPlay->isPlaybackStopped_ && !videoPlay->interruptHintStop_ && !videoPlay->playError_) { MediaThreadCondWait(videoPlay->cond_, videoPlay->mutex_); } MediaMutexUnLock(videoPlay->mutex_); return 0; } static void VideoStopAndExit(TjdUiVideoCommon *videoPlay) { MediaMutexLock(videoPlay->mutex_); int32_t ret = videoPlay->player_->Stop(); if (ret != 0) { static_print_error("stop failed"); } ret = videoPlay->player_->Reset(); if (ret != 0) { static_print_error("reset failed"); } videoPlay->isPlaybackCompleted_ = false; videoPlay->isPlayed_ = false; videoPlay->isPause_ = false; /*若有自定义组件需要结束时改变,在这里在修改*/ //videoPlay->button_->SetText("Play"); MediaMutexUnLock(videoPlay->mutex_); } static int32_t ReleasePlayer(TjdUiVideoCommon *videoPlay) { (void)videoPlay->player_->Reset(); (void)videoPlay->player_->Release(); (void)DeinitPlayerResources(videoPlay); return MEDIA_ERR; } static void SendBackgroundFrame(TjdUiVideoCommon *videoPlay) { int32_t ret = videoPlay->player_->Release(); if (ret != 0) { static_print_error("release failed"); } if (!videoPlay->needSendBackgroundFrame_) { (void)DeinitPlayerResources(videoPlay); return; } SurfaceBuffer *buf = nullptr; int32_t requestCount = 0; while ((buf = videoPlay->surface_->RequestBuffer()) == nullptr) { if (requestCount++ > REQUEST_BUFFER_REPET_COUNT) { static_print_error("request the last buffer failed"); break; } LiteSurface *liteSurface = dynamic_cast(videoPlay->surface_); if (liteSurface == nullptr) { static_print_error("liteSurface is null!"); break; } videoPlay->surface_->CancelBuffer(liteSurface->GetBackBuf()); } if (buf != nullptr) { buf->format = PIXEL_FMT_BUTT; videoPlay->surface_->FlushBuffer(buf); } (void)DeinitPlayerResources(videoPlay); } static Source GetVideoPlaySource(TjdUiVideoCommon *videoPlay) { Source source; if (!videoPlay->uri_.empty()) { std::map header; source = Source(videoPlay->uri_, header); } else if (videoPlay->fd_ > 0) { if (videoPlay->isLoop_) { int32_t ret = lseek(videoPlay->fd_, 0, SEEK_SET); if (ret == -1) { static_print_error("lseek[fd = %d] file fail, error[%d]\n", videoPlay->fd_, errno); return source; } } source = Source(videoPlay->fd_, videoPlay->offset_, 0); } return source; } static int32_t SetAudioSessionIdAndStreamType(TjdUiVideoCommon *videoPlay) { int32_t ret = MEDIA_OK; if (!videoPlay->isPureVideo_) { ret = videoPlay->player_->SetAudioSessionId(videoPlay->sessionId_); if (ret != MEDIA_OK) { static_print_error("AlbumVideoPlayer::%s: set audio sessionId failed", __func__); return ret; } ret = videoPlay->player_->SetAudioStreamType(AUDIO_STREAM_FITNESS_VIDEO); if (ret != MEDIA_OK) { static_print_error("AlbumVideoPlayer::%s: set audio stream type failed", __func__); return ret; } } return ret; } static int32_t RunVideoPlay(TjdUiVideoCommon *videoPlay) { bool success = InitPlayerResources(videoPlay); if (!success) { static_print_error("init player resources failed\n"); return MEDIA_ERR; } int32_t ret = 0; char filePath[PATH_MAX]; if (realpath(videoPlay->uri_.c_str(), filePath) == nullptr) { static_print_error("no %s resource", videoPlay->uri_.c_str()); return MEDIA_ERR; } Source source = GetVideoPlaySource(videoPlay); ret = videoPlay->player_->SetSource(source); if (ret != 0) { static_print_error("set source failed\n"); (void)DeinitPlayerResources(videoPlay); return MEDIA_ERR; } ret = videoPlay->player_->Prepare(); if (ret != 0) { static_print_error("prepare failed\n"); return ReleasePlayer(videoPlay); } ret = SetAudioSessionIdAndStreamType(videoPlay); if (ret != 0) { return ReleasePlayer(videoPlay); } ret = videoPlay->player_->SetVideoSurface(videoPlay->surface_); if (ret != 0) { static_print_error("set video surface failed\n"); return ReleasePlayer(videoPlay); } ret = VideoPlay(videoPlay); if (ret != 0) { return ReleasePlayer(videoPlay); } VideoStopAndExit(videoPlay); SendBackgroundFrame(videoPlay); return 0; } static bool RequsetAudioFocus(TjdUiVideoCommon *videoPlay) { if (videoPlay->isPureVideo_) { return true; } bool success = g_amInstance.Initialize(); if (!success) { static_print_error("AlbumVideoPlayer::%s: audiomanager init failed", __func__); return false; } AudioSession sessionId = g_amInstance.MakeSessionId(); if (sessionId == AUDIO_SESSION_ID_NONE) { static_print_error("AlbumVideoPlayer::%s: audio session id invalid", __func__); return false; } std::shared_ptr interruptListener = std::make_shared(videoPlay); if (interruptListener == nullptr || interruptListener.get() == nullptr) { static_print_error("AlbumVideoPlayer::%s: video player interrupt listener is nullptr", __func__); return false; } AudioInterrupt interrupt = {AUDIO_STREAM_FITNESS_VIDEO, sessionId, interruptListener}; if (g_amInstance.ActivateAudioInterrupt(interrupt) == INTERRUPT_FAILED) { static_print_error("AlbumVideoPlayer::%s: activate audio interrupt failed", __func__); return false; } videoPlay->sessionId_ = sessionId; videoPlay->interrupt_ = interrupt; return true; } static bool ReleaseAudioFocus(TjdUiVideoCommon *videoPlay) { if (videoPlay->isPureVideo_) { return true; } if (g_amInstance.DeactivateAudioInterrupt(videoPlay->interrupt_) != 0) { static_print_error("AlbumVideoPlayer::%s: deactivate audio interrupt failed", __func__); return false; } return true; } int32_t TjdUiVideoCommon::StartVideoPlay(void) { if (isEntered_) { static_print_info("video play is not stop, can not start!"); return MEDIA_OK; } isExited_ = false; isEntered_ = true; MediaThreadattr attr = {"VideoPlayThread", 0x2000, THREAD_SCHED_INVALID, 0, true}; if (isSyncExitMode_) { attr.detached = false; } threadHandle_ = MediaThreadCreate(VideoPlayThread, this, &attr); if (threadHandle_ == NULL) { static_print_error("create thread failed\n"); return MEDIA_ERR; } //play = this; return MEDIA_OK; } int32_t TjdUiVideoCommon::PauseVideoPlay(void) { MediaMutexLock(mutex_); if (player_ == NULL) { static_print_error("can not pause play"); MediaMutexUnLock(mutex_); return MEDIA_ERR; } if (isPlaybackStopped_ || isPlaybackCompleted_ || interruptHintStop_) { static_print_error("current state is stop, can not pause play"); MediaMutexUnLock(mutex_); return MEDIA_ERR; } int32_t ret = player_->Pause(); if (ret != MEDIA_OK) { static_print_error("pause failed"); MediaMutexUnLock(mutex_); return MEDIA_ERR; } isPause_ = true; MediaMutexUnLock(mutex_); return ret; } int32_t TjdUiVideoCommon::ResumeVideoPlay(void) { MediaMutexLock(mutex_); if (player_ == NULL) { static_print_error("can not resume play"); MediaMutexUnLock(mutex_); return MEDIA_ERR; } if (isPlaybackStopped_ || isPlaybackCompleted_ || interruptHintStop_) { static_print_error("current state is stop, can not resume play"); MediaMutexUnLock(mutex_); return MEDIA_ERR; } int32_t ret = player_->Play(); if (ret != MEDIA_OK) { static_print_error("resume failed"); MediaMutexUnLock(mutex_); return ret; } isPause_ = false; MediaMutexUnLock(mutex_); return ret; } int32_t TjdUiVideoCommon::StopVideoPlay(void) { MediaMutexLock(mutex_); if (isPlaybackStopped_ || isPlaybackCompleted_ || interruptHintStop_) { if (isLoop_) { if (!isPlaybackCompleted_) { static_print_info("video play already stopped"); MediaMutexUnLock(mutex_); return MEDIA_OK; } } else { static_print_info("video play already stopped"); MediaMutexUnLock(mutex_); return MEDIA_OK; } } isPlaybackStopped_ = true; MediaThreadCondSignal(cond_); MediaMutexUnLock(mutex_); if (isSyncExitMode_) { MediaThreadJoin(&threadHandle_); isPlaybackStopped_ = false; dynamic_cast(surface_) ->ClearBuffers(); // clear buffers safely in sync mode to reduce peak memory } //play = nullptr; return MEDIA_OK; } void TjdUiVideoCommon::DestroyVideoPlaySource(void) { needSendBackgroundFrame_ = false; StopVideoPlay(); uint32_t count = 0; while (!IsExitCompletely()) { count++; if (count == WAIT_VIDEO_EXIT_MAX_RETRY_COUNT) { static_print_error("media video play exit timeout\n"); break; } usleep(WAIT_VIDEO_EXIT_SUCCESS_MS); } } void *TjdUiVideoCommon::VideoPlayThread(void *arg) { TjdUiVideoCommon *videoPlay = (TjdUiVideoCommon *)arg; if (videoPlay->isPlayed_) { static_print_error("the video is playing.\n"); return nullptr; } bool success = RequsetAudioFocus(videoPlay); if (!success) { static_print_error("AlbumVideoPlayer::%s: requset audio focus failed", __func__); return nullptr; } int32_t ret = RunVideoPlay(videoPlay); if (ret != 0) { static_print_error("run video play failed\n"); (void)ReleaseAudioFocus(videoPlay); return nullptr; } success = ReleaseAudioFocus(videoPlay); if (!success) { static_print_error("AlbumVideoPlayer::%s: release audio focus failed", __func__); return nullptr; } static_print_error("exit all!\n"); return nullptr; } int32_t TjdUiVideoCommon::GetDumpInfo(PlayerDebugInfo *playerInfo) { MediaMutexLock(mutex_); if (player_ == NULL) { static_print_error("can not get dump info!"); MediaMutexUnLock(mutex_); return MEDIA_ERR; } int32_t ret = player_->DumpInfo(playerInfo); if (ret != MEDIA_OK) { static_print_error("get dump info failed"); MediaMutexUnLock(mutex_); return ret; } MediaMutexUnLock(mutex_); return ret; } void TjdUiVideoCommon::SetVideoPlayLoop(bool isLoop) { MediaMutexLock(mutex_); isLoop_ = isLoop; MediaMutexUnLock(mutex_); } void TjdUiVideoCommon::SetSyncExitMode(bool isSyncExitMode) { MediaMutexLock(mutex_); if (!isEntered_) { isSyncExitMode_ = isSyncExitMode; } MediaMutexUnLock(mutex_); } bool TjdUiVideoCommon::IsExitCompletely(void) { return isExited_; } }