mirror of
https://github.com/sipeed/MaixCDK.git
synced 2026-09-15 06:03:02 -05:00
* photos support play audio
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -252,6 +252,7 @@ namespace maix::rtsp
|
||||
uint64_t _timestamp;
|
||||
uint64_t _last_ms;
|
||||
int _region_max_number;
|
||||
void *_param;
|
||||
};
|
||||
} // namespace maix::rtsp
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ®ion : 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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user