Здравствуйте! Ипользую Вашу платформу
http://starterkit.ru/html/index.php?name=shop&op=view&id=97
Вы предоставляли машину с с билдрутом 2017г, но я решил освежить сборочку.
Использовал как базу образ, найденный у Вас на форуме. ССылка ниже:
http://starterkit.ru/html/index.php?name=forum&op=view&id=29909
В сборке я использую gstreamer1,
ниже приведены установленные пакеты.
\\\\\\\\\\\\\\\\\\\\\\\
--- gst1-imx
[ ] imxeglvivsink
[*] imxg2d
[*] imxipu
[ ] mp3encoder
[ ] imxpxp
[*] uniaudiodec
[*] imxvpu
[*] imxv4l2videosrc
[*] imxv4l2videosink
\\\\\\\\\\\\\\\\\\\\\\\\\\
...............................................................................
[ ] gmrender-resurrect
[ ] gstreamer 0.10
[*] gstreamer 1.x
[*] enable command-line parser
[*] enable tracing subsystem
[*] enable gst-debug trace support
[*] enable plugin registry
[*] install gst-launch & gst-inspect
[ ] gstreamer1-mm
-*- gst1-plugins-base --->
[*] gst1-plugins-good --->
-*- gst1-plugins-bad --->
[*] gst1-plugins-ugly --->
[*] gst1-imx --->
[ ] gst1-interpipe
[*] gst1-libav
[ ] gst1-rtsp-server
[ ] gst1-shark
*** gst1-validate depends on python ***
[ ] gst1-vaapi
*** gst-omx requires a OpenMAX implementation ***
[ ] gstreamer1-editing-services
......................................................................................
Мне необходимо производить конверсию кадров, я хочу произвести это с максимальной скоростью.
Вот ссылка , на gstreamer
https://github.com/Freescale/gstreamer-imx?tab=readme-ov-file
Я написал класс, для обработки кадров, которые передаются через QByteArray (у меня проприетарный протокол, кадры в h264), на gstreamer. Могу предоставить класс который делает эту конверсию. Проблемма в том , что кадры не всегда обрабатываются гстримером (какое-то время в стриме пишет что проблемма в nal заголовке, потом какое-то время - что во временной метке). Эта же функциональность на компьютере работает.
Так же в сборке не нашел
libimxdmabuffer - необходимый для imx плагинов.
Есть еще вариант без использования gstreamer, c прямым использованием libimxdmabuffer и libimxvpuapi.
Но я не нашел примеров использования в интеренете, прошу подсказать где можно посмотреть, или предоставить пример.
Класс для обработки кадров:
---------------------------------------------------------------------
#pragma once
#include <QObject>
#include <QImage>
#include <QByteArray>
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/app/gstappsink.h>
#include <memory>
class H264FrameDecoder : public QObject
{
Q_OBJECT
public:
H264FrameDecoder(QObject *parent = nullptr);
~H264FrameDecoder();
public slots:
void processFrame(const QByteArray &frame, int width, int height, int isIFrame);
void emitFrameProcessed();
signals:
void frameProcessed(const QImage &image);
private:
GstElement *pipeline;
GstElement *appsrc;
GstElement *h264parse;
GstElement *decoder;
GstElement *conv;
GstElement *sink;
GstBus *bus;
GstMessage *msg;
GstStateChangeReturn ret;
QImage m_rgbImage;
std::unique_ptr<uint8_t[]> m_rgb32Data = NULL;
bool initialized;
void initializeGStreamer();
void cleanupGStreamer();
static GstFlowReturn onNewSample(GstAppSink *sink, gpointer user_data);
void i420ToRgb32(const uint8_t *yuvData, int width, int height);
void NV12toRgb32(const uint8_t* nv12Data, int width, int height);
};
-----------------------------------------------------------------------------
#include "H264FrameDecoder.h"
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/app/gstappsink.h>
#include <libyuv.h>
#include <memory>
#include <QDebug>
#include <QCoreApplication>
#include <QProcessEnvironment>
H264FrameDecoder::H264FrameDecoder(QObject *parent)
: QObject(parent), initialized(false) {
initializeGStreamer();
}
H264FrameDecoder::~H264FrameDecoder() {
cleanupGStreamer();
}
void H264FrameDecoder::initializeGStreamer() {
gst_init(nullptr, nullptr);
pipeline = gst_pipeline_new("video-pipeline");
appsrc = gst_element_factory_make("appsrc", "source");
h264parse = gst_element_factory_make("h264parse", "h264-parser");
#ifdef IS_IMX6
decoder = gst_element_factory_make("imxvpudec", "decoder");
conv = gst_element_factory_make("imxg2dvideotransform", "converter");
sink = gst_element_factory_make("imxg2dvideosink", "sink");
#else
decoder = gst_element_factory_make("avdec_h264", "decoder");
conv = gst_element_factory_make("videoconvert", "converter");
sink = gst_element_factory_make("appsink", "sink");
#endif
if (!pipeline || !appsrc || !h264parse || !decoder || !conv || !sink) {
qWarning() << "Not all elements could be created.";
return;
}
gst_bin_add_many(GST_BIN(pipeline), appsrc, h264parse, decoder, conv, sink, nullptr);
if (!gst_element_link_many(appsrc, h264parse, decoder, conv, sink, nullptr)) {
qWarning() << "Elements could not be linked.";
gst_object_unref(pipeline);
return;
}
GstCaps *caps = gst_caps_new_simple("video/x-h264", "stream-format", G_TYPE_STRING, "byte-stream", nullptr);
g_object_set(G_OBJECT(appsrc), "caps", caps, nullptr);
gst_caps_unref(caps);
g_object_set(G_OBJECT(sink), "emit-signals", TRUE, "sync", FALSE, nullptr);
g_object_set(G_OBJECT(sink), "max-buffers", 20, nullptr); // Set max-buffers property
g_signal_connect(sink, "new-sample", G_CALLBACK(onNewSample), this);
ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
qWarning() << "Unable to set the pipeline to the playing state.";
gst_object_unref(pipeline);
return;
}
initialized = true;
}
void H264FrameDecoder::cleanupGStreamer() {
if (initialized) {
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(pipeline);
}
}
void H264FrameDecoder::processFrame(const QByteArray &frame, int width, int height, int frameRate) {
if (!initialized) {
qWarning() << "GStreamer not initialized.";
return;
}
// Set the caps on the appsrc element to specify the width and height
GstCaps *caps = gst_caps_new_simple("video/x-h264",
"width", G_TYPE_INT, width,
"height", G_TYPE_INT, height,
"framerate", GST_TYPE_FRACTION, frameRate, 1,
"stream-format", G_TYPE_STRING, "byte-stream",
nullptr);
g_object_set(G_OBJECT(appsrc), "caps", caps, nullptr);
gst_caps_unref(caps);
// Create a buffer from the frame data
GstBuffer *buffer = gst_buffer_new_and_alloc(frame.size());
GstMapInfo map;
gst_buffer_map(buffer, &map, GST_MAP_WRITE);
memcpy(map.data, frame.constData(), frame.size());
gst_buffer_unmap(buffer, &map);
// Set buffer timestamp and duration
GstClockTime timestamp = gst_util_get_timestamp();
GstClockTime duration = gst_util_uint64_scale_int(1, GST_SECOND, frameRate);
GST_BUFFER_PTS(buffer) = timestamp;
GST_BUFFER_DTS(buffer) = timestamp;
GST_BUFFER_DURATION(buffer) = duration;
// Push the buffer into the pipeline
GstFlowReturn ret;
g_signal_emit_by_name(appsrc, "push-buffer", buffer, &ret);
gst_buffer_unref(buffer);
if (ret != GST_FLOW_OK) {
qWarning() << "Error pushing buffer to GStreamer pipeline.";
}
// Check the pipeline state
/* It have a side effect - stopping running program process in this function */
#if 0
GstState state;
gst_element_get_state(pipeline, &state, nullptr, GST_CLOCK_TIME_NONE);
qDebug() << "Pipeline state:" << state;
#endif
}
void H264FrameDecoder::emitFrameProcessed()
{
emit frameProcessed(m_rgbImage);
}
GstFlowReturn H264FrameDecoder::onNewSample(GstAppSink *sink, gpointer user_data) {
GstSample *sample;
GstBuffer *buffer;
GstMapInfo map;
sample = gst_app_sink_pull_sample(sink);
buffer = gst_sample_get_buffer(sample);
gst_buffer_map(buffer, &map, GST_MAP_READ);
GstCaps *caps = gst_sample_get_caps(sample);
GstStructure *structure = gst_caps_get_structure(caps, 0);
int width, height;
gst_structure_get_int(structure, "width", &width);
gst_structure_get_int(structure, "height", &height);
H264FrameDecoder *processor = static_cast<H264FrameDecoder *>(user_data);
// Ensure the image format is correct
const gchar *format = gst_structure_get_string(structure, "format");
if (format && strcmp(format, "I420") == 0) {
processor->i420ToRgb32(map.data, width, height);
} else if (format && strcmp(format, "NV12") == 0){
processor->NV12toRgb32(map.data, width, height);
} else {
qWarning() << "Unsupported pixel format:" << format;
}
processor->emitFrameProcessed();
gst_buffer_unmap(buffer, &map);
gst_sample_unref(sample);
return GST_FLOW_OK;
}
void H264FrameDecoder::i420ToRgb32(const uint8_t *yuvData, int width, int height) {
// Calculate the sizes of the Y, U, and V planes
int ySize = width * height;
int uvSize = (width * height) / 4;
// Extract the Y, U, and V planes from the I420 data
const uint8_t *yPlane = yuvData;
const uint8_t *uPlane = yuvData + ySize;
const uint8_t *vPlane = yuvData + ySize + uvSize;
// Allocate memory for the RGB32 data
if (!m_rgb32Data.get())
m_rgb32Data = std::make_unique<uint8_t[]>(width * height * 4);
// Convert I420 to ARGB using libyuv
libyuv::I420ToARGB(yPlane, width,
uPlane, width / 2,
vPlane, width / 2,
m_rgb32Data.get(), width * 4,
width, height);
QImage tmpImg(m_rgb32Data.get(), width, height, QImage::Format_RGB32);
// Create a QImage from the RGB32 data
m_rgbImage.swap(tmpImg);
}
void H264FrameDecoder::NV12toRgb32(const uint8_t *nv12Data, int width, int height) {
int ySize = width * height;
int uvSize = (width * height) / 2;
// Extract the Y and UV planes from the NV12 data
const uint8_t* yPlane = nv12Data;
const uint8_t* uvPlane = nv12Data + ySize;
// Allocate memory for the RGB data if not already allocated
if (!m_rgb32Data) {
m_rgb32Data = std::make_unique<uint8_t[]>(width * height * 4); // ARGB format requires 4 bytes per pixel
}
// Convert NV12 to ARGB using libyuv
libyuv::NV12ToARGB(yPlane, width,
uvPlane, width,
m_rgb32Data.get(), width * 4, // ARGB format requires 4 bytes per pixel
width, height);
// Create a QImage from the ARGB data
QImage rgbImage(m_rgb32Data.get(), width, height, QImage::Format_ARGB32);
// Ensure the QImage does not take ownership of the raw data pointer
// Create a deep copy of the QImage to ensure the data is not deleted prematurely
m_rgbImage = rgbImage.copy();
}
----------------------------------------------------------------------------
Прошу подсказать, что я делаю неправильно.