Skip to content

Commit

Permalink
add h264 hw encoder
Browse files Browse the repository at this point in the history
  • Loading branch information
[email protected] committed Feb 15, 2021
1 parent 9991bff commit a6ddbfc
Show file tree
Hide file tree
Showing 4 changed files with 265 additions and 6 deletions.
12 changes: 9 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ add_subdirectory(zlmediakit)

set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -latomic")

set(RPI_VC_DIR "/opt/vc")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSTANDALONE -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_LINUX -fPIC -DPIC -D_REENTRANT -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -U_FORTIFY_SOURCE -Wall -g -DHAVE_LIBOPENMAX=2 -DOMX -DOMX_SKIP64BIT -ftree-vectorize -pipe -DUSE_EXTERNAL_OMX -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM -Wno-psabi")
link_directories(${RPI_VC_DIR}/lib)

#set header
INCLUDE_DIRECTORIES(zlmediakit/3rdpart/ZLToolKit/src)
Expand All @@ -14,7 +17,10 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/3rdpart)
INCLUDE_DIRECTORIES(src)
INCLUDE_DIRECTORIES(src/v4l2/inc)

file(GLOB_RECURSE server_src src/*.cpp src/*.c)
INCLUDE_DIRECTORIES(${RPI_VC_DIR}/include)
INCLUDE_DIRECTORIES(${RPI_VC_DIR}/src/hello_pi/libs/ilclient)

add_executable(rpi-hw-mediaserver main.cpp ${server_src})
target_link_libraries(rpi-hw-mediaserver zlmediakit zltoolkit ssl crypto mpeg mov flv pthread)
file(GLOB_RECURSE server_src src/*.cpp src/*.c ${RPI_VC_DIR}/src/hello_pi/libs/ilclient/*.c)

add_executable(rpi-hw-mediaserver main.cpp ${server_src} src/VideoEncoder.cpp src/VideoEncoder.h)
target_link_libraries(rpi-hw-mediaserver zlmediakit zltoolkit ssl crypto mpeg mov flv pthread openmaxil bcm_host vcos vchiq_arm rt m)
33 changes: 30 additions & 3 deletions main.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,43 @@
//
// Created by wind on 2021/2/9.
//
#include <signal.h>
#include <csignal>
#include "MediaServer.h"

#include "V4l2Capture.h"

#include "VideoEncoder.h"

int main(){
//set logger
Logger::Instance().add(std::make_shared<ConsoleChannel>("ConsoleChannel", LTrace));

//start media server
MediaServer::Ptr server = make_shared<MediaServer>();
server->start();
// MediaServer::Ptr server = make_shared<MediaServer>();
// server->start();

int width = 1920,height = 1080;

V4L2DeviceParameters param("/dev/video0", V4L2_PIX_FMT_YUV420, width, height, 25, 0);
V4l2Capture* videoCapture = V4l2Capture::create(param, V4l2Access::IOTYPE_MMAP);


ofstream f("aaa.h264",iostream::binary);
VideoEncoder venc(width,height,1000000);
venc.init();
venc.setOnEncodedFrameCallback([&f](uint8_t *outBuffer,size_t bufferLen){
f.write(reinterpret_cast<const char *>(outBuffer), bufferLen);
f.flush();
});
int size = width*height*3/2;
char *buff = new char[size];
for (int i = 0; i < 1000; ++i) {
timeval timeout = {0,4000};
if(videoCapture->isReadable(&timeout) == 1){
videoCapture->read(buff,size);
venc.encodeFrame(reinterpret_cast<uint8_t *>(buff), size);
}
}

//set interrupt signal
static semaphore sem;
Expand Down
181 changes: 181 additions & 0 deletions src/VideoEncoder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
//
// Created by admin on 2021/2/14.
//

#include "VideoEncoder.h"
#include "Util/logger.h"

using namespace toolkit;
bool VideoEncoder::init() {
bcm_host_init();

_client = shared_ptr<ILCLIENT_T>(ilclient_init(),&ilclient_destroy);

if (!_client) {
ErrorL << "init client error.";
return false;
}

if (OMX_Init() != OMX_ErrorNone) {
return false;
}

OMX_ERRORTYPE error;
COMPONENT_T * pEnc;
// create video_encode
error = static_cast<OMX_ERRORTYPE>(ilclient_create_component(_client.get(), &pEnc, "video_encode",
static_cast<ILCLIENT_CREATE_FLAGS_T>(
ILCLIENT_DISABLE_ALL_PORTS |
ILCLIENT_ENABLE_INPUT_BUFFERS |
ILCLIENT_ENABLE_OUTPUT_BUFFERS)));
_video_encode = shared_ptr<COMPONENT_T>(pEnc,[](COMPONENT_T * comp){
DebugL << "disabling port buffers for 200 and 201...";
ilclient_disable_port_buffers(comp, 200, nullptr, nullptr, nullptr);
ilclient_disable_port_buffers(comp, 201, nullptr, nullptr, nullptr);


COMPONENT_T *list[5];
list[0] = comp;
ilclient_state_transition(list, OMX_StateIdle);
ilclient_state_transition(list, OMX_StateLoaded);

ilclient_cleanup_components(list);

OMX_Deinit();
});
if (error != 0) {
ErrorL << "ilclient_create_component() for video_encode failed with "<<error;
}


// get current settings of video_encode component from port 200
memset(&_def, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
_def.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
_def.nVersion.nVersion = OMX_VERSION;
_def.nPortIndex = 200;

if (OMX_GetParameter
(ILC_GET_HANDLE(_video_encode.get()), OMX_IndexParamPortDefinition,
&_def) != OMX_ErrorNone) {
ErrorL<< " OMX_GetParameter() for video_encode port 200 failed!";
return false;
}


// Port 200: in 1/1 115200 16 enabled,not pop.,not cont. 320x240 320x240 @1966080 20
_def.format.video.nFrameWidth = _width;
_def.format.video.nFrameHeight = _height;
_def.format.video.xFramerate = 30 << 16;
_def.format.video.nSliceHeight = ALIGN_UP(_def.format.video.nFrameHeight, 16);
_def.format.video.nStride = _def.format.video.nFrameWidth;
_def.format.video.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;

error = OMX_SetParameter(ILC_GET_HANDLE(_video_encode.get()),
OMX_IndexParamPortDefinition, &_def);
if (error != OMX_ErrorNone) {
ErrorL<< " OMX_SetParameter() for video_encode port 200 failed with "<< error <<"!\n";
return false;
}

memset(&_format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE));
_format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE);
_format.nVersion.nVersion = OMX_VERSION;
_format.nPortIndex = 201;
_format.eCompressionFormat = OMX_VIDEO_CodingAVC;

InfoL<<"OMX_SetParameter for video_encode:201...";
error = OMX_SetParameter(ILC_GET_HANDLE(_video_encode.get()),
OMX_IndexParamVideoPortFormat, &_format);
if (error != OMX_ErrorNone) {
ErrorL<< " OMX_SetParameter() for video_encode port 201 failed with "<< error <<"!";
return false;
}

OMX_VIDEO_PARAM_BITRATETYPE bitrateType;
// set current bitrate to 1Mbit
memset(&bitrateType, 0, sizeof(OMX_VIDEO_PARAM_BITRATETYPE));
bitrateType.nSize = sizeof(OMX_VIDEO_PARAM_BITRATETYPE);
bitrateType.nVersion.nVersion = OMX_VERSION;
bitrateType.eControlRate = OMX_Video_ControlRateVariable;
bitrateType.nTargetBitrate = _bitRate;
bitrateType.nPortIndex = 201;
error = OMX_SetParameter(ILC_GET_HANDLE(_video_encode.get()),
OMX_IndexParamVideoBitrate, &bitrateType);
if (error != OMX_ErrorNone) {
ErrorL<< " OMX_SetParameter() for bitrate for video_encode port 201 failed with "<< error <<"!";
return false;
}


// get current bitrate
memset(&bitrateType, 0, sizeof(OMX_VIDEO_PARAM_BITRATETYPE));
bitrateType.nSize = sizeof(OMX_VIDEO_PARAM_BITRATETYPE);
bitrateType.nVersion.nVersion = OMX_VERSION;
bitrateType.nPortIndex = 201;

if (OMX_GetParameter
(ILC_GET_HANDLE(_video_encode.get()), OMX_IndexParamVideoBitrate,
&bitrateType) != OMX_ErrorNone) {
ErrorL<< " OMX_GetParameter() for video_encode for bitrate port 201 failed!";
return false;
}
InfoL << "Current Bitrate=" << bitrateType.nTargetBitrate;


InfoL<<"encode to idle...";
if (ilclient_change_component_state(_video_encode.get(), OMX_StateIdle) == -1) {
ErrorL<< " ilclient_change_component_state(video_encode, OMX_StateIdle) failed";
}

InfoL << "enabling port buffers for 200...";
if (ilclient_enable_port_buffers(_video_encode.get(), 200, nullptr, nullptr, nullptr) != 0) {
ErrorL<< " enabling port buffers for 200 failed!";
return false;
}

InfoL << "enabling port buffers for 201...";
if (ilclient_enable_port_buffers(_video_encode.get(), 201, nullptr, nullptr, nullptr) != 0) {
ErrorL<<"enabling port buffers for 201 failed!";
return false;
}

InfoL<<"encode to executing...";
ilclient_change_component_state(_video_encode.get(), OMX_StateExecuting);

return true;
}

void VideoEncoder::encodeFrame(uint8_t * yuvBuffer,size_t bufferLen){
OMX_BUFFERHEADERTYPE *buf;
OMX_BUFFERHEADERTYPE *out;
buf = ilclient_get_input_buffer(_video_encode.get(), 200, 1);
if (buf == nullptr) {
ErrorL<< "Doh, no buffers for me!";
} else {
/* fill it */
buf->nFilledLen = bufferLen;
memmove(buf->pBuffer, yuvBuffer, buf->nFilledLen);

if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(_video_encode.get()), buf) !=
OMX_ErrorNone) {
ErrorL<<"Error emptying buffer!";
}

