实现jpeg编码。
This commit is contained in:
parent
c48c31c316
commit
a865f749be
@ -2,6 +2,7 @@
|
||||
#include "BoostLog.h"
|
||||
#include "Configuration.h"
|
||||
#include "H264Palyer.h"
|
||||
#include "Settings.h"
|
||||
#include "VideoFrameProvider.h"
|
||||
#include <QFileInfo>
|
||||
#include <QFont>
|
||||
@ -9,10 +10,31 @@
|
||||
#include <QQmlApplicationEngine>
|
||||
#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)
|
||||
: m_app(std::make_shared<QGuiApplication>(argc, argv)), m_videoFrameProvider(new VideoFrameProvider()),
|
||||
m_player(std::make_shared<H264Palyer>()), m_devices(new DeviceListModel(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;
|
||||
font.setPointSize(16);
|
||||
font.setFamily("微软雅黑");
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
class QGuiApplication;
|
||||
class VideoFrameProvider;
|
||||
class Settings;
|
||||
class H264Palyer;
|
||||
|
||||
class Application : public QObject {
|
||||
@ -102,6 +103,7 @@ protected:
|
||||
private:
|
||||
std::shared_ptr<QGuiApplication> m_app;
|
||||
VideoFrameProvider *m_videoFrameProvider = nullptr;
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
std::shared_ptr<H264Palyer> m_player;
|
||||
std::weak_ptr<DeviceConnection> m_device;
|
||||
DeviceListModel *m_devices = nullptr;
|
||||
|
@ -52,6 +52,7 @@ add_executable(AntiClipSettings
|
||||
DeviceListModel.h DeviceListModel.cpp
|
||||
H264Palyer.h H264Palyer.cpp
|
||||
VideoFrameProvider.h VideoFrameProvider.cpp
|
||||
Settings.h Settings.cpp
|
||||
)
|
||||
|
||||
if (Qt6_FOUND)
|
||||
@ -92,10 +93,12 @@ add_subdirectory(${Projects_ROOT}/Kylin/Encrypt Encrypt)
|
||||
target_include_directories(AntiClipSettings
|
||||
PRIVATE ${FFmpeg_INCLUDE_DIR}
|
||||
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
|
||||
PRIVATE ${Libraries_ROOT}/libjpeg-turbo64/include
|
||||
)
|
||||
|
||||
target_link_directories(AntiClipSettings
|
||||
PRIVATE ${FFmpeg_LIB_DIR}
|
||||
PRIVATE ${Libraries_ROOT}/libjpeg-turbo64/lib
|
||||
)
|
||||
|
||||
target_link_libraries(AntiClipSettings
|
||||
@ -111,6 +114,7 @@ target_link_libraries(AntiClipSettings
|
||||
PRIVATE avformat
|
||||
PRIVATE Universal
|
||||
PRIVATE Encrypt
|
||||
PRIVATE turbojpeg-static
|
||||
PRIVATE Ws2_32
|
||||
)
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <boost/json/object.hpp>
|
||||
#include <boost/json/parse.hpp>
|
||||
#include <boost/json/serialize.hpp>
|
||||
#include <turbojpeg.h>
|
||||
|
||||
DataCollection::DataCollection(QObject *parent) : QObject{parent} {
|
||||
m_manager = new QNetworkAccessManager(this);
|
||||
@ -24,6 +25,15 @@ void DataCollection::stop() {
|
||||
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 {
|
||||
return m_path;
|
||||
}
|
||||
@ -78,13 +88,81 @@ void DataCollection::onCaptureFinished() {
|
||||
void DataCollection::onDataGetFinished() {
|
||||
auto reply = dynamic_cast<QNetworkReply *>(sender());
|
||||
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);
|
||||
file.open(QIODevice::WriteOnly);
|
||||
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();
|
||||
if (m_enabled) {
|
||||
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;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifndef DATACOLLECTION_H
|
||||
#define DATACOLLECTION_H
|
||||
|
||||
#include "DataStructure.h"
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
@ -17,6 +18,7 @@ public:
|
||||
explicit DataCollection(QObject *parent = nullptr);
|
||||
Q_INVOKABLE void start(const QString &address);
|
||||
Q_INVOKABLE void stop();
|
||||
void setImageFormat(ImageFormat format, int quality);
|
||||
|
||||
QString path() const;
|
||||
void setPath(const QString &path);
|
||||
@ -31,6 +33,7 @@ protected:
|
||||
void start();
|
||||
void onCaptureFinished();
|
||||
void onDataGetFinished();
|
||||
static std::vector<uint8_t> encodeNv21ToJpeg(uint8_t *nv21, int width, int height, int quality);
|
||||
|
||||
private:
|
||||
QNetworkAccessManager *m_manager = nullptr;
|
||||
@ -38,6 +41,9 @@ private:
|
||||
QString m_address;
|
||||
QString m_path;
|
||||
QString m_filename;
|
||||
|
||||
ImageFormat m_format = ImageFormat::Jpeg;
|
||||
int m_quality = 100;
|
||||
};
|
||||
|
||||
#endif // DATACOLLECTION_H
|
||||
|
@ -4,6 +4,15 @@
|
||||
#include <QObject>
|
||||
#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 {
|
||||
Q_GADGET
|
||||
QML_NAMED_ELEMENT(networkInfomation)
|
||||
|
50
Settings.cpp
Normal file
50
Settings.cpp
Normal 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
24
Settings.h
Normal 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
|
Loading…
Reference in New Issue
Block a user