实现jpeg编码。

This commit is contained in:
luocai 2024-09-11 14:18:56 +08:00
parent c48c31c316
commit a865f749be
8 changed files with 198 additions and 3 deletions

View File

@ -2,6 +2,7 @@
#include "BoostLog.h" #include "BoostLog.h"
#include "Configuration.h" #include "Configuration.h"
#include "H264Palyer.h" #include "H264Palyer.h"
#include "Settings.h"
#include "VideoFrameProvider.h" #include "VideoFrameProvider.h"
#include <QFileInfo> #include <QFileInfo>
#include <QFont> #include <QFont>
@ -9,10 +10,31 @@
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QQmlContext> #include <QQmlContext>
std::ostream &operator<<(std::ostream &os, const ImageFormat &format) {
switch (format) {
case ImageFormat::Jpeg:
os << "Jpeg";
break;
case ImageFormat::YUV:
os << "YUV";
break;
default:
os << "Unknown";
break;
}
return os;
}
Application::Application(int &argc, char **argv) Application::Application(int &argc, char **argv)
: m_app(std::make_shared<QGuiApplication>(argc, argv)), m_videoFrameProvider(new VideoFrameProvider()), : m_app(std::make_shared<QGuiApplication>(argc, argv)), m_videoFrameProvider(new VideoFrameProvider()),
m_player(std::make_shared<H264Palyer>()), m_devices(new DeviceListModel(this)), m_player(std::make_shared<H264Palyer>()), m_devices(new DeviceListModel(this)),
m_collector(new DataCollection(this)) { m_collector(new DataCollection(this)) {
using namespace Amass;
m_settings = Singleton<Settings>::instance<Construct>();
m_settings->load();
m_collector->setImageFormat(m_settings->imageFormat(), m_settings->imageQuality());
QFont font; QFont font;
font.setPointSize(16); font.setPointSize(16);
font.setFamily("微软雅黑"); font.setFamily("微软雅黑");

View File

@ -12,6 +12,7 @@
class QGuiApplication; class QGuiApplication;
class VideoFrameProvider; class VideoFrameProvider;
class Settings;
class H264Palyer; class H264Palyer;
class Application : public QObject { class Application : public QObject {
@ -102,6 +103,7 @@ protected:
private: private:
std::shared_ptr<QGuiApplication> m_app; std::shared_ptr<QGuiApplication> m_app;
VideoFrameProvider *m_videoFrameProvider = nullptr; VideoFrameProvider *m_videoFrameProvider = nullptr;
std::shared_ptr<Settings> m_settings;
std::shared_ptr<H264Palyer> m_player; std::shared_ptr<H264Palyer> m_player;
std::weak_ptr<DeviceConnection> m_device; std::weak_ptr<DeviceConnection> m_device;
DeviceListModel *m_devices = nullptr; DeviceListModel *m_devices = nullptr;

View File

@ -52,6 +52,7 @@ add_executable(AntiClipSettings
DeviceListModel.h DeviceListModel.cpp DeviceListModel.h DeviceListModel.cpp
H264Palyer.h H264Palyer.cpp H264Palyer.h H264Palyer.cpp
VideoFrameProvider.h VideoFrameProvider.cpp VideoFrameProvider.h VideoFrameProvider.cpp
Settings.h Settings.cpp
) )
if (Qt6_FOUND) if (Qt6_FOUND)
@ -92,10 +93,12 @@ add_subdirectory(${Projects_ROOT}/Kylin/Encrypt Encrypt)
target_include_directories(AntiClipSettings target_include_directories(AntiClipSettings
PRIVATE ${FFmpeg_INCLUDE_DIR} PRIVATE ${FFmpeg_INCLUDE_DIR}
PRIVATE ${CMAKE_CURRENT_BINARY_DIR} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
PRIVATE ${Libraries_ROOT}/libjpeg-turbo64/include
) )
target_link_directories(AntiClipSettings target_link_directories(AntiClipSettings
PRIVATE ${FFmpeg_LIB_DIR} PRIVATE ${FFmpeg_LIB_DIR}
PRIVATE ${Libraries_ROOT}/libjpeg-turbo64/lib
) )
target_link_libraries(AntiClipSettings target_link_libraries(AntiClipSettings
@ -111,6 +114,7 @@ target_link_libraries(AntiClipSettings
PRIVATE avformat PRIVATE avformat
PRIVATE Universal PRIVATE Universal
PRIVATE Encrypt PRIVATE Encrypt
PRIVATE turbojpeg-static
PRIVATE Ws2_32 PRIVATE Ws2_32
) )

View File

@ -7,6 +7,7 @@
#include <boost/json/object.hpp> #include <boost/json/object.hpp>
#include <boost/json/parse.hpp> #include <boost/json/parse.hpp>
#include <boost/json/serialize.hpp> #include <boost/json/serialize.hpp>
#include <turbojpeg.h>
DataCollection::DataCollection(QObject *parent) : QObject{parent} { DataCollection::DataCollection(QObject *parent) : QObject{parent} {
m_manager = new QNetworkAccessManager(this); m_manager = new QNetworkAccessManager(this);
@ -24,6 +25,15 @@ void DataCollection::stop() {
emit enabledChanged(); emit enabledChanged();
} }
void DataCollection::setImageFormat(ImageFormat format, int quality) {
if (m_format != format) {
m_format = format;
}
if (m_quality != quality) {
m_quality = quality;
}
}
QString DataCollection::path() const { QString DataCollection::path() const {
return m_path; return m_path;
} }
@ -78,13 +88,81 @@ void DataCollection::onCaptureFinished() {
void DataCollection::onDataGetFinished() { void DataCollection::onDataGetFinished() {
auto reply = dynamic_cast<QNetworkReply *>(sender()); auto reply = dynamic_cast<QNetworkReply *>(sender());
auto data = reply->readAll(); auto data = reply->readAll();
LOG(info) << "error: " << reply->error() << ", capture data size: " << data.size(); if (m_format == ImageFormat::Jpeg) {
m_filename.replace("yuv", "jpg");
}
LOG(info) << "filename: " << m_filename.toStdString() << ", error: " << reply->error()
<< ", capture data size: " << data.size();
QFile file(m_path + "/" + m_filename); QFile file(m_path + "/" + m_filename);
file.open(QIODevice::WriteOnly); file.open(QIODevice::WriteOnly);
file.write(data); if (m_format == ImageFormat::Jpeg) {
auto jpegBuffer =
encodeNv21ToJpeg(reinterpret_cast<uint8_t *>(data.data()), ImageWidth, ImageHeight, m_quality);
file.write(reinterpret_cast<const char *>(jpegBuffer.data()), jpegBuffer.size());
} else {
file.write(data);
}
file.close(); file.close();
if (m_enabled) { if (m_enabled) {
QTimer::singleShot(0, this, [this]() { start(); }); QTimer::singleShot(0, this, [this]() { start(); });
} }
} }
static void convertNV21ToYUV420(uint8_t *nv21, uint8_t *yuv420, int width, int height) {
int frameSize = width * height;
int chromaSize = frameSize / 4;
memcpy(yuv420, nv21, frameSize);
uint8_t *u = yuv420 + frameSize;
uint8_t *v = u + chromaSize;
uint8_t *uv = nv21 + frameSize;
for (int i = 0; i < chromaSize; i++) {
u[i] = uv[2 * i + 1];
v[i] = uv[2 * i];
}
}
std::vector<uint8_t> DataCollection::encodeNv21ToJpeg(uint8_t *nv21, int width, int height, int quality) {
tjhandle handle = tjInitCompress();
if (handle == nullptr) {
fprintf(stderr, "Failed to initialize turbojpeg compressor\n");
return {};
}
unsigned long jpeg_size = 0;
uint8_t *jpeg_buffer = nullptr;
int yuv_size = tjBufSizeYUV2(width, 4, height, TJSAMP_420);
uint8_t *yuv_buffer = (uint8_t *)malloc(yuv_size);
if (yuv_buffer == nullptr) {
fprintf(stderr, "Failed to allocate YUV buffer\n");
tjDestroy(handle);
return {};
}
// Convert NV21 to YUV420
convertNV21ToYUV420(nv21, yuv_buffer, width, height);
// Compress YUV to JPEG
int retval = tjCompressFromYUV(handle, yuv_buffer, width, 4, height, TJSAMP_420, &jpeg_buffer, &jpeg_size, quality,
TJFLAG_FASTDCT);
if (retval != 0) {
fprintf(stderr, "Failed to compress to JPEG: %s\n", tjGetErrorStr());
free(yuv_buffer);
tjDestroy(handle);
return {};
}
// Copy the JPEG buffer into a std::vector
std::vector<uint8_t> jpeg_vector(jpeg_buffer, jpeg_buffer + jpeg_size);
// Free the JPEG buffer allocated by libjpeg-turbo
tjFree(jpeg_buffer);
free(yuv_buffer);
tjDestroy(handle);
return jpeg_vector;
}

View File

@ -1,6 +1,7 @@
#ifndef DATACOLLECTION_H #ifndef DATACOLLECTION_H
#define DATACOLLECTION_H #define DATACOLLECTION_H
#include "DataStructure.h"
#include <QObject> #include <QObject>
#include <QQmlEngine> #include <QQmlEngine>
@ -17,6 +18,7 @@ public:
explicit DataCollection(QObject *parent = nullptr); explicit DataCollection(QObject *parent = nullptr);
Q_INVOKABLE void start(const QString &address); Q_INVOKABLE void start(const QString &address);
Q_INVOKABLE void stop(); Q_INVOKABLE void stop();
void setImageFormat(ImageFormat format, int quality);
QString path() const; QString path() const;
void setPath(const QString &path); void setPath(const QString &path);
@ -31,6 +33,7 @@ protected:
void start(); void start();
void onCaptureFinished(); void onCaptureFinished();
void onDataGetFinished(); void onDataGetFinished();
static std::vector<uint8_t> encodeNv21ToJpeg(uint8_t *nv21, int width, int height, int quality);
private: private:
QNetworkAccessManager *m_manager = nullptr; QNetworkAccessManager *m_manager = nullptr;
@ -38,6 +41,9 @@ private:
QString m_address; QString m_address;
QString m_path; QString m_path;
QString m_filename; QString m_filename;
ImageFormat m_format = ImageFormat::Jpeg;
int m_quality = 100;
}; };
#endif // DATACOLLECTION_H #endif // DATACOLLECTION_H

View File

@ -4,6 +4,15 @@
#include <QObject> #include <QObject>
#include <QQmlEngine> #include <QQmlEngine>
constexpr int ImageWidth = 576;
constexpr int ImageHeight = 320;
enum class ImageFormat : int {
Jpeg = 0,
YUV, // nv21 576x320
None, // 新增枚举需要放在这上面
};
std::ostream &operator<<(std::ostream &os, const ImageFormat &format);
struct NetworkInfomation { struct NetworkInfomation {
Q_GADGET Q_GADGET
QML_NAMED_ELEMENT(networkInfomation) QML_NAMED_ELEMENT(networkInfomation)

50
Settings.cpp Normal file
View File

@ -0,0 +1,50 @@
#include "Settings.h"
#include "BoostLog.h"
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <filesystem>
constexpr auto SettingsFilePath = "settings.xml";
Settings::Settings() {
if (!std::filesystem::exists(SettingsFilePath)) {
save();
}
}
void Settings::save() {
boost::property_tree::ptree ptree;
ptree.put("Application.DataCollection.ImageFormat", static_cast<int>(m_imageFormat));
std::ostringstream oss;
for (int i = 0; i < static_cast<int>(ImageFormat::None); i++) {
oss << i << ":" << static_cast<ImageFormat>(i) << " ";
}
ptree.put("Application.DataCollection.ImageFormat.<xmlcomment>", oss.str());
ptree.put("Application.DataCollection.ImageQuality", m_imageQuality);
ptree.put("Application.DataCollection.ImageQuality.<xmlcomment>", "0-100,仅对jpg有效");
boost::property_tree::xml_writer_settings<std::string> settings('\t', 1);
boost::property_tree::write_xml(SettingsFilePath, ptree, std::locale(), settings);
}
void Settings::load() {
boost::property_tree::ptree ptree;
try {
boost::property_tree::read_xml(SettingsFilePath, ptree);
m_imageFormat = static_cast<ImageFormat>(ptree.get<int>("Application.DataCollection.ImageFormat"));
m_imageQuality = ptree.get<int>("Application.DataCollection.ImageQuality");
} catch (...) {
LOG(error) << "parse " << SettingsFilePath << " failed.";
}
LOG(info) << "image format: " << m_imageFormat << ", quality: " << m_imageQuality;
}
ImageFormat Settings::imageFormat() const {
return m_imageFormat;
}
int Settings::imageQuality() const {
return m_imageQuality;
}

24
Settings.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef SETTINGS_H
#define SETTINGS_H
#include "DataStructure.h"
#include "Singleton.h"
class Settings {
friend class Amass::Singleton<Settings>;
public:
void save();
void load();
ImageFormat imageFormat() const;
int imageQuality() const;
protected:
Settings();
private:
ImageFormat m_imageFormat = ImageFormat::Jpeg; // 0: jpg 1: yuv
int m_imageQuality = 100;
};
#endif // SETTINGS_H