29 #include <libavcodec/avcodec.h>
30 #include <libavutil/opt.h>
31 #include <libavutil/imgutils.h>
32 #include <libavformat/avformat.h>
33 #include <libswscale/swscale.h>
43 "yarp.carrier.portmonitor.image_compression_ffmpeg",
58void split(
const std::string &s,
char delim, std::vector<std::string> &elements) {
59 std::istringstream iss(s);
61 while (std::getline(iss, item, delim)) {
62 elements.push_back(item);
72 AVCodecID codecId = AV_CODEC_ID_MPEG2VIDEO;
83 codec = avcodec_find_encoder(codecId);
85 codec = avcodec_find_decoder(codecId);
96 yCError(FFMPEGMONITOR,
"Codec context is already allocated");
100 yCError(FFMPEGMONITOR,
"Could not allocate video codec context");
131 yCTrace(FFMPEGMONITOR,
"setparam");
137 yCTrace(FFMPEGMONITOR,
"getparam");
145 yCTrace(FFMPEGMONITOR,
"accept - sender");
150 yCError(FFMPEGMONITOR,
"Expected type Image in sender side, but got wrong data type!");
156 yCTrace(FFMPEGMONITOR,
"accept - receiver");
161 yCError(FFMPEGMONITOR,
"Expected type Bottle in receiver side, but got wrong data type!");
172 yCTrace(FFMPEGMONITOR,
"update - sender");
176 AVPacket *packet = av_packet_alloc();
177 if (packet == NULL) {
178 yCError(FFMPEGMONITOR,
"Error in packet allocation");
183 if (success &&
compress(img, packet) != 0) {
184 yCError(FFMPEGMONITOR,
"Error in compression");
191 int successCode = success ? 1 : 0;
200 Value p(packet,
sizeof(*packet));
203 Value d(packet->data, packet->size);
208 Value bd(packet->buf->data, packet->buf->size);
212 for (
int i = 0; i < packet->side_data_elems; i++) {
215 Value sd(packet->side_data[i].data, packet->side_data[i].size);
221 av_packet_unref(packet);
225 yCTrace(FFMPEGMONITOR,
"update - receiver");
231 int width = compressedBottle->
get(1).
asInt32();
232 int height = compressedBottle->
get(2).
asInt32();
233 int pixelCode = compressedBottle->
get(3).
asInt32();
234 int pixelSize = compressedBottle->
get(4).
asInt32();
241 if (compressedBottle->
get(0).
asInt32() == 1) {
244 AVPacket* tmp = (AVPacket*) compressedBottle->
get(5).
asBlob();
246 AVPacket* packet = av_packet_alloc();
248 packet->dts = tmp->dts;
249 packet->duration = tmp->duration;
250 packet->flags = tmp->flags;
251 packet->pos = tmp->pos;
252 packet->pts = tmp->pts;
253 packet->side_data_elems = 0;
254 packet->stream_index = tmp->stream_index;
255 packet->size = tmp->size;
257 packet->data = (uint8_t *) compressedBottle->
get(6).
asBlob();
259 packet->buf = av_buffer_create((uint8_t *) compressedBottle->
get(8).
asBlob(),
260 compressedBottle->
get(7).
asInt32(), av_buffer_default_free,
261 nullptr, AV_BUFFER_FLAG_READONLY);
264 for (
int i = 0; i < tmp->side_data_elems; i++) {
266 int ret = av_packet_add_side_data(packet,
267 (AVPacketSideDataType) compressedBottle->
get(10).
asInt32(),
268 (uint8_t *) compressedBottle->
get(11).
asBlob(),
277 if (success &&
decompress(packet, width, height, pixelCode) != 0) {
278 yCError(FFMPEGMONITOR,
"Error in decompression");
283 av_freep(&packet->side_data);
295 yCTrace(FFMPEGMONITOR,
"compress");
300 int w = img->
width();
304 startFrame = av_frame_alloc();
305 if (startFrame == NULL) {
306 yCError(FFMPEGMONITOR,
"Cannot allocate starting frame!");
311 endFrame = av_frame_alloc();
312 if (endFrame == NULL) {
313 yCError(FFMPEGMONITOR,
"Cannot allocate end frame!");
314 av_frame_free(&startFrame);
319 int success = av_image_alloc(startFrame->data, startFrame->linesize,
324 yCError(FFMPEGMONITOR,
"Cannot allocate starting frame buffer!");
325 av_frame_free(&startFrame);
326 av_frame_free(&endFrame);
333 av_freep(&startFrame->data[0]);
335 startFrame->height = h;
336 startFrame->width = w;
340 success = av_image_alloc(endFrame->data, endFrame->linesize,
345 yCError(FFMPEGMONITOR,
"Cannot allocate end frame buffer!");
346 av_frame_free(&startFrame);
347 av_frame_free(&endFrame);
352 endFrame->height = h;
357 static struct SwsContext *img_convert_ctx;
360 img_convert_ctx = sws_getContext(w, h,
366 if (img_convert_ctx == NULL) {
367 yCError(FFMPEGMONITOR,
"Cannot initialize pixel format conversion context!");
368 av_freep(&endFrame->data[0]);
369 av_frame_free(&startFrame);
370 av_frame_free(&endFrame);
375 int ret = sws_scale(img_convert_ctx, startFrame->data, startFrame->linesize, 0,
376 h, endFrame->data, endFrame->linesize);
379 yCError(FFMPEGMONITOR,
"Could not convert pixel format!");
380 sws_freeContext(img_convert_ctx);
381 av_freep(&endFrame->data[0]);
382 av_frame_free(&startFrame);
383 av_frame_free(&endFrame);
398 yCError(FFMPEGMONITOR,
"Could not open codec");
399 sws_freeContext(img_convert_ctx);
400 av_freep(&endFrame->data[0]);
401 av_frame_free(&startFrame);
402 av_frame_free(&endFrame);
414 yCError(FFMPEGMONITOR,
"Error sending a frame for encoding");
415 sws_freeContext(img_convert_ctx);
416 av_freep(&endFrame->data[0]);
417 av_frame_free(&startFrame);
418 av_frame_free(&endFrame);
424 sws_freeContext(img_convert_ctx);
425 av_freep(&endFrame->data[0]);
426 av_frame_free(&startFrame);
427 av_frame_free(&endFrame);
429 if (
ret == AVERROR(EAGAIN)) {
431 yCError(FFMPEGMONITOR,
"Error EAGAIN");
433 }
else if (
ret == AVERROR_EOF) {
435 yCError(FFMPEGMONITOR,
"Error EOF");
437 }
else if (
ret < 0) {
438 yCError(FFMPEGMONITOR,
"Error during encoding");
447 yCTrace(FFMPEGMONITOR,
"decompress");
462 yCError(FFMPEGMONITOR,
"Could not open codec");
469 startFrame = av_frame_alloc();
470 if (startFrame == NULL) {
471 yCError(FFMPEGMONITOR,
"Could not allocate start frame!");
478 yCError(FFMPEGMONITOR,
"Error sending a frame for encoding");
479 av_frame_free(&startFrame);
485 if (
ret == AVERROR(EAGAIN)) {
487 yCError(FFMPEGMONITOR,
"Error EAGAIN");
488 av_frame_free(&startFrame);
491 else if (
ret == AVERROR_EOF) {
493 yCError(FFMPEGMONITOR,
"Error EOF");
494 av_frame_free(&startFrame);
498 yCError(FFMPEGMONITOR,
"Error during encoding");
499 av_frame_free(&startFrame);
504 endFrame = av_frame_alloc();
505 if (endFrame == NULL) {
506 yCError(FFMPEGMONITOR,
"Could not allocate start frame!");
507 av_frame_free(&startFrame);
512 int success = av_image_alloc(endFrame->data, endFrame->linesize,
517 yCError(FFMPEGMONITOR,
"Error allocating end frame buffer!");
518 av_frame_free(&startFrame);
519 av_frame_free(&endFrame);
524 endFrame->height = h;
529 static struct SwsContext *img_convert_ctx;
532 img_convert_ctx = sws_getContext(w, h,
538 if (img_convert_ctx == NULL) {
539 yCError(FFMPEGMONITOR,
"Cannot initialize the pixel format conversion context!");
540 av_freep(&endFrame->data[0]);
541 av_frame_free(&endFrame);
542 av_frame_free(&startFrame);
547 ret = sws_scale(img_convert_ctx, startFrame->data, startFrame->linesize, 0,
548 h, endFrame->data, endFrame->linesize);
551 yCError(FFMPEGMONITOR,
"Could not convert pixel format!");
552 av_freep(&endFrame->data[0]);
553 av_frame_free(&endFrame);
554 av_frame_free(&startFrame);
555 sws_freeContext(img_convert_ctx);
563 av_freep(&endFrame->data[0]);
564 av_frame_free(&endFrame);
565 av_frame_free(&startFrame);
566 sws_freeContext(img_convert_ctx);
574 std::vector<std::string> parameters;
576 split(carrierString,
'+', parameters);
579 for (std::string param: parameters) {
587 auto pointPosition = param.find(
'.');
588 if (pointPosition == std::string::npos) {
589 yCError(FFMPEGMONITOR,
"Error parsing parameters!");
594 std::string paramKey = param.substr(0, pointPosition);
595 std::string paramValue = param.substr(pointPosition + 1, param.length());
614 yCError(FFMPEGMONITOR,
"Unrecognized codec: %s", paramValue.c_str());
623 paramsMap.insert( std::pair<std::string, std::string>(paramKey, paramValue) );
635 std::string key = x.first;
636 std::string value = x.second;
639 int globalError = av_opt_set(
codecContext, key.c_str(), value.c_str(), 0);
641 int privError = av_opt_set(
codecContext->priv_data, key.c_str(), value.c_str(), 0);
644 if (globalError == AVERROR(ERANGE) || privError == AVERROR(ERANGE)) {
645 yCError(FFMPEGMONITOR,
"Parameter out of range: %s", key.c_str());
649 else if (globalError == AVERROR(EINVAL) || privError == AVERROR(EINVAL)) {
650 yCError(FFMPEGMONITOR,
"Invalid value for parameter: %s", key.c_str());
654 else if (
senderSide && globalError == AVERROR_OPTION_NOT_FOUND && privError == AVERROR_OPTION_NOT_FOUND) {
655 yCError(FFMPEGMONITOR,
"Parameter not found: %s", key.c_str());
int getParamsFromCommandLine(std::string carrierString, AVCodecID &codecId)
This function parses the command line parameters from a string containing the entire command used to ...
yarp::sig::FlexImage imageOut
The final decompressed image that will be sent to the original destination.
int compress(yarp::sig::Image *img, AVPacket *pkt)
This function performs all the compression actions on the incoming Image and saves the resulting comp...
bool accept(yarp::os::Things &thing) override
This function is used by the port monitor to decide if an incoming packet can be accepted (it tries t...
int setCommandLineParams()
This function iterates over the attribute paramsMap and sets all the specified parameters into the at...
bool firstTime
Boolean variable used to check if the current call to the "compression" (or "decompression") function...
std::string codecName
The string containing codec name.
yarp::os::Things & update(yarp::os::Things &thing) override
This function is the one that manipulates the incoming packet.
yarp::os::Bottle data
The bottle that is filled with compressed image and all the information needed for decompression (it ...
const AVCodec * codec
Ffmpeg structure containing all codec information needed for compression / decompression.
yarp::os::Things th
The object returned by the "update" function; it can be a yarp::os::Bottle (sender side) or a yarp::s...
bool senderSide
Boolean variable that tells if the current execution is in sender side or not.
bool create(const yarp::os::Property &options) override
This function is called when the object is created and it is used to initialize all its attributes.
bool setparam(const yarp::os::Property ¶ms) override
This will be called when the portmonitor carrier parameters are set via YARP admin port.
bool getparam(yarp::os::Property ¶ms) override
This will be called when the portmonitor carrier parameters are requested via YARP admin port.
int decompress(AVPacket *pkt, int w, int h, int pixelCode)
This function decompresses the incoming AVPacket passed as parameter and saves decompressed data into...
std::map< std::string, std::string > paramsMap
Structure that maps every parameter inserted from command line into its value (both as strings).
void destroy(void) override
This function is called when the execution is terminated and the object is destroyed.
AVCodecContext * codecContext
Ffmpeg structure containing all codec context information needed for compression / decompression.
A simple collection of objects that can be described and transmitted in a portable way.
void add(const Value &value)
Add a Value to the bottle, at the end of the list.
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
void clear()
Empties the bottle of any objects it contains.
void addInt32(std::int32_t x)
Places a 32-bit integer in the bottle, at the end of the list.
static LogCallback printCallback()
Get current print callback.
static LogType minimumPrintLevel()
Get current minimum print level.
A class for storing options and configuration information.
Value & find(const std::string &key) const override
Gets a value corresponding to a given keyword.
Base class for generic things.
void setPortWriter(yarp::os::PortWriter *writer)
Set the reference to a PortWriter object.
A single value (typically within a Bottle).
virtual bool asBool() const
Get boolean value.
virtual std::int32_t asInt32() const
Get 32-bit integer value.
virtual const char * asBlob() const
Get binary data value.
virtual std::string asString() const
Get string value.
void setPixelCode(int imgPixelCode)
void setPixelSize(size_t imgPixelSize)
Base class for storing images.
size_t width() const
Gets width of image in pixels.
size_t getRowSize() const
Size of the underlying image buffer rows.
unsigned char * getRawImage() const
Access to the internal image buffer.
virtual size_t getPixelSize() const
Gets pixel size in memory in bytes.
void resize(size_t imgWidth, size_t imgHeight)
Reallocate an image to be of a desired size, throwing away its current contents.
void zero()
Set all pixels to 0.
size_t height() const
Gets height of image in pixels.
virtual int getPixelCode() const
Gets pixel type identifier.
File containing constans used in FfmpegPortmonitor.cpp.
static const std::vector< std::string > FFMPEGPORTMONITOR_IGNORE_PARAMS
This vector contains all parameters that have to be ignored while parsing command line string.
static const std::string FFMPEGPORTMONITOR_CL_CODEC_KEY
This string is the "key" value for the codec parameter.
static std::map< int, int > FFMPEGPORTMONITOR_CODECPIXELMAP
This structure maps Ffmpeg video codecs with their needed Ffmpeg pixel format code.
static const std::vector< std::string > FFMPEGPORTMONITOR_CL_CODECS
This vector contains the only accepted values for the command line parameter "codec".
static std::map< int, int > FFMPEGPORTMONITOR_PIXELMAP
This structure maps YARP pixel format codec into Ffmpeg pixel format codes.
static const std::vector< int > FFMPEGPORTMONITOR_CODE_CODECS
This vector contains the codec ids corresponding to the codecs of the FFMPEGPORTMONITOR_CL_CODECS vec...
void split(const std::string &s, char delim, std::vector< std::string > &elements)
This function simply splits a string into a vector of strings basing on a delimiter character.
Header file of FfmpegPortmonitor: a port monitor for video compression/decompression.
#define yCError(component,...)
#define yCTrace(component,...)
#define YARP_LOG_COMPONENT(name,...)
An interface to the operating system, including Port based communication.