out = ilclient_get_output_buffer(_video_encode.get(), 201, 1);

if (out != nullptr) {
if (_onEncodedFrameCallback&&out->nFilledLen){
_onEncodedFrameCallback(out->pBuffer, out->nFilledLen);
}
out->nFilledLen = 0;
} else {
WarnL<<"Not getting it!";
}

OMX_ERRORTYPE error = OMX_FillThisBuffer(ILC_GET_HANDLE(_video_encode.get()), out);
if (error != OMX_ErrorNone) {
ErrorL<<"Error sending buffer for filling: " << error;
}
}
}

45 changes: 45 additions & 0 deletions src/VideoEncoder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// Created by admin on 2021/2/14.
//

#ifndef RPI_HW_MEDIASERVER_VIDEOENCODER_H
#define RPI_HW_MEDIASERVER_VIDEOENCODER_H

extern "C"{
#include <ilclient.h>
#include <bcm_host.h>
};
#include <memory>
#include <functional>
#include <utility>

using namespace std;
class VideoEncoder {

private:
OMX_VIDEO_PARAM_PORTFORMATTYPE _format{};
OMX_PARAM_PORTDEFINITIONTYPE _def{};
shared_ptr<COMPONENT_T> _video_encode;
shared_ptr<ILCLIENT_T> _client;
int _status = 0;
int _framenumber = 0;
int _width = 0;
int _height = 0;
ulong _bitRate = 0;
function<void(uint8_t *outBuffer,size_t bufferLen)> _onEncodedFrameCallback = nullptr;

public:
VideoEncoder(int width,int height,ulong bitRate){
_width = width;
_height = height;
_bitRate = bitRate;
}
bool init();
void encodeFrame(uint8_t * yuvBuffer,size_t bufferLen);
void setOnEncodedFrameCallback(function<void(uint8_t *outBuffer,size_t bufferLen)> cb){
_onEncodedFrameCallback = std::move(cb);
}
};


#endif //RPI_HW_MEDIASERVER_VIDEOENCODER_H

0 comments on commit a6ddbfc

Please sign in to comment.