2024-06-13 15:41:40 +08:00
|
|
|
#include "VideoPlayer.h"
|
|
|
|
#include "BoostLog.h"
|
|
|
|
#include <QImage>
|
|
|
|
extern "C" {
|
2024-09-05 22:33:07 +08:00
|
|
|
#include <libavdevice/avdevice.h>
|
|
|
|
#include <libavformat/avformat.h>
|
2024-06-13 15:41:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
VideoPlayer::VideoPlayer() {
|
|
|
|
static bool init = false;
|
|
|
|
if (!init) {
|
|
|
|
avdevice_register_all();
|
|
|
|
init = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoPlayer::setFrameCallback(FrameCallback &&callback) {
|
|
|
|
m_callback = std::move(callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoPlayer::setErrorCallback(ErrorCallback &&callback) {
|
|
|
|
m_errorCallback = std::move(callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
VideoPlayer::~VideoPlayer() {
|
|
|
|
close();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VideoPlayer::open(const std::string &deviceName) {
|
|
|
|
bool ret = true;
|
|
|
|
m_formatContext = avformat_alloc_context();
|
2024-10-02 03:01:37 +08:00
|
|
|
#ifdef WIN32
|
|
|
|
constexpr auto format = "dshow";
|
|
|
|
std::ostringstream oss;
|
|
|
|
oss << "video=" << deviceName;
|
|
|
|
auto device = oss.str();
|
|
|
|
#else
|
|
|
|
constexpr auto format = "v4l2";
|
|
|
|
auto &device = deviceName;
|
|
|
|
#endif
|
|
|
|
auto inputFormat = av_find_input_format(format);
|
2024-06-13 15:41:40 +08:00
|
|
|
|
|
|
|
AVDictionary *dictionary = nullptr;
|
|
|
|
// ffmpeg -f dshow -list_options true -i video="UVC Camera"
|
|
|
|
av_dict_set(&dictionary, "video_size", "800*600", 0);
|
|
|
|
|
2024-10-02 03:01:37 +08:00
|
|
|
int status = avformat_open_input(&m_formatContext, device.c_str(), inputFormat, &dictionary);
|
2024-06-13 15:41:40 +08:00
|
|
|
if (status != 0) {
|
|
|
|
char message[256] = {0};
|
|
|
|
av_make_error_string(message, sizeof(message), status);
|
|
|
|
LOG(error) << "open device[" << device << "] failed: " << status << "," << message;
|
|
|
|
ret = false;
|
|
|
|
} else {
|
2024-10-02 03:01:37 +08:00
|
|
|
av_dump_format(m_formatContext, 0, device.c_str(), 0);
|
|
|
|
// for (unsigned int i = 0; i < m_formatContext->nb_streams; i++) {
|
|
|
|
// AVStream *stream = m_formatContext->streams[i];
|
|
|
|
// if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
|
|
|
// std::cout << "当前分辨率: " << stream->codecpar->width << "x" << stream->codecpar->height << std::endl;
|
|
|
|
// }
|
|
|
|
// }
|
2024-06-13 15:41:40 +08:00
|
|
|
m_exit = false;
|
|
|
|
m_thread = std::thread(&VideoPlayer::run, this);
|
|
|
|
}
|
|
|
|
av_dict_free(&dictionary);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoPlayer::close() {
|
|
|
|
m_exit = true;
|
|
|
|
if (m_thread.joinable()) {
|
|
|
|
m_thread.join();
|
|
|
|
}
|
|
|
|
avformat_close_input(&m_formatContext);
|
|
|
|
avformat_free_context(m_formatContext);
|
|
|
|
m_formatContext = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoPlayer::run() {
|
|
|
|
auto packet = av_packet_alloc();
|
|
|
|
while (!m_exit) {
|
|
|
|
auto status = av_read_frame(m_formatContext, packet);
|
|
|
|
if (status == 0) {
|
|
|
|
QImage image;
|
|
|
|
image.loadFromData(packet->data, packet->size);
|
2024-09-25 18:44:04 +08:00
|
|
|
QTransform transform;
|
|
|
|
transform.rotate(90);
|
|
|
|
image = image.transformed(transform);
|
2024-06-13 15:41:40 +08:00
|
|
|
if (!image.isNull() && m_callback) m_callback(image);
|
|
|
|
} else {
|
|
|
|
char message[256] = {0};
|
|
|
|
av_make_error_string(message, sizeof(message), status);
|
|
|
|
if (m_errorCallback) m_errorCallback(status, message);
|
|
|
|
LOG(error) << "av_read_frame() failed: " << status << "," << message;
|
|
|
|
if (status == -EIO) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
av_packet_unref(packet);
|
|
|
|
}
|
|
|
|
|
|
|
|
av_packet_free(&packet);
|
|
|
|
QImage image(800, 600, QImage::Format_RGB888);
|
|
|
|
image.fill(Qt::black);
|
|
|
|
if (m_callback) m_callback(image);
|
|
|
|
}
|