* photos support play audio

This commit is contained in:
lxowalle
2024-11-25 18:26:57 +08:00
parent 4a4aa4431e
commit fefc7240b3
9 changed files with 846 additions and 234 deletions

View File

@@ -43,7 +43,7 @@ list(APPEND ADD_REQUIREMENTS zbar omv)
if(PLATFORM_LINUX)
list(APPEND ADD_REQUIREMENTS sdl)
elseif(PLATFORM_MAIXCAM)
list(APPEND ADD_REQUIREMENTS FFmpeg maixcam_lib media_server)
list(APPEND ADD_REQUIREMENTS FFmpeg maixcam_lib media_server RtspServer)
if(NOT CONFIG_MAIXCAM_LIB_COMPILE_FROM_SOURCE)
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--start-group vision/libvision.a -lmaixcam_lib -Wl,--end-group" PARENT_SCOPE)
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -Wl,--start-group vision/libvision.a -lmaixcam_lib -Wl,--end-group" PARENT_SCOPE)

View File

@@ -252,6 +252,7 @@ namespace maix::rtsp
uint64_t _timestamp;
uint64_t _last_ms;
int _region_max_number;
void *_param;
};
} // namespace maix::rtsp

View File

@@ -103,6 +103,7 @@ namespace maix::video
_pcm = NULL;
_image = NULL;
_raw_data = NULL;
}
/**
@@ -129,6 +130,7 @@ namespace maix::video
_audio_channels = channels;
_pcm = NULL;
_image = NULL;
_raw_data = NULL;
}
~Context() {
@@ -143,6 +145,12 @@ namespace maix::video
_pcm = NULL;
}
}
if (_raw_data) {
free(_raw_data);
_raw_data = NULL;
_raw_data_size = 0;
}
}
/**
@@ -211,6 +219,45 @@ namespace maix::video
_last_pts = last_pts < 0 ? 0 : last_pts;
}
/**
* @brief Set private data
* @param data private raw data
* @param data_size private raw data size
* @param duration Duration of the current image. unit: timebase
* @param pts The start time of this image playback. If it is 0, it means this parameter is not supported. unit: timebase
* @param last_pts The start time of the previous image playback. It can be used to ensure the playback order. If it is 0, it means this parameter is not supported. unit: timebase
* @maixcdk maix.video.Context.set_raw_data
*/
void set_raw_data(void *data, size_t data_size, int duration = 0, uint64_t pts = 0, uint64_t last_pts = 0, bool copy = false) {
if (copy) {
void *new_data = malloc(data_size);
err::check_null_raise(new_data, "malloc raw data failed!");
memcpy(new_data, data, data_size);
_raw_data = new_data;
_raw_data_size = data_size;
} else {
_raw_data = data;
_raw_data_size = data_size;
}
_duration = duration < 0 ? 0 : duration;
_pts = pts < 0 ? 0 : pts;
_last_pts = last_pts < 0 ? 0 : last_pts;
}
/**
* @brief Get private data
* @maixcdk maix.video.Context.get_raw_data
*/
void *get_raw_data() {
void *new_data = _raw_data;
_raw_data = NULL;
return new_data;
}
size_t get_raw_data_size() {
return _raw_data_size;
}
/**
* @brief Retrieve the image data to be played.
* @attention Note that if you call this interface, you are responsible for releasing the memory of the image, and this interface cannot be called again.
@@ -272,6 +319,8 @@ namespace maix::video
private:
video::MediaType _media_type;
image::Image *_image;
void *_raw_data;
size_t _raw_data_size;
uint64_t _pts;
uint64_t _last_pts;
std::vector<int> _timebase; // [den, num], timebase = den / num
@@ -820,6 +869,13 @@ namespace maix::video
*/
video::Context * decode(bool block = true);
/**
* Unpacking the video and audio stream
* @return Unpacking context information.
* @maixcdk maix.video.Decoder.unpack
*/
video::Context * unpack();
/**
* @brief Get sample rate of audio (only valid in the context of audio)
* @return sample rate

View File

@@ -11,6 +11,7 @@
#include "maix_basic.hpp"
#include <dirent.h>
#include "sophgo_middleware.hpp"
#include "maix_rtsp_server.hpp"
namespace maix::rtsp
{
@@ -122,9 +123,22 @@ namespace maix::rtsp
return err::ERR_NONE;
}
enum RtspStatus{
RTSP_IDLE = 0,
RTSP_RUNNING,
RTSP_STOP,
};
typedef struct {
MaixRtspServer *rtsp_server;
enum RtspStatus status;
int *clients;
} rtsp_param_t;
Rtsp::Rtsp(std::string ip, int port, int fps, rtsp::RtspStreamType stream_type) {
err::check_bool_raise(stream_type == rtsp::RtspStreamType::RTSP_STREAM_H265,
"support RTSP_STREAM_H265 only!");
rtsp_param_t *param = (rtsp_param_t *)malloc(sizeof(rtsp_param_t));
err::check_null_raise(param, "malloc failed!");
this->_ip = ip;
this->_port = port;
this->_fps = fps;
@@ -132,6 +146,7 @@ namespace maix::rtsp
this->_bind_camera = false;
this->_is_start = false;
this->_thread = NULL;
this->_param = param;
this->_region_max_number = 16;
for (int i = 0; i < this->_region_max_number; i ++) {
this->_region_list.push_back(NULL);
@@ -139,22 +154,66 @@ namespace maix::rtsp
this->_region_used_list.push_back(false);
}
char *new_ip = NULL;
if (this->_ip.size() != 0) {
new_ip = (char *)this->_ip.c_str();
if (_ip.size() == 0) {
_ip = "0.0.0.0";
}
this->_timestamp = 0;
this->_last_ms = 0;
err::check_bool_raise(!rtsp_server_init(new_ip, this->_port), "Rtsp init failed!");
MaixRtspServerBuilder rtsp_builder = MaixRtspServerBuilder()
.set_ip(_ip)
.set_port(this->_port)
.set_session_name("live")
.set_audio_channels(1)
.set_audio_sample_rate(48000);log::info("============[%s][%d]", __func__, __LINE__);
std::shared_ptr<MaixRtspServer> rtsp_server = rtsp_builder.build();log::info("============[%s][%d]", __func__, __LINE__);
// std::string ip = _ip;
// int port = 8554;
// ip = "0.0.0.0";
// port = 8554;
// std::string session_name = "live";
// int audio_sample_rate = 48000;
// int audio_channels = 1;
// printf("ip=%s, port=%d, session_name=%s, audio_sample_rate=%d, audio_channels=%d\r\n", ip.c_str(), port, session_name.c_str(), audio_sample_rate, audio_channels);
// std::shared_ptr<xop::EventLoop> event_loop(new xop::EventLoop());
// std::shared_ptr<xop::RtspServer> server = xop::RtspServer::Create(event_loop.get());
// if (!server->Start(ip, port)) {
// throw "rtsp server start failed";
// }
// xop::MediaSession *session = xop::MediaSession::CreateNew(session_name);
// session->AddSource(xop::channel_0, xop::H264Source::CreateNew());
// session->AddSource(xop::channel_1, xop::AACSource::CreateNew(audio_sample_rate, audio_channels));
// int *clients = (int *)malloc(sizeof(int));
// if (clients == nullptr) {
// throw "alloc memory failed";
// }
// session->AddNotifyConnectedCallback([clients] (xop::MediaSessionId sessionId, std::string peer_ip, uint16_t peer_port){
// printf("RTSP client connect, ip=%s, port=%hu \n", peer_ip.c_str(), peer_port);
// (*clients) ++;
// });
// session->AddNotifyDisconnectedCallback([clients](xop::MediaSessionId sessionId, std::string peer_ip, uint16_t peer_port) {
// printf("RTSP client disconnect, ip=%s, port=%hu \n", peer_ip.c_str(), peer_port);
// (*clients) --;
// });
// xop::MediaSessionId session_id = server->AddSession(session);
// printf("================[%s][%d]\n", __func__, __LINE__);
// auto rtsp_server = new MaixRtspServer(clients, server, session_id);printf("================[%s][%d]\n", __func__, __LINE__);
// param->rtsp_server = rtsp_server;printf("================[%s][%d]\n", __func__, __LINE__);
// err::check_null_raise(new_server, "rtsp server init failed!");log::info("============[%s][%d]", __func__, __LINE__);
}
Rtsp::~Rtsp() {
if (this->_is_start) {
this->stop();
}
if (0 != rtsp_server_deinit()) {
log::warn("rtsp deinit failed!\r\n");
rtsp_param_t *param = (rtsp_param_t *)_param;
if (param) {
if (param->rtsp_server) {
delete param->rtsp_server;
param->rtsp_server = nullptr;
}
}
for (auto &region : this->_region_list) {
@@ -163,99 +222,56 @@ namespace maix::rtsp
}
static void _camera_push_thread(void *args) {
Rtsp *rtsp = (Rtsp *)args;
void *data;
int data_size, width, height, format;
int vi_ch = 0, enc_ch = 1;
int fps = rtsp->to_camera()->fps();
uint64_t wait_us = 1000000 / fps;
uint64_t last_us = time::time_us();
while (rtsp->rtsp_is_start()) {
rtsp->update_timestamp();
uint64_t timestamp = rtsp->get_timestamp();
rtsp_param_t *param = (rtsp_param_t *)args;
MaixRtspServer *rtsp_server = param->rtsp_server;
while (param->status != RTSP_RUNNING) {
log::info("rtsp server clients:%d", rtsp_server->get_clients());
if (rtsp_server->get_clients() > 0) {
mmf_h265_stream_t stream;
if (!mmf_enc_h265_pop(enc_ch, &stream)) {
int stream_size = 0;
for (int i = 0; i < stream.count; i ++) {
// log::info("[%d] stream.data:%p stream.len:%d\n", i, stream.data[i], stream.data_size[i]);
stream_size += stream.data_size[i];
}
if (stream.count > 1) {
uint8_t *stream_buffer = (uint8_t *)malloc(stream_size);
if (stream_buffer) {
int copy_length = 0;
for (int i = 0; i < stream.count; i ++) {
memcpy(stream_buffer + copy_length, stream.data[i], stream.data_size[i]);
copy_length += stream.data_size[i];
}
rtsp_send_h265_data(timestamp, stream_buffer, copy_length);
free(stream_buffer);
} else {
log::warn("malloc failed!\r\n");
}
} else if (stream.count == 1) {
rtsp_send_h265_data(timestamp, (uint8_t *)stream.data[0], stream.data_size[0]);
}
if (mmf_enc_h265_free(enc_ch)) {
log::warn("mmf_enc_h265_free failed\n");
continue;
}
}
}
if (mmf_vi_frame_pop(vi_ch, &data, &data_size, &width, &height, &format)) {
continue;
}
while (time::ticks_us() - last_us < wait_us) {
time::sleep_us(50);
}
last_us = time::ticks_us();
if (mmf_enc_h265_push(enc_ch, (uint8_t *)data, width, height, format)) {
log::warn("mmf_enc_h265_push failed\n");
continue;
}
mmf_vi_frame_free(vi_ch);
if (param->status == RTSP_STOP) {
param->status = RTSP_IDLE;
}
}
err::Err Rtsp::start() {
err::Err err = err::ERR_NONE;
if (0 != rtsp_server_start()) {
log::error("rtsp start failed!\r\n");
rtsp_param_t *param = (rtsp_param_t *)_param;
if (!param) {
return err::ERR_RUNTIME;
}
if (this->_bind_camera) {
this->_thread = new thread::Thread(_camera_push_thread, this);
if (this->_thread == NULL) {
log::error("create camera thread failed!\r\n");
return err::ERR_RUNTIME;
}
if (param->status != RTSP_IDLE) {
return err::ERR_BUSY;
}
this->_is_start = true;
if (!_bind_camera) {
log::error("bind camera failed!");
return err::ERR_RUNTIME;
}
param->status = RTSP_RUNNING;
_thread = new thread::Thread(_camera_push_thread, param);
if (_thread == NULL) {
log::error("create camera thread failed!\r\n");
return err::ERR_RUNTIME;
}
return err;
}
err::Err Rtsp::stop() {
err::Err err = err::ERR_NONE;
this->_is_start = false;
if (this->_bind_camera) {
this->_thread->join();
rtsp_param_t *param = (rtsp_param_t *)_param;
if (param->status != RTSP_RUNNING) {
return err::ERR_NONE;
}
if (0 != rtsp_server_stop()) {
log::error("rtsp stop failed!\r\n");
this->_is_start = true;
return err::ERR_RUNTIME;
param->status = RTSP_STOP;
if (_bind_camera) {
_thread->join();
_thread = nullptr;
}
return err;

View File

@@ -2552,6 +2552,116 @@ _retry:
}
}
video::Context *Decoder::unpack() {
decoder_param_t *param = (decoder_param_t *)_param;
AVPacket *pPacket = param->pPacket;
AVFormatContext *pFormatContext = param->pFormatContext;
AVBSFContext * bsfc = param->bsfc;
int video_stream_index = param->video_stream_index;
int audio_stream_index = param->audio_stream_index;
AVCodecContext *audio_codec_ctx = param->audio_codec_ctx;
AVFrame *audio_frame = param->audio_frame;
int resample_channels = param->resample_channels;
int resample_sample_rate = param->resample_sample_rate;
enum AVSampleFormat resample_format = param->resample_sample_format;
SwrContext *swr_ctx = param->swr_ctx;
image::Image *img = NULL;
video::Context *context = NULL;
uint64_t last_pts = 0;
bool is_video = false;
bool is_audio = false;
while (av_read_frame(pFormatContext, pPacket) >= 0) {
if (pPacket->stream_index == video_stream_index) {
last_pts = _last_pts;
_last_pts = pPacket->pts;
is_video = true;
int64_t packet_duration = pPacket->duration;
switch (param->video_format) {
case VIDEO_FORMAT_H264:
break;
case VIDEO_FORMAT_H264_FLV:
err::check_bool_raise(!av_bsf_send_packet(bsfc, pPacket), "av_bsf_send_packet failed");
err::check_bool_raise(!av_bsf_receive_packet(bsfc, pPacket), "av_bsf_send_packet failed");
break;
case VIDEO_FORMAT_H264_MP4:
err::check_bool_raise(!av_bsf_send_packet(bsfc, pPacket), "av_bsf_send_packet failed");
err::check_bool_raise(!av_bsf_receive_packet(bsfc, pPacket), "av_bsf_send_packet failed");
break;
default:
err::check_raise(err::ERR_RUNTIME, "Unknown video format");
break;
}
video::MediaType media_type = MEDIA_TYPE_VIDEO;
std::vector<int> timebase = {(int)pFormatContext->streams[video_stream_index]->time_base.num,
(int)pFormatContext->streams[video_stream_index]->time_base.den};
context = new video::Context(media_type, timebase);
context->set_raw_data(pPacket->data, pPacket->size, packet_duration, pPacket->pts, last_pts, true);
av_packet_unref(pPacket);
break;
} else if (pPacket->stream_index == audio_stream_index) {
is_audio = true;
if (avcodec_send_packet(audio_codec_ctx, pPacket) >= 0) {
while (avcodec_receive_frame(audio_codec_ctx, audio_frame) >= 0) {
uint8_t *output;
int out_samples = av_rescale_rnd(
swr_get_delay(swr_ctx, audio_codec_ctx->sample_rate) + audio_frame->nb_samples,
audio_codec_ctx->sample_rate,
audio_codec_ctx->sample_rate,
AV_ROUND_UP
);
av_samples_alloc(&output, NULL, audio_codec_ctx->channels, out_samples, AV_SAMPLE_FMT_S16, 0);
int converted_samples = swr_convert(
swr_ctx,
&output, out_samples,
(const uint8_t **)audio_frame->data, audio_frame->nb_samples
);
video::MediaType media_type = MEDIA_TYPE_AUDIO;
if (converted_samples > 0) {
// Process the converted PCM data in `output` (e.g., write to file or buffer)
// fwrite(output, 1, converted_samples * codec_ctx->channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16), stdout);
media_type = MEDIA_TYPE_AUDIO;
} else {
media_type = MEDIA_TYPE_UNKNOWN;
}
std::vector<int> timebase = {(int)pFormatContext->streams[audio_stream_index]->time_base.num,
(int)pFormatContext->streams[audio_stream_index]->time_base.den};
// context = new video::Context(MEDIA_TYPE_UNKNOWN, timebase);
context = new video::Context(media_type, timebase, resample_sample_rate, _audio_format_from_alsa(resample_format), resample_channels);
Bytes data(output, converted_samples * audio_codec_ctx->channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16));
context->set_pcm(&data, pPacket->duration, pPacket->pts);
// log::info("data:%p size:%d sample_rate:%d channel:%d timebase:%d/%d, duration:%d pts:%d", output,
// converted_samples * audio_codec_ctx->channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16),
// audio_codec_ctx->sample_rate, audio_codec_ctx->channels,
// timebase[0], timebase[1], pPacket->duration, audio_frame->pts);
av_freep(&output);
}
}
break;
}
av_packet_unref(pPacket);
}
if (is_video) {
if (context) {
param->next_pts += context->duration();
}
return context;
} else if (is_audio) {
return context;
} else {
return NULL;
}
}
double Decoder::seek(double time) {
#if 0
decoder_param_t *param = (decoder_param_t *)_param;

View File

@@ -348,8 +348,16 @@ namespace maix::audio
int len = 0;
int frame_byte = snd_pcm_format_width(format) / 8;
int write_count = buffer_size / frame_byte / channels;
if (snd_pcm_wait(handle, 1000) < 0) {
printf("alsa pcm wait timeout!\r\n");
int ret = 0;
int retry_count = 2;
_retry:
if ((ret = snd_pcm_wait(handle, 5000)) < 0) {
// printf(" alsa pcm wait timeout! retry_count:%d ret:%d(%s)\r\n", retry_count, ret, snd_strerror(errno));
snd_pcm_prepare(handle);
if (retry_count > 0) {
retry_count--;
goto _retry;
}
return 0;
}

View File

@@ -14,7 +14,10 @@ void helper(void)
{
printf( "========================\r\n"
"Intput param:\r\n"
"0 <filepath> <seek_s> : decode the video and display. example: ./decoder_demo 0 test.h264\r\n"
"0 <filepath> <seek_s> : decode the video and display. example: ./decoder_demo 0 test.mp4\r\n"
"1 <filepath> <seek_s> : decode the audio and display. example: ./decoder_demo 1 test.mp4\r\n"
"2 <filepath> <seek_s> : decode the audio/video and display. example: ./decoder_demo 2 test.mp4\r\n"
"3 <filepath> <seek_s> : decode the audio/video and display faster. example: ./decoder_demo 3 test.mp4\r\n"
"========================\r\n");
}
@@ -126,7 +129,6 @@ int _main(int argc, char* argv[])
double seek_s = 0.0;
if (argc > 3) seek_s = atof(argv[3]);
video::Decoder decoder = video::Decoder(filepath);
video::Decoder decoder2 = video::Decoder(filepath);
display::Display disp = display::Display();
audio::Player *p = NULL;
uint64_t loop_ms = time::ticks_ms(), last_us = time::ticks_us();
@@ -141,7 +143,6 @@ int _main(int argc, char* argv[])
log::info("audio_sample_rate:%d audio_format:%d audio_channels:%d", decoder.audio_sample_rate(), decoder.audio_format(),decoder.audio_channels());
p = new audio::Player("", decoder.audio_sample_rate(), decoder.audio_format(), decoder.audio_channels());
}
std::list<video::Context *> *audio_list = new std::list<video::Context *>();
std::list<video::Context *> *video_list = new std::list<video::Context *>();
bool find_first_pts = false;
@@ -149,7 +150,13 @@ int _main(int argc, char* argv[])
while (!app::need_exit()) {
uint64_t t = time::ticks_ms();
video::Context *ctx = NULL;
while ((ctx = decoder.decode()) != NULL) {
while (1) {
t = time::ticks_ms();
if ((ctx = decoder.decode()) == NULL) {
break;
}
log::info("decode used %lld ms", time::ticks_ms() - t);
if (ctx->media_type() == video::MEDIA_TYPE_VIDEO) {
video_list->push_back(ctx);
break;
@@ -161,7 +168,6 @@ int _main(int argc, char* argv[])
log::info("decode video over");
break;
}
log::info("decode used %lld ms", time::ticks_ms() - t);
t = time::ticks_ms();
std::list<video::Context *>::iterator iter;
@@ -221,6 +227,163 @@ int _main(int argc, char* argv[])
}
break;
}
case 3: {
if (argc < 3) {
helper();
return -1;
}
std::string filepath = argv[2];
double seek_s = 0.0;
if (argc > 3) seek_s = atof(argv[3]);
video::Decoder decoder = video::Decoder(filepath);
display::Display disp = display::Display();
audio::Player *p = NULL;
uint64_t loop_ms = time::ticks_ms(), last_us = time::ticks_us();
log::info("filepath:%s seek_s:%f", filepath.c_str(), seek_s);
log::info("has video:%d has audio:%d resolution:%dx%d bitrate:%d duration:%.2f s fps:%d seek_s:%f", decoder.has_video(), decoder.has_audio(), decoder.width(), decoder.height(), decoder.bitrate(), decoder.duration(), decoder.fps(), seek_s);
if (decoder.has_video()) {
err::check_bool_raise(decoder.seek(seek_s) >= 0, "decoder.seek failed");
log::info("decoder.seek:%f", decoder.seek());
}
if (decoder.has_audio()) {
log::info("audio_sample_rate:%d audio_format:%d audio_channels:%d", decoder.audio_sample_rate(), decoder.audio_format(),decoder.audio_channels());
p = new audio::Player("", decoder.audio_sample_rate(), decoder.audio_format(), decoder.audio_channels());
}
std::list<video::Context *> *audio_list = new std::list<video::Context *>();
std::list<video::Context *> *video_list = new std::list<video::Context *>();
bool find_first_pts = false;
uint64_t first_play_ms = 0;
uint64_t next_play_ms = 0;
bool show_is_image = false;
VIDEO_FRAME_INFO_S frame = {0};
bool show_frame_is_ready = false;
image::Image *show_image = NULL;
uint64_t show_image_wait_us = 0;
int vdec_ch = 0;
uint64_t last_pts = 0;
while (!app::need_exit()) {
uint64_t t = time::ticks_ms();
video::Context *ctx = NULL;
do {
t = time::ticks_ms();
if ((ctx = decoder.unpack()) == NULL) {
break;
}
log::info("unpack used %lld ms", time::ticks_ms() - t);
if (ctx->media_type() == video::MEDIA_TYPE_VIDEO) {
video_list->push_back(ctx);
break;
} else if (ctx->media_type() == video::MEDIA_TYPE_AUDIO) {
audio_list->push_back(ctx);
}
} while (audio_list->size() < 1);
t = time::ticks_ms();
std::list<video::Context *>::iterator iter;
for(iter=video_list->begin();iter!=video_list->end();iter++) {
video::Context *video_ctx = *iter;
log::info("video pts:%d last_pts:%d", video_ctx->pts(), last_pts);
if (!find_first_pts) {
last_pts = video_ctx->pts();
find_first_pts = true;
first_play_ms = time::ticks_ms();
}
if (last_pts == video_ctx->pts()) {
last_pts += video_ctx->duration();
log::info("[VIDEO] play pts:%.2f ms next_pts:%d curr wait:%lld need wait:%lld",
video::timebase_to_ms(video_ctx->timebase(), video_ctx->pts()), last_pts,
(time::ticks_us() - last_us) / 1000, video_ctx->duration_us() / 1000);
video_list->erase(iter);
void *data = video_ctx->get_raw_data();
if (data) {
size_t data_size = video_ctx->get_raw_data_size();
log::info("Frame :%p size:%d", data, data_size);
VDEC_STREAM_S stStream = {0};
stStream.pu8Addr = (CVI_U8 *)data;
stStream.u32Len = data_size;
stStream.u64PTS = video_ctx->pts();
stStream.bEndOfFrame = CVI_TRUE;
stStream.bEndOfStream = CVI_FALSE;
stStream.bDisplay = 1;
err::check_bool_raise(!mmf_vdec_push_v2(vdec_ch, &stStream));
err::check_bool_raise(!mmf_vdec_pop_v2(vdec_ch, &frame));
show_frame_is_ready = true;
next_play_ms = first_play_ms + video::timebase_to_ms(video_ctx->timebase(), last_pts);
free(data);
}
delete video_ctx;
break;
}
}
log::info("playback video use %lld ms, video list size:%d", time::ticks_ms() - t, video_list->size());
t = time::ticks_ms();
while (audio_list->size() > 0) {
video::Context *audio_ctx = *audio_list->begin();
if (audio_ctx) {
audio_list->pop_front();
if (audio_ctx->media_type() == video::MEDIA_TYPE_AUDIO) {
Bytes *pcm = audio_ctx->get_pcm();
static uint64_t last_ms = 0;
log::info("[AUDIO] play pts:%.2fms last:%lld data:%p length:%d sample_rate:%d format:%d channels:%d duration:%lld(%d) ms",
video::timebase_to_ms(audio_ctx->timebase(), audio_ctx->pts()), time::ticks_ms() - last_ms,
pcm->data, pcm->data_len, audio_ctx->audio_sample_rate(), audio_ctx->audio_format(), audio_ctx->audio_channels(), audio_ctx->duration_us() / 1000, audio_ctx->duration());
last_ms = time::ticks_ms();
if (p) p->play(pcm);
delete pcm;
}
delete audio_ctx;
}
}
log::info("playback audio use %lld ms, audio list size:%d", time::ticks_ms() - t, audio_list->size());
if (show_frame_is_ready) {
mmf_vo_frame_push2(0, 0, 2, &frame);
err::check_bool_raise(!mmf_vdec_free(vdec_ch));
show_frame_is_ready = false;
log::info("Display curr ms:%ld next_ms:%ld", time::ticks_ms(), next_play_ms);
while (time::ticks_ms() < next_play_ms - 1) {
time::sleep_ms(1);
}
last_us = time::ticks_us();
}
if (!ctx && video_list->empty() && audio_list->empty()) {
log::info("decode video over");
break;
}
log::info("loop time:%.2d ms, count:%d", (time::ticks_ms() - loop_ms));
loop_ms = time::ticks_ms();
}
if (audio_list) {
for (auto iter = audio_list->begin(); iter != audio_list->end(); iter++) {
auto ctx = *iter;
delete ctx;
iter = audio_list->erase(iter);
}
delete audio_list;
}
if (video_list) {
for (auto iter = video_list->begin(); iter != video_list->end(); iter++) {
auto ctx = *iter;
delete ctx;
iter = video_list->erase(iter);
}
delete video_list;
}
break;
}
default:
helper();
break;

View File

@@ -5,8 +5,10 @@
#include "maix_image.hpp"
#include "maix_video.hpp"
#include "maix_display.hpp"
#include "maix_audio.hpp"
#include <list>
#include <sys/stat.h>
#include "sophgo_middleware.hpp"
using namespace maix;
@@ -419,9 +421,25 @@ typedef struct {
char *big_img_filename;
PhotoVideo *photo_video;
display::Display *disp;
image::Image *next_image;
uint64_t next_image_try_keep_ms;
video::Decoder *decoder;
audio::Player *audio_player;
std::list<video::Context *> *audio_list;
std::list<video::Context *> *video_list;
image::Image *default_show_image;
uint64_t default_show_keep_ms;
bool next_is_frame;
void *next_show_ptr;
bool find_first_pts;
VIDEO_FRAME_INFO_S *next_raw_frame;
uint64_t first_play_ms, next_play_ms;
uint64_t last_pts;
bool found_frame;
VIDEO_FRAME_INFO_S *video_frame;
bool play_video;
bool pause_video;
@@ -429,20 +447,115 @@ typedef struct {
priv_t priv;
static void config_next_display_image(image::Image *image, uint64_t try_keep_ms)
static void _audio_video_list_reset()
{
if (priv.next_image) {
delete priv.next_image;
priv.next_image = NULL;
if (priv.audio_list) {
for (auto iter = priv.audio_list->begin(); iter != priv.audio_list->end(); iter++) {
auto ctx = *iter;
delete ctx;
iter = priv.audio_list->erase(iter);
}
}
if (priv.video_list) {
for (auto iter = priv.video_list->begin(); iter != priv.video_list->end(); iter++) {
auto ctx = *iter;
delete ctx;
iter = priv.video_list->erase(iter);
}
}
}
static void _audio_video_list_deinit()
{
_audio_video_list_reset();
if (priv.audio_list) {
delete priv.audio_list;
priv.audio_list = NULL;
}
if (priv.video_list) {
delete priv.video_list;
priv.video_list = NULL;
}
}
static void _audio_video_list_init()
{
_audio_video_list_deinit();
priv.audio_list = new std::list<video::Context *>();
priv.video_list = new std::list<video::Context *>();
err::check_null_raise(priv.audio_list, "audio list init failed!");
err::check_null_raise(priv.video_list, "video list init failed!");
}
static void config_default_display_image(image::Image *image, uint64_t try_keep_ms)
{
if (priv.default_show_image) {
delete priv.default_show_image;
priv.default_show_image = nullptr;
}
priv.default_show_image = image->copy();
priv.default_show_keep_ms = try_keep_ms;
}
static void config_next_display_image(void *next_show_ptr, uint64_t try_keep_ms, bool is_raw_frame = false)
{
if (is_raw_frame) {
if (priv.next_show_ptr) {
free(priv.next_show_ptr);
priv.next_show_ptr = nullptr;
}
priv.next_show_ptr = next_show_ptr;
} else {
image::Image *img = (image::Image *)priv.next_show_ptr;
if (priv.next_show_ptr) {
delete img;
priv.next_show_ptr = nullptr;
}
priv.next_show_ptr = next_show_ptr;
}
priv.next_image_try_keep_ms = try_keep_ms;
priv.next_image = image;
priv.next_is_frame = is_raw_frame;
}
static void show_default_image()
{
priv.next_is_frame = false;
priv.next_image_try_keep_ms = 10;
}
static void show_frame()
{
priv.next_is_frame = true;
}
static bool show_next_is_frame() {
return priv.next_is_frame;
}
static image::Image *get_default_display_image()
{
return (image::Image *)priv.default_show_image;
}
static uint64_t get_default_try_keep_ms()
{
return priv.default_show_keep_ms;
}
static image::Image *get_next_display_image()
{
return priv.next_image;
return (image::Image *)priv.next_show_ptr;
}
static void *get_next_display_frame()
{
return priv.next_show_ptr;
}
static uint64_t get_next_image_try_keep_ms()
@@ -450,6 +563,32 @@ static uint64_t get_next_image_try_keep_ms()
return priv.next_image_try_keep_ms;
}
static void decoder_seek(double seek_ms)
{
if (priv.decoder) {
priv.decoder->seek(seek_ms);
_audio_video_list_reset();
priv.find_first_pts = false;
}
}
static void decoder_release()
{
if (priv.decoder) {
if (priv.found_frame) {
if (priv.video_frame) {
free(priv.video_frame);
priv.video_frame = nullptr;
}
err::check_bool_raise(!mmf_vdec_free(0));
priv.found_frame = false;
}
delete priv.decoder;
priv.decoder = NULL;
}
}
static char *find_thumbnail_path_by_path(char *path)
{
photos_list_t *photos = priv.photos;
@@ -563,6 +702,46 @@ void nv21_resize(uint8_t *src_nv21, uint32_t src_width, uint32_t src_height,
}
}
void nv21_resize_frame(uint8_t *y, uint8_t *uv, uint32_t src_width, uint32_t src_height,
uint8_t *dst_nv21, uint32_t dst_width, uint32_t dst_height) {
float scale_x = (float)dst_width / src_width;
float scale_y = (float)dst_height / src_height;
float scale = (scale_x < scale_y) ? scale_x : scale_y;
uint32_t new_width = (uint32_t)(src_width * scale);
uint32_t new_height = (uint32_t)(src_height * scale);
uint32_t offset_x = (dst_width - new_width) / 2;
uint32_t offset_y = (dst_height - new_height) / 2;
memset(dst_nv21, 0, dst_width * dst_height);
memset(dst_nv21 + dst_width * dst_height, 128, dst_width * dst_height / 2);
uint8_t *src_y = y;
uint8_t *dst_y = dst_nv21;
for (uint32_t y = 0; y < new_height; y++) {
uint32_t src_y_index = (uint32_t)(y / scale);
for (uint32_t x = 0; x < new_width; x++) {
uint32_t src_x_index = (uint32_t)(x / scale);
dst_y[(y + offset_y) * dst_width + (x + offset_x)] = src_y[src_y_index * src_width + src_x_index];
}
}
uint8_t *src_uv = uv;
uint8_t *dst_uv = dst_nv21 + dst_width * dst_height;
for (uint32_t y = 0; y < new_height / 2; y++) {
uint32_t src_uv_y_index = (uint32_t)(y / scale);
for (uint32_t x = 0; x < new_width / 2; x++) {
uint32_t src_uv_x_index = (uint32_t)(x / scale);
uint32_t src_index = src_uv_y_index * src_width + 2 * src_uv_x_index;
uint32_t dst_index = (y + offset_y / 2) * dst_width + 2 * (x + offset_x / 2);
dst_uv[dst_index] = src_uv[src_index];
dst_uv[dst_index + 1] = src_uv[src_index + 1];
}
}
}
static lv_image_dsc_t *load_thumbnail_image(char *path, char *thumbnail_path)
{
image::Image *thumbnail_img = NULL;
@@ -585,13 +764,10 @@ static lv_image_dsc_t *load_thumbnail_image(char *path, char *thumbnail_path)
size_t pos = src_path.rfind(".mp4");
if (pos != std::string::npos) {
try {
if (priv.decoder) {
delete priv.decoder;
priv.decoder = NULL;
}
decoder_release();
priv.decoder = new video::Decoder(src_path);
priv.decoder->seek(0);
err::check_null_raise(priv.decoder, "Decoder init failed!");
decoder_seek(0);
auto ctx = priv.decoder->decode_video();
if (ctx && ctx->media_type() == video::MEDIA_TYPE_VIDEO) {
image::Image *img = ctx->image();
@@ -609,13 +785,9 @@ static lv_image_dsc_t *load_thumbnail_image(char *path, char *thumbnail_path)
log::error("decode video %s failed!\r\n", &src_path[0]);
return NULL;
}
delete priv.decoder;
priv.decoder = NULL;
decoder_release();
} catch (std::exception &e) {
if (priv.decoder) {
delete priv.decoder;
priv.decoder = NULL;
}
decoder_release();
log::error("decode video %s failed!\r\n", &src_path[0]);
return NULL;
}
@@ -692,17 +864,18 @@ int app_init(display::Display *disp)
image::Image *img = new image::Image(disp->width(), disp->height(), maix::image::FMT_YVU420SP);
img->clear();
memset((uint8_t *)img->data() + img->width() * img->height(), 128, img->width() * img->height() / 2);
config_next_display_image(img, 0);
config_default_display_image(img, 15);
delete img;
} else {
priv.disp_w = 552;
priv.disp_h = 368;
}
#if 1
priv.photo_video = new PhotoVideo("/maixapp/share/picture", "/maixapp/share/video");
priv.photo_video->collect_video_photo();
// priv.photo_video->print_video_photo_list();
_audio_video_list_init();
// log::info("========= PUSH TO UI ==========");
auto list = priv.photo_video->get_video_photo_list();
auto iter = list->begin();
@@ -730,67 +903,6 @@ int app_init(display::Display *disp)
ui_photo_print();
ui_photo_list_screen_update();
#else
char *base_path = "./photos";
photos_list_t *photos = get_photo_list(base_path);
// if (photos) {
// for (int i = 0; i < photos->photo_directory_num; i ++) {
// photo_directory_t *dir = (photo_directory_t *)&photos->photo_directory[i];
// printf("[dir] path:%s name:%s photo_num:%d\r\n", dir->path, dir->name, dir->photos_num);
// for (int j = 0; j < dir->photos_num; j ++) {
// photo_t *photo = (photo_t *)&dir->photos[j];
// printf("[photo] path:%s thumbnail_path:%s name:%s\r\n", photo->path, photo->thumbnail_path, photo->file_name);
// }
// }
// }
destroy_photos_list(&photos);
lv_image_dsc_t *dsc = &test_img_128x128;
char *dir1 = "2003-03-05";
ui_photo_add_dir(dir1);
ui_photo_add_photo(dir1, "pic1", dsc);
ui_photo_add_photo(dir1, "pic2", dsc);
ui_photo_add_photo(dir1, "pic3", dsc);
char *dir2 = "2013-03-15";
ui_photo_add_dir(dir2);
ui_photo_add_photo(dir2, "pic4", dsc);
ui_photo_add_photo(dir2, "pic5", dsc);
ui_photo_add_photo(dir2, "pic6", dsc);
ui_photo_add_photo(dir2, "pic9", dsc);
ui_photo_add_photo(dir2, "pic7", dsc);
ui_photo_add_photo(dir2, "pic65", dsc);
ui_photo_add_photo(dir2, "pic546", dsc);
ui_photo_add_photo(dir2, "pic4564", dsc);
ui_photo_add_photo(dir2, "pic354345", dsc);
ui_photo_add_photo(dir2, "pic343456", dsc);
ui_photo_add_photo(dir2, "pic343456", dsc);
ui_photo_add_photo(dir2, "pic343456", dsc);
ui_photo_add_photo(dir2, "pic343456", dsc);
ui_photo_add_photo(dir2, "pic343456", dsc);
ui_photo_add_photo(dir2, "pic343456", dsc);
ui_photo_add_photo(dir2, "pic343456", dsc);
ui_photo_add_photo(dir2, "pic343456", dsc);
ui_photo_add_photo(dir2, "pic343456", dsc);
ui_photo_add_photo(dir2, "pic343456", dsc);
char *dir3 = "2026-04-25";
ui_photo_add_dir(dir3);
ui_photo_add_photo(dir3, "pic7", dsc);
ui_photo_add_photo(dir3, "pic8", dsc);
ui_photo_add_photo(dir3, "pic9", dsc);
ui_photo_add_photo(dir3, "pic7", dsc);
ui_photo_add_photo(dir3, "pic8", dsc);
ui_photo_add_photo(dir3, "pic9", dsc);
ui_photo_add_photo(dir3, "pic7", dsc);
ui_photo_add_photo(dir3, "pic8", dsc);
ui_photo_print();
ui_photo_list_screen_update();
#endif
return 0;
}
@@ -857,14 +969,29 @@ static void ui_set_big_image(char *dir_name, char *path)
static void ui_set_video_first_image(char *dir_name, char *path)
{
int retry_cnt = 3;
_retry:
try {
if (priv.decoder) {
delete priv.decoder;
priv.decoder = NULL;
}
decoder_release();
priv.decoder = new video::Decoder(path);
priv.decoder->seek(0);
decoder_seek(0);
err::check_null_raise(priv.decoder, "Decoder init failed!");
log::info("decoder width:%d height:%d", priv.decoder->width(), priv.decoder->height());
if (priv.decoder->has_audio()) {
if (priv.audio_player) {
delete priv.audio_player;
priv.audio_player = NULL;
}
int sample_rate = priv.decoder->audio_sample_rate();
int channel = priv.decoder->audio_channels();
audio::Format format = priv.decoder->audio_format();
log::info("audio_sample_rate:%d audio_format:%d audio_channels:%d", sample_rate, channel, format);
priv.audio_player = new audio::Player("", sample_rate, format, channel);
err::check_null_raise(priv.audio_player, "Audio player init failed!");
}
auto ctx = priv.decoder->decode_video();
if (ctx && ctx->media_type() == video::MEDIA_TYPE_VIDEO) {
image::Image *img = ctx->image();
@@ -895,54 +1022,162 @@ static void ui_set_video_first_image(char *dir_name, char *path)
return;
_error:
if (retry_cnt > 0) {
retry_cnt --;
time::sleep_ms(100);
log::warn("decode video %s failed, retry...\r\n");
goto _retry;
}
log::error("decode video %s failed!\r\n", &path[0]);
return;
}
static void play_video(void)
{
if (!priv.decoder) {
priv.decoder = new video::Decoder(priv. big_img_filename);
priv.decoder->seek(0);
err::check_null_raise(priv.decoder, "Decoder init failed!");
}
if (priv.play_video) {
if (!priv.decoder) {
priv.decoder = new video::Decoder(priv.big_img_filename);
err::check_null_raise(priv.decoder, "Decoder init failed!");
decoder_seek(0);
}
double new_seek = ui_get_video_bar_value() * priv.decoder->duration();
double old_seek = priv.decoder->seek();
if (abs(old_seek - new_seek) >= 1) {
priv.decoder->seek(new_seek);
}
if (priv.decoder->has_audio()) {
if (!priv.audio_player) {
int sample_rate = priv.decoder->audio_sample_rate();
int channel = priv.decoder->audio_channels();
audio::Format format = priv.decoder->audio_format();
log::info("audio_sample_rate:%d audio_format:%d audio_channels:%d", sample_rate, channel, format);
priv.audio_player = new audio::Player("", sample_rate, format, channel);
err::check_null_raise(priv.audio_player, "Audio player init failed!");
}
}
if (priv.decoder) {
do {
auto ctx = priv.decoder->decode_video();
if (ctx) {
if (ctx->media_type() == video::MEDIA_TYPE_VIDEO) {
image::Image *img = ctx->image();
if (img) {
// uint64_t t = time::ticks_ms();
image::Image *new_img = new image::Image(priv.disp->width(), priv.disp->height(), image::Format::FMT_YVU420SP);
nv21_resize((uint8_t *)img->data(), img->width(), img->height(), (uint8_t *)new_img->data(),new_img->width(), new_img->height());
config_next_display_image(new_img, ctx->duration_us() / 1000);
ui_set_video_bar_s(priv.decoder->seek(), priv.decoder->duration());
// delete new_img; // delete auto
delete img;
delete ctx;
double new_seek = ui_get_video_bar_value() * priv.decoder->duration();
double old_seek = priv.decoder->seek();
if (abs(old_seek - new_seek) >= 1) {
decoder_seek(new_seek);
log::info("config decoder seek :%f", new_seek);
}
video::Decoder *decoder = priv.decoder;
if (decoder) {
uint64_t t = time::ticks_ms();
video::Context *ctx = NULL;
do {
while ((ctx = decoder->unpack()) != nullptr) {
if (ctx->media_type() == video::MEDIA_TYPE_VIDEO) {
priv.video_list->push_back(ctx);
break;
} else if (ctx->media_type() == video::MEDIA_TYPE_AUDIO) {
priv.audio_list->push_back(ctx);
}
} else {
delete ctx;
continue;
}
} else {
} while (priv.audio_list->size() < 1 && ctx);
log::info("unpack video/audio use %lld ms, video list size:%d, audio list size:%d", time::ticks_ms() - t, priv.video_list->size(), priv.audio_list->size());
t = time::ticks_ms();
std::list<video::Context *>::iterator iter;
for(iter=priv.video_list->begin();iter!=priv.video_list->end();iter++) {
video::Context *video_ctx = *iter;
if (!priv.find_first_pts) {
priv.last_pts = video_ctx->pts();
priv.find_first_pts = true;
priv.first_play_ms = priv.next_play_ms = time::ticks_ms() - video::timebase_to_ms(video_ctx->timebase(), video_ctx->pts());
}
if (priv.last_pts == video_ctx->pts()) {
priv.last_pts += video_ctx->duration();
// log::info("[VIDEO] play pts:%.2f ms next_pts:%d curr wait:%lld need wait:%lld",
// video::timebase_to_ms(video_ctx->timebase(), video_ctx->pts()), priv.last_pts,
// (time::ticks_us() - priv.last_us) / 1000, video_ctx->duration_us() / 1000);
iter = priv.video_list->erase(iter);
void *data = video_ctx->get_raw_data();
if (data) {
size_t data_size = video_ctx->get_raw_data_size();
VDEC_STREAM_S stStream = {0};
stStream.pu8Addr = (CVI_U8 *)data;
stStream.u32Len = data_size;
stStream.u64PTS = video_ctx->pts();
stStream.bEndOfFrame = CVI_TRUE;
stStream.bEndOfStream = CVI_FALSE;
stStream.bDisplay = 1;
VIDEO_FRAME_INFO_S *frame = (VIDEO_FRAME_INFO_S *)malloc(sizeof(VIDEO_FRAME_INFO_S));
err::check_null_raise(frame, "video frame is null!");
memset(frame, 0, sizeof(VIDEO_FRAME_INFO_S));
if (priv.found_frame) {
if (priv.video_frame) {
free(priv.video_frame);
priv.video_frame = nullptr;
}
err::check_bool_raise(!mmf_vdec_free(0));
priv.found_frame = false;
}
err::check_bool_raise(!mmf_vdec_push_v2(0, &stStream));
err::check_bool_raise(!mmf_vdec_pop_v2(0, frame));
#if 0
image::Image *new_img = new image::Image(priv.disp->width(), priv.disp->height(), image::Format::FMT_YVU420SP);
nv21_resize_frame( (uint8_t *)frame->stVFrame.pu8VirAddr[0],
(uint8_t *)frame->stVFrame.pu8VirAddr[1],
frame->stVFrame.u32Width, frame->stVFrame.u32Height,
(uint8_t *)new_img->data(),new_img->width(), new_img->height());
config_next_display_image(new_img, ctx->duration_us() / 1000);
// delete new_img; // delete in next call config_next_display_image
err::check_bool_raise(!mmf_vdec_free(0));
free(frame);
#else
show_frame();
priv.found_frame = true;
priv.video_frame = frame;
#endif
ui_set_video_bar_s(priv.decoder->seek(), priv.decoder->duration());
priv.next_play_ms = priv.first_play_ms + video::timebase_to_ms(video_ctx->timebase(), video_ctx->pts());
free(data);
}
delete video_ctx;
break;
}
}
log::info("playback video use %lld ms, video list size:%d", time::ticks_ms() - t, priv.video_list->size());
while (priv.audio_list->size() > 0) {
video::Context *audio_ctx = *priv.audio_list->begin();
if (audio_ctx) {
priv.audio_list->pop_front();
if (audio_ctx->media_type() == video::MEDIA_TYPE_AUDIO) {
Bytes *pcm = audio_ctx->get_pcm();
if (priv.audio_player) priv.audio_player->play(pcm);
delete pcm;
}
delete audio_ctx;
}
}
log::info("playback audio use %lld ms, audio list size:%d", time::ticks_ms() - t, priv.audio_list->size());
if (ctx == nullptr && priv.video_list->size() == 0 && priv.audio_list->size() == 0) {
show_default_image();
_audio_video_list_reset();
ui_set_video_bar_s(0, priv.decoder->duration());
delete priv.decoder;
priv.decoder = NULL;
decoder_release();
priv.find_first_pts = false;
priv.pause_video = true;
priv.play_video = false;
break;
}
} while (1);
}
}
if (priv.found_frame) {
mmf_vo_frame_push2(0, 0, 2, priv.video_frame);
}
int view_flag = ui_get_view_flag();
if (view_flag == 4 && priv.pause_video && view_flag != 5) {
ui_set_view_flag(5);
}
}
@@ -952,35 +1187,46 @@ int app_loop(void)
printf("release video bar\r\n");
double value = ui_get_video_bar_value();
if (priv.decoder) {
priv.decoder->seek(value * priv.decoder->duration());
decoder_seek(value * priv.decoder->duration());
}
printf("percent:%f\r\n", value);
}
int view_flag = ui_get_view_flag();
if (priv.play_video) {
play_video();
}
if (view_flag == 4 && priv.pause_video && view_flag != 5) {
ui_set_view_flag(5);
}
play_video();
if (priv.disp) {
image::Image *img = get_next_display_image();
uint64_t try_keep_ms = get_next_image_try_keep_ms();
if (img) {
// log::info("image resolution:%dx%d image format:%s", img->width(), img->height(), image::fmt_names[img->format()].c_str());
priv.disp->show(*img, image::FIT_COVER);
// delete img; // delete in config_next_display_image()
// time::sleep_ms(try_keep_ms);
}
static uint64_t last_show_ms = time::ticks_ms();
while (time::ticks_ms() - last_show_ms <= (try_keep_ms > 0 ? try_keep_ms : 10)) {
time::sleep_ms(1);
if (!show_next_is_frame()) {
image::Image *img = nullptr;
uint64_t try_keep_ms = 0;
if ((img = get_next_display_image()) == nullptr) {
img = get_default_display_image();
try_keep_ms = get_default_try_keep_ms();
} else {
try_keep_ms = get_next_image_try_keep_ms();
}
if (img) {
priv.disp->show(*img, image::FIT_COVER);
}
while (time::ticks_ms() - last_show_ms <= (try_keep_ms > 0 ? try_keep_ms : 10)) {
time::sleep_ms(1);
}
last_show_ms = time::ticks_ms();
} else {
while (time::ticks_ms() < priv.next_play_ms) {
time::sleep_ms(1);
}
while (time::ticks_ms() - last_show_ms <= 10) {
time::sleep_ms(1);
}
last_show_ms = time::ticks_ms();
}
last_show_ms = time::ticks_ms();
}
if (ui_get_touch_small_image_flag()) {
@@ -1147,16 +1393,28 @@ int app_loop(void)
printf("The next photo is not found!\r\n");
}
}
#if 0
static uint64_t loop_ms = 0;
if (time::ticks_ms() - loop_ms > 5) {
log::info(" loop time: %lld", time::ticks_ms() - loop_ms);
}
loop_ms = time::ticks_ms();
#endif
return 0;
}
int app_deinit(void)
{
if (priv.decoder) {
delete priv.decoder;
priv.decoder = NULL;
_audio_video_list_deinit();
if (priv.audio_player) {
delete priv.audio_player;
priv.audio_player = NULL;
}
decoder_release();
if (priv.photo_video) {
delete priv.photo_video;
priv.photo_video = NULL;

View File

@@ -32,7 +32,7 @@ int _main(int argc, char **argv)
app_pre_init();
// init display
display::Display disp = display::Display();
display::Display disp = display::Display(-1, -1, image::FMT_YVU420SP);
err::check_bool_raise(disp.is_opened(), "camera open failed");
display::Display *other_disp = disp.add_channel(); // This object(other_disp) is depend on disp, so we must keep disp.show() running.
err::check_bool_raise(disp.is_opened(), "display open failed");