<commit_before>/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2020, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

#if defined(__GNUC__)  // Needed for ffmpeg headers. Only allowed here when not
// using precomp. headers
#define __STDC_CONSTANT_MACROS	// Needed for having "UINT64_C" and so
#endif

#include <mrpt/config.h>

#include "hwdrivers-precomp.h"	// Precompiled headers

#if MRPT_HAS_FFMPEG
extern "C"
{
#define _MSC_STDINT_H_	// We already have pstdint.h in MRPT
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
#endif

#include <mrpt/hwdrivers/CFFMPEG_InputStream.h>

using namespace mrpt;
using namespace mrpt::hwdrivers;

// JLBC: This file takes portions of code from the example
// "avcodec_sample.0.4.9.cpp"
//
// Minimum ffmpeg libs versions we want to support:
// Ubuntu 16.04 LTS: avcodec 56.60.100, avutil 54.31.100, avformat 56.40.101
// Ubuntu 20.04 LTS: avcodec 58.54.100, avutil 56.31.100, avformat 58.29.100
//
#if MRPT_HAS_FFMPEG
namespace mrpt::hwdrivers
{
// All context for ffmpeg:
struct TFFMPEGContext
{
	AVFormatContext* pFormatCtx{nullptr};
	int videoStream{0};
#if LIBAVFORMAT_VERSION_MAJOR >= 58
	AVCodecParameters* pCodecPars{nullptr};
#endif
	AVCodec* pCodec{nullptr};
	AVCodecContext* pCodecCtx{nullptr};
	AVFrame* pFrame{nullptr};
	AVFrame* pFrameRGB{nullptr};
	SwsContext* img_convert_ctx{nullptr};
	std::vector<uint8_t> buffer;
};
}  // namespace mrpt::hwdrivers
#endif

struct CFFMPEG_InputStream::Impl
{
#if MRPT_HAS_FFMPEG
	TFFMPEGContext m_state;
#endif
};

/* --------------------------------------------------------
					Ctor
   -------------------------------------------------------- */
CFFMPEG_InputStream::CFFMPEG_InputStream()
#if MRPT_HAS_FFMPEG
	: m_impl(mrpt::make_impl<CFFMPEG_InputStream::Impl>())
{
// av_register_all() not needed in ffmpeg >=4.0
#if LIBAVFORMAT_VERSION_MAJOR < 58
	// Register all formats and codecs
	av_register_all();
#endif
}
#else
{
	THROW_EXCEPTION("MRPT has been compiled without FFMPEG libraries.");
}
#endif

/* --------------------------------------------------------
					Dtor
   -------------------------------------------------------- */
CFFMPEG_InputStream::~CFFMPEG_InputStream()
{
#if MRPT_HAS_FFMPEG
	// Close everything:
	this->close();
#endif
}

/* --------------------------------------------------------
					isOpen
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::isOpen() const
{
#if MRPT_HAS_FFMPEG
	const TFFMPEGContext* ctx = &m_impl->m_state;
	return ctx->pFormatCtx != nullptr;
#else
	return false;
#endif
}

/* --------------------------------------------------------
					openURL
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::openURL(
	const std::string& url, bool grab_as_grayscale, bool verbose)
{
#if MRPT_HAS_FFMPEG
	this->close();	// Close first

	TFFMPEGContext* ctx = &m_impl->m_state;

	this->m_url = url;
	this->m_grab_as_grayscale = grab_as_grayscale;

	// Open video file
	if (avformat_open_input(&ctx->pFormatCtx, url.c_str(), nullptr, nullptr) !=
		0)
	{
		ctx->pFormatCtx = nullptr;
		std::cerr << "[CFFMPEG_InputStream::openURL] Cannot open video: " << url
				  << std::endl;
		return false;
	}

	// Retrieve stream information
	if (avformat_find_stream_info(ctx->pFormatCtx, nullptr) < 0)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Couldn't find stream "
					 "information: "
				  << url << std::endl;
		return false;
	}

	// Dump information about file onto standard error
	if (verbose)
	{
		av_dump_format(ctx->pFormatCtx, 0, url.c_str(), false);
	}

	// Find the first video stream
	ctx->videoStream = -1;
	for (unsigned int i = 0; i < ctx->pFormatCtx->nb_streams; i++)
	{
#if LIBAVFORMAT_VERSION_MAJOR >= 58
		auto codecType = ctx->pFormatCtx->streams[i]->codecpar->codec_type;
#else
		auto codecType = ctx->pFormatCtx->streams[i]->codec->codec_type;
#endif
		if (codecType == AVMEDIA_TYPE_VIDEO)
		{
			ctx->videoStream = (int)i;
			break;
		}
	}
	if (ctx->videoStream == -1)
	{
		std::cerr
			<< "[CFFMPEG_InputStream::openURL] Didn't find a video stream: "
			<< url << std::endl;
		return false;
	}

	// Get a pointer to the codec context for the video stream
#if LIBAVFORMAT_VERSION_MAJOR >= 58
	ctx->pCodecPars = ctx->pFormatCtx->streams[ctx->videoStream]->codecpar;
	// Find the decoder for the video stream
	ctx->pCodec = avcodec_find_decoder(ctx->pCodecPars->codec_id);
#else
	ctx->pCodecCtx = ctx->pFormatCtx->streams[ctx->videoStream]->codec;
	// Find the decoder for the video stream
	ctx->pCodec = avcodec_find_decoder(ctx->pCodecCtx->codec_id);
#endif
	if (ctx->pCodec == nullptr)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Codec not found: " << url
				  << std::endl;
		return false;
	}

#if LIBAVFORMAT_VERSION_MAJOR >= 58
	ctx->pCodecCtx = avcodec_alloc_context3(nullptr /*ctx->pCodec*/);
	if (!ctx->pCodecCtx)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Cannot alloc avcodec "
					 "context for: "
				  << url << std::endl;
		return false;
	}

	// Add stream parameters to context
	if (avcodec_parameters_to_context(
			ctx->pCodecCtx,
			ctx->pFormatCtx->streams[ctx->videoStream]->codecpar))
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Failed "
					 "avcodec_parameters_to_context() for: "
				  << url << std::endl;
		return false;
	}

	// Make sure that Codecs are identical or  avcodec_open2 fails.
	ctx->pCodecCtx->codec_id = ctx->pCodec->id;
#endif

	// Open codec
	if (avcodec_open2(ctx->pCodecCtx, ctx->pCodec, nullptr) < 0)
	{
		std::cerr
			<< "[CFFMPEG_InputStream::openURL] avcodec_open2() failed for: "
			<< url << std::endl;
		return false;
	}

	// Allocate video frame
	ctx->pFrame = av_frame_alloc();
	// Allocate an AVFrame structure
	ctx->pFrameRGB = av_frame_alloc();

	if (ctx->pFrameRGB == nullptr || ctx->pFrame == nullptr)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Could not alloc memory "
					 "for frame buffers: "
				  << url << std::endl;
		return false;
	}

	// Determine required buffer size and allocate buffer
#if LIBAVFORMAT_VERSION_MAJOR >= 58
	const auto width = ctx->pCodecPars->width, height = ctx->pCodecPars->height;
#else
	const auto width = ctx->pCodecCtx->width, height = ctx->pCodecCtx->height;
#endif
	int numBytes = av_image_get_buffer_size(
		m_grab_as_grayscale ? AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24, width,
		height, 1);
	if (numBytes < 0)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] av_image_get_buffer_size "
					 "error code: "
				  << numBytes << std::endl;
		return false;
	}

	ctx->buffer.resize(numBytes);

	// Assign appropriate parts of buffer to image planes in pFrameRGB

	av_image_fill_arrays(
		ctx->pFrameRGB->data, ctx->pFrameRGB->linesize, &ctx->buffer[0],
		m_grab_as_grayscale ? AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24, width,
		height, 1);

	return true;  // OK.
#else
	return false;
#endif
}

/* --------------------------------------------------------
					close
   -------------------------------------------------------- */
void CFFMPEG_InputStream::close()
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return;

	TFFMPEGContext* ctx = &m_impl->m_state;

	// Close the codec
	if (ctx->pCodecCtx)
	{
		avcodec_close(ctx->pCodecCtx);
		ctx->pCodecCtx = nullptr;
	}

	// Close the video file
	if (ctx->pFormatCtx)
	{
		avformat_close_input(&ctx->pFormatCtx);
		ctx->pFormatCtx = nullptr;
	}

	// Free frames memory:
	ctx->buffer.clear();

	if (ctx->pFrameRGB)
	{
		av_frame_free(&ctx->pFrameRGB);
		ctx->pFrameRGB = nullptr;
	}
	if (ctx->pFrame)
	{
		av_frame_free(&ctx->pFrame);
		ctx->pFrame = nullptr;
	}

	if (ctx->img_convert_ctx)
	{
		sws_freeContext(ctx->img_convert_ctx);
		ctx->img_convert_ctx = nullptr;
	}

#endif
}

/* --------------------------------------------------------
					retrieveFrame
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::retrieveFrame(mrpt::img::CImage& out_img)
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return false;

	TFFMPEGContext* ctx = &m_impl->m_state;

	AVPacket packet;

#if LIBAVFORMAT_VERSION_MAJOR < 58
	int frameFinished;
#endif

#if LIBAVFORMAT_VERSION_MAJOR >= 58
	const auto width = ctx->pCodecPars->width, height = ctx->pCodecPars->height;
#else
	const auto width = ctx->pCodecCtx->width, height = ctx->pCodecCtx->height;
#endif

	while (av_read_frame(ctx->pFormatCtx, &packet) >= 0)
	{
		// Is this a packet from the video stream?
		if (packet.stream_index != ctx->videoStream)
		{
			av_packet_unref(&packet);
			continue;
		}

		// Decode video frame
#if LIBAVFORMAT_VERSION_MAJOR >= 58
		int ret = avcodec_send_packet(ctx->pCodecCtx, &packet);
		if (ret < 0)
		{
			std::cerr << "[CFFMPEG_InputStream] avcodec_send_packet error code="
					  << ret << std::endl;
			return false;
		}
		// while (ret >= 0)
		ret = avcodec_receive_frame(ctx->pCodecCtx, ctx->pFrame);
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
			return false;
		else if (ret < 0)
		{
			std::cerr << "[CFFMPEG_InputStream] avcodec_receive_frame "
						 "error code="
					  << ret << std::endl;
			return false;
		}

#else
		avcodec_decode_video2(
			ctx->pCodecCtx, ctx->pFrame, &frameFinished, &packet);
		if (!frameFinished)
		{
			// Free the packet that was allocated by av_read_frame
			av_packet_unref(&packet);
			continue;
		}
#endif
		// Convert the image from its native format to RGB:
		ctx->img_convert_ctx = sws_getCachedContext(
			ctx->img_convert_ctx, width, height, ctx->pCodecCtx->pix_fmt, width,
			height,
			m_grab_as_grayscale ?  // BGR vs. RGB for OpenCV
				AV_PIX_FMT_GRAY8
								: AV_PIX_FMT_BGR24,
			SWS_BICUBIC, nullptr, nullptr, nullptr);

		sws_scale(
			ctx->img_convert_ctx, ctx->pFrame->data, ctx->pFrame->linesize, 0,
			height, ctx->pFrameRGB->data, ctx->pFrameRGB->linesize);

		// std::cout << "[retrieveFrame] Generating image: " <<
		// ctx->pCodecPars->width << "x" << ctx->pCodecPars->height
		// << std::endl; std::cout << "  linsize: " <<
		// ctx->pFrameRGB->linesize[0] << std::endl;

		if (ctx->pFrameRGB->linesize[0] !=
			((m_grab_as_grayscale ? 1 : 3) * width))
			THROW_EXCEPTION("FIXME: linesize!=width case not handled yet.");

		out_img.loadFromMemoryBuffer(
			width, height, !m_grab_as_grayscale, ctx->pFrameRGB->data[0]);

		// Free the packet that was allocated by av_read_frame
		av_packet_unref(&packet);
		return true;

		// Free the packet that was allocated by av_read_frame
		av_packet_unref(&packet);
	}

	return false;  // Error reading/ EOF
#else
	return false;
#endif
}

/* --------------------------------------------------------
					getVideoFPS
   -------------------------------------------------------- */
double CFFMPEG_InputStream::getVideoFPS() const
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return -1;

	const TFFMPEGContext* ctx = &m_impl->m_state;
	if (!ctx) return -1;
	if (!ctx->pCodecCtx) return -1;

	return static_cast<double>(ctx->pCodecCtx->time_base.den) /
		   ctx->pCodecCtx->time_base.num;
#else
	return false;
#endif
}
<commit_msg>ffmpeg: fix version check<commit_after>/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2020, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

#if defined(__GNUC__)  // Needed for ffmpeg headers. Only allowed here when not
// using precomp. headers
#define __STDC_CONSTANT_MACROS  // Needed for having "UINT64_C" and so
#endif

#include <mrpt/config.h>

#include "hwdrivers-precomp.h"  // Precompiled headers

#if MRPT_HAS_FFMPEG
extern "C"
{
#define _MSC_STDINT_H_  // We already have pstdint.h in MRPT
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
#endif

#include <mrpt/hwdrivers/CFFMPEG_InputStream.h>

using namespace mrpt;
using namespace mrpt::hwdrivers;

// JLBC: This file takes portions of code from the example
// "avcodec_sample.0.4.9.cpp"
//
// Minimum ffmpeg libs versions we want to support:
// Ubuntu 16.04 LTS: avcodec 56.60.100, avutil 54.31.100, avformat 56.40.101
// Ubuntu 20.04 LTS: avcodec 58.54.100, avutil 56.31.100, avformat 58.29.100
//
#if MRPT_HAS_FFMPEG
namespace mrpt::hwdrivers
{
// All context for ffmpeg:
struct TFFMPEGContext
{
	AVFormatContext* pFormatCtx{nullptr};
	int videoStream{0};
#if LIBAVFORMAT_VERSION_MAJOR >= 57
	AVCodecParameters* pCodecPars{nullptr};
#endif
	AVCodec* pCodec{nullptr};
	AVCodecContext* pCodecCtx{nullptr};
	AVFrame* pFrame{nullptr};
	AVFrame* pFrameRGB{nullptr};
	SwsContext* img_convert_ctx{nullptr};
	std::vector<uint8_t> buffer;
};
}  // namespace mrpt::hwdrivers
#endif

struct CFFMPEG_InputStream::Impl
{
#if MRPT_HAS_FFMPEG
	TFFMPEGContext m_state;
#endif
};

/* --------------------------------------------------------
					Ctor
   -------------------------------------------------------- */
CFFMPEG_InputStream::CFFMPEG_InputStream()
#if MRPT_HAS_FFMPEG
	: m_impl(mrpt::make_impl<CFFMPEG_InputStream::Impl>())
{
// av_register_all() not needed in ffmpeg >=4.0
#if LIBAVFORMAT_VERSION_MAJOR < 58
	// Register all formats and codecs
	av_register_all();
#endif
}
#else
{
	THROW_EXCEPTION("MRPT has been compiled without FFMPEG libraries.");
}
#endif

/* --------------------------------------------------------
					Dtor
   -------------------------------------------------------- */
CFFMPEG_InputStream::~CFFMPEG_InputStream()
{
#if MRPT_HAS_FFMPEG
	// Close everything:
	this->close();
#endif
}

/* --------------------------------------------------------
					isOpen
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::isOpen() const
{
#if MRPT_HAS_FFMPEG
	const TFFMPEGContext* ctx = &m_impl->m_state;
	return ctx->pFormatCtx != nullptr;
#else
	return false;
#endif
}

/* --------------------------------------------------------
					openURL
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::openURL(
	const std::string& url, bool grab_as_grayscale, bool verbose)
{
#if MRPT_HAS_FFMPEG
	this->close();  // Close first

	TFFMPEGContext* ctx = &m_impl->m_state;

	this->m_url = url;
	this->m_grab_as_grayscale = grab_as_grayscale;

	// Open video file
	if (avformat_open_input(&ctx->pFormatCtx, url.c_str(), nullptr, nullptr) !=
		0)
	{
		ctx->pFormatCtx = nullptr;
		std::cerr << "[CFFMPEG_InputStream::openURL] Cannot open video: " << url
				  << std::endl;
		return false;
	}

	// Retrieve stream information
	if (avformat_find_stream_info(ctx->pFormatCtx, nullptr) < 0)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Couldn't find stream "
					 "information: "
				  << url << std::endl;
		return false;
	}

	// Dump information about file onto standard error
	if (verbose)
	{
		av_dump_format(ctx->pFormatCtx, 0, url.c_str(), false);
	}

	// Find the first video stream
	ctx->videoStream = -1;
	for (unsigned int i = 0; i < ctx->pFormatCtx->nb_streams; i++)
	{
#if LIBAVFORMAT_VERSION_MAJOR >= 57
		auto codecType = ctx->pFormatCtx->streams[i]->codecpar->codec_type;
#else
		auto codecType = ctx->pFormatCtx->streams[i]->codec->codec_type;
#endif
		if (codecType == AVMEDIA_TYPE_VIDEO)
		{
			ctx->videoStream = (int)i;
			break;
		}
	}
	if (ctx->videoStream == -1)
	{
		std::cerr
			<< "[CFFMPEG_InputStream::openURL] Didn't find a video stream: "
			<< url << std::endl;
		return false;
	}

	// Get a pointer to the codec context for the video stream
#if LIBAVFORMAT_VERSION_MAJOR >= 57
	ctx->pCodecPars = ctx->pFormatCtx->streams[ctx->videoStream]->codecpar;
	// Find the decoder for the video stream
	ctx->pCodec = avcodec_find_decoder(ctx->pCodecPars->codec_id);
#else
	ctx->pCodecCtx = ctx->pFormatCtx->streams[ctx->videoStream]->codec;
	// Find the decoder for the video stream
	ctx->pCodec = avcodec_find_decoder(ctx->pCodecCtx->codec_id);
#endif
	if (ctx->pCodec == nullptr)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Codec not found: " << url
				  << std::endl;
		return false;
	}

#if LIBAVFORMAT_VERSION_MAJOR >= 57
	ctx->pCodecCtx = avcodec_alloc_context3(nullptr /*ctx->pCodec*/);
	if (!ctx->pCodecCtx)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Cannot alloc avcodec "
					 "context for: "
				  << url << std::endl;
		return false;
	}

	// Add stream parameters to context
	if (avcodec_parameters_to_context(
			ctx->pCodecCtx,
			ctx->pFormatCtx->streams[ctx->videoStream]->codecpar))
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Failed "
					 "avcodec_parameters_to_context() for: "
				  << url << std::endl;
		return false;
	}

	// Make sure that Codecs are identical or  avcodec_open2 fails.
	ctx->pCodecCtx->codec_id = ctx->pCodec->id;
#endif

	// Open codec
	if (avcodec_open2(ctx->pCodecCtx, ctx->pCodec, nullptr) < 0)
	{
		std::cerr
			<< "[CFFMPEG_InputStream::openURL] avcodec_open2() failed for: "
			<< url << std::endl;
		return false;
	}

	// Allocate video frame
	ctx->pFrame = av_frame_alloc();
	// Allocate an AVFrame structure
	ctx->pFrameRGB = av_frame_alloc();

	if (ctx->pFrameRGB == nullptr || ctx->pFrame == nullptr)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Could not alloc memory "
					 "for frame buffers: "
				  << url << std::endl;
		return false;
	}

	// Determine required buffer size and allocate buffer
#if LIBAVFORMAT_VERSION_MAJOR >= 57
	const auto width = ctx->pCodecPars->width, height = ctx->pCodecPars->height;
#else
	const auto width = ctx->pCodecCtx->width, height = ctx->pCodecCtx->height;
#endif
	int numBytes = av_image_get_buffer_size(
		m_grab_as_grayscale ? AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24, width,
		height, 1);
	if (numBytes < 0)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] av_image_get_buffer_size "
					 "error code: "
				  << numBytes << std::endl;
		return false;
	}

	ctx->buffer.resize(numBytes);

	// Assign appropriate parts of buffer to image planes in pFrameRGB

	av_image_fill_arrays(
		ctx->pFrameRGB->data, ctx->pFrameRGB->linesize, &ctx->buffer[0],
		m_grab_as_grayscale ? AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24, width,
		height, 1);

	return true;  // OK.
#else
	return false;
#endif
}

/* --------------------------------------------------------
					close
   -------------------------------------------------------- */
void CFFMPEG_InputStream::close()
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return;

	TFFMPEGContext* ctx = &m_impl->m_state;

	// Close the codec
	if (ctx->pCodecCtx)
	{
		avcodec_close(ctx->pCodecCtx);
		ctx->pCodecCtx = nullptr;
	}

	// Close the video file
	if (ctx->pFormatCtx)
	{
		avformat_close_input(&ctx->pFormatCtx);
		ctx->pFormatCtx = nullptr;
	}

	// Free frames memory:
	ctx->buffer.clear();

	if (ctx->pFrameRGB)
	{
		av_frame_free(&ctx->pFrameRGB);
		ctx->pFrameRGB = nullptr;
	}
	if (ctx->pFrame)
	{
		av_frame_free(&ctx->pFrame);
		ctx->pFrame = nullptr;
	}

	if (ctx->img_convert_ctx)
	{
		sws_freeContext(ctx->img_convert_ctx);
		ctx->img_convert_ctx = nullptr;
	}

#endif
}

/* --------------------------------------------------------
					retrieveFrame
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::retrieveFrame(mrpt::img::CImage& out_img)
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return false;

	TFFMPEGContext* ctx = &m_impl->m_state;

	AVPacket packet;

#if LIBAVFORMAT_VERSION_MAJOR < 58
	int frameFinished;
#endif

#if LIBAVFORMAT_VERSION_MAJOR >= 57
	const auto width = ctx->pCodecPars->width, height = ctx->pCodecPars->height;
#else
	const auto width = ctx->pCodecCtx->width, height = ctx->pCodecCtx->height;
#endif

	while (av_read_frame(ctx->pFormatCtx, &packet) >= 0)
	{
		// Is this a packet from the video stream?
		if (packet.stream_index != ctx->videoStream)
		{
			av_packet_unref(&packet);
			continue;
		}

		// Decode video frame
#if LIBAVFORMAT_VERSION_MAJOR >= 57
		int ret = avcodec_send_packet(ctx->pCodecCtx, &packet);
		if (ret < 0)
		{
			std::cerr << "[CFFMPEG_InputStream] avcodec_send_packet error code="
					  << ret << std::endl;
			return false;
		}
		// while (ret >= 0)
		ret = avcodec_receive_frame(ctx->pCodecCtx, ctx->pFrame);
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
			return false;
		else if (ret < 0)
		{
			std::cerr << "[CFFMPEG_InputStream] avcodec_receive_frame "
						 "error code="
					  << ret << std::endl;
			return false;
		}

#else
		avcodec_decode_video2(
			ctx->pCodecCtx, ctx->pFrame, &frameFinished, &packet);
		if (!frameFinished)
		{
			// Free the packet that was allocated by av_read_frame
			av_packet_unref(&packet);
			continue;
		}
#endif
		// Convert the image from its native format to RGB:
		ctx->img_convert_ctx = sws_getCachedContext(
			ctx->img_convert_ctx, width, height, ctx->pCodecCtx->pix_fmt, width,
			height,
			m_grab_as_grayscale ?  // BGR vs. RGB for OpenCV
				AV_PIX_FMT_GRAY8
								: AV_PIX_FMT_BGR24,
			SWS_BICUBIC, nullptr, nullptr, nullptr);

		sws_scale(
			ctx->img_convert_ctx, ctx->pFrame->data, ctx->pFrame->linesize, 0,
			height, ctx->pFrameRGB->data, ctx->pFrameRGB->linesize);

		// std::cout << "[retrieveFrame] Generating image: " <<
		// ctx->pCodecPars->width << "x" << ctx->pCodecPars->height
		// << std::endl; std::cout << "  linsize: " <<
		// ctx->pFrameRGB->linesize[0] << std::endl;

		if (ctx->pFrameRGB->linesize[0] !=
			((m_grab_as_grayscale ? 1 : 3) * width))
			THROW_EXCEPTION("FIXME: linesize!=width case not handled yet.");

		out_img.loadFromMemoryBuffer(
			width, height, !m_grab_as_grayscale, ctx->pFrameRGB->data[0]);

		// Free the packet that was allocated by av_read_frame
		av_packet_unref(&packet);
		return true;

		// Free the packet that was allocated by av_read_frame
		av_packet_unref(&packet);
	}

	return false;  // Error reading/ EOF
#else
	return false;
#endif
}

/* --------------------------------------------------------
					getVideoFPS
   -------------------------------------------------------- */
double CFFMPEG_InputStream::getVideoFPS() const
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return -1;

	const TFFMPEGContext* ctx = &m_impl->m_state;
	if (!ctx) return -1;
	if (!ctx->pCodecCtx) return -1;

	return static_cast<double>(ctx->pCodecCtx->time_base.den) /
		   ctx->pCodecCtx->time_base.num;
#else
	return false;
#endif
}
<|endoftext|>


<commit_before>/* +---------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)               |
   |                          http://www.mrpt.org/                             |
   |                                                                           |
   | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file        |
   | See: http://www.mrpt.org/Authors - All rights reserved.                   |
   | Released under BSD License. See details in http://www.mrpt.org/License    |
   +---------------------------------------------------------------------------+ */

#if defined(__GNUC__)  // Needed for ffmpeg headers. Only allowed here when not using precomp. headers
	#define __STDC_CONSTANT_MACROS  // Needed for having "UINT64_C" and so
#endif

#include "hwdrivers-precomp.h"   // Precompiled headers

#include <mrpt/config.h>
#include <mrpt/utils/utils_defs.h>

#if MRPT_HAS_FFMPEG
	extern "C"
	{
	#define _MSC_STDINT_H_    // We already have pstdint.h in MRPT
	#include <libavformat/avformat.h>
	#include <libavcodec/avcodec.h>
	#include <libswscale/swscale.h>
	#include <libavutil/imgutils.h>
	}
#endif

#include <mrpt/hwdrivers/CFFMPEG_InputStream.h>

using namespace mrpt;
using namespace mrpt::hwdrivers;

// JLBC: This file takes portions of code from the example "avcodec_sample.0.4.9.cpp"
#if MRPT_HAS_FFMPEG
namespace mrpt
{
	namespace hwdrivers
	{
		// All context for ffmpeg:
		struct TFFMPEGContext
		{
			AVFormatContext *pFormatCtx;
			int             videoStream;
			AVCodecContext  *pCodecCtx;
			AVCodec         *pCodec;
			AVFrame         *pFrame;
			AVFrame         *pFrameRGB;
			SwsContext		*img_convert_ctx;
			std::vector<uint8_t>  buffer;
		};
	}
}
#endif

#define MY_FFMPEG_STATE	const_cast<TFFMPEGContext*>(static_cast<const TFFMPEGContext*>(m_state.get()))

/* --------------------------------------------------------
					Ctor
   -------------------------------------------------------- */
CFFMPEG_InputStream::CFFMPEG_InputStream()
{
#if MRPT_HAS_FFMPEG
	m_state.set( new TFFMPEGContext[1] );
	TFFMPEGContext *ctx = MY_FFMPEG_STATE;

	ctx->pFormatCtx = nullptr;
	ctx->pCodecCtx = nullptr;
	ctx->pCodec = nullptr;
	ctx->videoStream = 0;
	ctx->pFrame = nullptr;
	ctx->pFrameRGB = nullptr;
	ctx->img_convert_ctx = nullptr;

    // Register all formats and codecs
    av_register_all();
#else
	THROW_EXCEPTION("MRPT has been compiled without FFMPEG libraries.")
#endif
}

/* --------------------------------------------------------
					Dtor
   -------------------------------------------------------- */
CFFMPEG_InputStream::~CFFMPEG_InputStream()
{
#if MRPT_HAS_FFMPEG
	// Close everything:
	this->close();

	// Free context struct. memory
	delete[] MY_FFMPEG_STATE;
	m_state.set(nullptr);
#endif
}

/* --------------------------------------------------------
					isOpen
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::isOpen() const
{
#if MRPT_HAS_FFMPEG
	TFFMPEGContext *ctx = MY_FFMPEG_STATE;
	return ctx->pFormatCtx != nullptr;
#else
	return false;
#endif
}

/* --------------------------------------------------------
					openURL
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::openURL( const std::string &url, bool grab_as_grayscale, bool verbose  )
{
#if MRPT_HAS_FFMPEG
	this->close();	// Close first

	TFFMPEGContext *ctx = MY_FFMPEG_STATE;

	this->m_url = url;
	this->m_grab_as_grayscale = grab_as_grayscale;

    // Open video file
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,2,0)
     if(avformat_open_input( &ctx->pFormatCtx, url.c_str(), nullptr, nullptr)!=0)
#else
     if(av_open_input_file( &ctx->pFormatCtx, url.c_str(), nullptr, 0, nullptr)!=0)
#endif

     {
          ctx->pFormatCtx = nullptr;
          std::cerr << "[CFFMPEG_InputStream::openURL] Cannot open video: " << url << std::endl;
               return false;
     }

    // Retrieve stream information
    if (
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,35,0)
		avformat_find_stream_info(ctx->pFormatCtx, nullptr)<0
#else
		av_find_stream_info(ctx->pFormatCtx)<0
#endif
		)
    {
    	std::cerr << "[CFFMPEG_InputStream::openURL] Couldn't find stream information: " << url << std::endl;
        return false;
    }

    // Dump information about file onto standard error
    if (verbose)
    {
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,2,0)
          av_dump_format(ctx->pFormatCtx, 0, url.c_str(), false);
#else
		dump_format(ctx->pFormatCtx, 0, url.c_str(), false);
#endif
    }

    // Find the first video stream
    ctx->videoStream=-1;
    for(unsigned int i=0; i<ctx->pFormatCtx->nb_streams; i++)
    {
		if(ctx->pFormatCtx->streams[i]->codec->codec_type==
#if LIBAVCODEC_VERSION_INT<AV_VERSION_INT(53,0,0)
			CODEC_TYPE_VIDEO
#else
			AVMEDIA_TYPE_VIDEO
#endif
			)
        {
            ctx->videoStream=(int)i;
            break;
        }
    }
    if(ctx->videoStream==-1)
    {
    	std::cerr << "[CFFMPEG_InputStream::openURL] Didn't find a video stream: " << url << std::endl;
        return false;
    }

    // Get a pointer to the codec context for the video stream
    ctx->pCodecCtx= ctx->pFormatCtx->streams[ctx->videoStream]->codec;

    // Find the decoder for the video stream
    ctx->pCodec=avcodec_find_decoder(ctx->pCodecCtx->codec_id);
    if(ctx->pCodec==nullptr)
    {
    	std::cerr << "[CFFMPEG_InputStream::openURL] Codec not found: " << url << std::endl;
        return false;
    }

    // Open codec
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,6,0)
    if(avcodec_open2(ctx->pCodecCtx, ctx->pCodec,nullptr)<0)
#else
    if(avcodec_open(ctx->pCodecCtx, ctx->pCodec)<0)
#endif
    {
    	std::cerr << "[CFFMPEG_InputStream::openURL] Could not open codec: " << url << std::endl;
        return false;
    }

#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,46,0)
	// Allocate video frame
	ctx->pFrame=av_frame_alloc();
	// Allocate an AVFrame structure
	ctx->pFrameRGB=av_frame_alloc();
#else
	// Allocate video frame
	ctx->pFrame=avcodec_alloc_frame();
	// Allocate an AVFrame structure
	ctx->pFrameRGB=avcodec_alloc_frame();
#endif

    if(ctx->pFrameRGB==nullptr || ctx->pFrame==nullptr)
    {
    	std::cerr << "[CFFMPEG_InputStream::openURL] Could not alloc memory for frame buffers: " << url << std::endl;
        return false;
    }

    // Determine required buffer size and allocate buffer
    size_t numBytes = av_image_get_buffer_size(
		m_grab_as_grayscale ?    // BGR vs. RGB for OpenCV
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,00,0)
			AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24,
#else
			PIX_FMT_GRAY8 : PIX_FMT_BGR24,
#endif
		ctx->pCodecCtx->width,
		ctx->pCodecCtx->height, 1);

    ctx->buffer.resize(numBytes);

    // Assign appropriate parts of buffer to image planes in pFrameRGB
    avpicture_fill(
		(AVPicture *)ctx->pFrameRGB,
		&ctx->buffer[0],
		m_grab_as_grayscale ?    // BGR vs. RGB for OpenCV
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,00,0)
			AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24,
#else
			PIX_FMT_GRAY8 : PIX_FMT_BGR24,
#endif
		ctx->pCodecCtx->width,
		ctx->pCodecCtx->height);

	return true; // OK.
#else
	return false;
#endif
}

/* --------------------------------------------------------
					close
   -------------------------------------------------------- */
void CFFMPEG_InputStream::close()
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return;

	TFFMPEGContext *ctx = MY_FFMPEG_STATE;

    // Close the codec
    if (ctx->pCodecCtx)
    {
		avcodec_close(ctx->pCodecCtx);
		ctx->pCodecCtx=nullptr;
    }

    // Close the video file
    if (ctx->pFormatCtx)
    {
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,35,0)
		avformat_close_input(&ctx->pFormatCtx);
#else
		av_close_input_file(ctx->pFormatCtx);
#endif
		ctx->pFormatCtx = nullptr;
    }

    // Free frames memory:
    ctx->buffer.clear();

    if (ctx->pFrameRGB)
    {
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,46,0)
		av_frame_free(&ctx->pFrameRGB);
#else
		av_free(ctx->pFrameRGB);
#endif
		ctx->pFrameRGB=nullptr;
    }
    if (ctx->pFrame)
    {
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,46,0)
		av_frame_free(&ctx->pFrame);
#else
		av_free(ctx->pFrame);
#endif
		ctx->pFrame = nullptr;
    }

	if (ctx->img_convert_ctx)
	{
		sws_freeContext( ctx->img_convert_ctx );
		ctx->img_convert_ctx = nullptr;
	}

#endif
}

/* --------------------------------------------------------
					retrieveFrame
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::retrieveFrame( mrpt::utils::CImage &out_img )
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return false;

	TFFMPEGContext *ctx = MY_FFMPEG_STATE;

    AVPacket        packet;
    int             frameFinished;

    while(av_read_frame(ctx->pFormatCtx, &packet)>=0)
    {
        // Is this a packet from the video stream?
        if(packet.stream_index==ctx->videoStream)
        {
            // Decode video frame
#if LIBAVCODEC_VERSION_MAJOR>52 || (LIBAVCODEC_VERSION_MAJOR==52 && LIBAVCODEC_VERSION_MINOR>=72)
            avcodec_decode_video2(
				ctx->pCodecCtx,
				ctx->pFrame,
				&frameFinished,
                &packet);
#else
            avcodec_decode_video(
				ctx->pCodecCtx,
				ctx->pFrame,
				&frameFinished,
                packet.data,
                packet.size);
#endif
            // Did we get a video frame?
            if(frameFinished)
            {
                // Convert the image from its native format to RGB:
				ctx->img_convert_ctx = sws_getCachedContext(
					ctx->img_convert_ctx,
					ctx->pCodecCtx->width,
					ctx->pCodecCtx->height,
					ctx->pCodecCtx->pix_fmt,
					ctx->pCodecCtx->width,
					ctx->pCodecCtx->height,
					m_grab_as_grayscale ?    // BGR vs. RGB for OpenCV
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,00,0)
						AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24,
#else
						PIX_FMT_GRAY8 : PIX_FMT_BGR24,
#endif
					SWS_BICUBIC,
					nullptr, nullptr, nullptr);

				sws_scale(
					ctx->img_convert_ctx,
					ctx->pFrame->data,
					ctx->pFrame->linesize,0,
					ctx->pCodecCtx->height,
					ctx->pFrameRGB->data,
					ctx->pFrameRGB->linesize);

				//std::cout << "[retrieveFrame] Generating image: " << ctx->pCodecCtx->width << "x" << ctx->pCodecCtx->height << std::endl;
				//std::cout << "  linsize: " << ctx->pFrameRGB->linesize[0] << std::endl;

				if( ctx->pFrameRGB->linesize[0]!= ((m_grab_as_grayscale ? 1:3)*ctx->pCodecCtx->width) )
					THROW_EXCEPTION("FIXME: linesize!=width case not handled yet.")

				out_img.loadFromMemoryBuffer(
					ctx->pCodecCtx->width,
					ctx->pCodecCtx->height,
					!m_grab_as_grayscale, // Color
					ctx->pFrameRGB->data[0]
					);

				// Free the packet that was allocated by av_read_frame
				av_packet_unref(&packet);
				return true;
            }
        }

        // Free the packet that was allocated by av_read_frame
        av_packet_unref(&packet);
    }

	return false; // Error reading/ EOF
#else
	return false;
#endif
}

/* --------------------------------------------------------
					getVideoFPS
   -------------------------------------------------------- */
double CFFMPEG_InputStream::getVideoFPS() const
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return -1;

	TFFMPEGContext *ctx = MY_FFMPEG_STATE;
	if (!ctx) return -1;
	if (!ctx->pCodecCtx) return -1;

	return static_cast<double>(ctx->pCodecCtx->time_base.den) / ctx->pCodecCtx->time_base.num;
#else
	return false;
#endif
}
<commit_msg>Add version checks for previous libav commit<commit_after>/* +---------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)               |
   |                          http://www.mrpt.org/                             |
   |                                                                           |
   | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file        |
   | See: http://www.mrpt.org/Authors - All rights reserved.                   |
   | Released under BSD License. See details in http://www.mrpt.org/License    |
   +---------------------------------------------------------------------------+ */

#if defined(__GNUC__)  // Needed for ffmpeg headers. Only allowed here when not using precomp. headers
	#define __STDC_CONSTANT_MACROS  // Needed for having "UINT64_C" and so
#endif

#include "hwdrivers-precomp.h"   // Precompiled headers

#include <mrpt/config.h>
#include <mrpt/utils/utils_defs.h>

#if MRPT_HAS_FFMPEG
	extern "C"
	{
	#define _MSC_STDINT_H_    // We already have pstdint.h in MRPT
	#include <libavformat/avformat.h>
	#include <libavcodec/avcodec.h>
	#include <libswscale/swscale.h>
	#include <libavutil/imgutils.h>
	}
#endif

#include <mrpt/hwdrivers/CFFMPEG_InputStream.h>

using namespace mrpt;
using namespace mrpt::hwdrivers;

// JLBC: This file takes portions of code from the example "avcodec_sample.0.4.9.cpp"
#if MRPT_HAS_FFMPEG
namespace mrpt
{
	namespace hwdrivers
	{
		// All context for ffmpeg:
		struct TFFMPEGContext
		{
			AVFormatContext *pFormatCtx;
			int             videoStream;
			AVCodecContext  *pCodecCtx;
			AVCodec         *pCodec;
			AVFrame         *pFrame;
			AVFrame         *pFrameRGB;
			SwsContext		*img_convert_ctx;
			std::vector<uint8_t>  buffer;
		};
	}
}
#endif

#define MY_FFMPEG_STATE	const_cast<TFFMPEGContext*>(static_cast<const TFFMPEGContext*>(m_state.get()))

/* --------------------------------------------------------
					Ctor
   -------------------------------------------------------- */
CFFMPEG_InputStream::CFFMPEG_InputStream()
{
#if MRPT_HAS_FFMPEG
	m_state.set( new TFFMPEGContext[1] );
	TFFMPEGContext *ctx = MY_FFMPEG_STATE;

	ctx->pFormatCtx = nullptr;
	ctx->pCodecCtx = nullptr;
	ctx->pCodec = nullptr;
	ctx->videoStream = 0;
	ctx->pFrame = nullptr;
	ctx->pFrameRGB = nullptr;
	ctx->img_convert_ctx = nullptr;

    // Register all formats and codecs
    av_register_all();
#else
	THROW_EXCEPTION("MRPT has been compiled without FFMPEG libraries.")
#endif
}

/* --------------------------------------------------------
					Dtor
   -------------------------------------------------------- */
CFFMPEG_InputStream::~CFFMPEG_InputStream()
{
#if MRPT_HAS_FFMPEG
	// Close everything:
	this->close();

	// Free context struct. memory
	delete[] MY_FFMPEG_STATE;
	m_state.set(nullptr);
#endif
}

/* --------------------------------------------------------
					isOpen
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::isOpen() const
{
#if MRPT_HAS_FFMPEG
	TFFMPEGContext *ctx = MY_FFMPEG_STATE;
	return ctx->pFormatCtx != nullptr;
#else
	return false;
#endif
}

/* --------------------------------------------------------
					openURL
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::openURL( const std::string &url, bool grab_as_grayscale, bool verbose  )
{
#if MRPT_HAS_FFMPEG
	this->close();	// Close first

	TFFMPEGContext *ctx = MY_FFMPEG_STATE;

	this->m_url = url;
	this->m_grab_as_grayscale = grab_as_grayscale;

    // Open video file
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,2,0)
     if(avformat_open_input( &ctx->pFormatCtx, url.c_str(), nullptr, nullptr)!=0)
#else
     if(av_open_input_file( &ctx->pFormatCtx, url.c_str(), nullptr, 0, nullptr)!=0)
#endif

     {
          ctx->pFormatCtx = nullptr;
          std::cerr << "[CFFMPEG_InputStream::openURL] Cannot open video: " << url << std::endl;
               return false;
     }

    // Retrieve stream information
    if (
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,35,0)
		avformat_find_stream_info(ctx->pFormatCtx, nullptr)<0
#else
		av_find_stream_info(ctx->pFormatCtx)<0
#endif
		)
    {
    	std::cerr << "[CFFMPEG_InputStream::openURL] Couldn't find stream information: " << url << std::endl;
        return false;
    }

    // Dump information about file onto standard error
    if (verbose)
    {
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,2,0)
          av_dump_format(ctx->pFormatCtx, 0, url.c_str(), false);
#else
		dump_format(ctx->pFormatCtx, 0, url.c_str(), false);
#endif
    }

    // Find the first video stream
    ctx->videoStream=-1;
    for(unsigned int i=0; i<ctx->pFormatCtx->nb_streams; i++)
    {
		if(ctx->pFormatCtx->streams[i]->codec->codec_type==
#if LIBAVCODEC_VERSION_INT<AV_VERSION_INT(53,0,0)
			CODEC_TYPE_VIDEO
#else
			AVMEDIA_TYPE_VIDEO
#endif
			)
        {
            ctx->videoStream=(int)i;
            break;
        }
    }
    if(ctx->videoStream==-1)
    {
    	std::cerr << "[CFFMPEG_InputStream::openURL] Didn't find a video stream: " << url << std::endl;
        return false;
    }

    // Get a pointer to the codec context for the video stream
    ctx->pCodecCtx= ctx->pFormatCtx->streams[ctx->videoStream]->codec;

    // Find the decoder for the video stream
    ctx->pCodec=avcodec_find_decoder(ctx->pCodecCtx->codec_id);
    if(ctx->pCodec==nullptr)
    {
    	std::cerr << "[CFFMPEG_InputStream::openURL] Codec not found: " << url << std::endl;
        return false;
    }

    // Open codec
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,6,0)
    if(avcodec_open2(ctx->pCodecCtx, ctx->pCodec,nullptr)<0)
#else
    if(avcodec_open(ctx->pCodecCtx, ctx->pCodec)<0)
#endif
    {
    	std::cerr << "[CFFMPEG_InputStream::openURL] Could not open codec: " << url << std::endl;
        return false;
    }

#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,46,0)
	// Allocate video frame
	ctx->pFrame=av_frame_alloc();
	// Allocate an AVFrame structure
	ctx->pFrameRGB=av_frame_alloc();
#else
	// Allocate video frame
	ctx->pFrame=avcodec_alloc_frame();
	// Allocate an AVFrame structure
	ctx->pFrameRGB=avcodec_alloc_frame();
#endif

    if(ctx->pFrameRGB==nullptr || ctx->pFrame==nullptr)
    {
    	std::cerr << "[CFFMPEG_InputStream::openURL] Could not alloc memory for frame buffers: " << url << std::endl;
        return false;
    }

    // Determine required buffer size and allocate buffer
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 6, 0)
    size_t numBytes=avpicture_get_size(
#else
    size_t numBytes = av_image_get_buffer_size(
#endif
		m_grab_as_grayscale ?    // BGR vs. RGB for OpenCV
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,00,0)
		AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24,
#else
		PIX_FMT_GRAY8 : PIX_FMT_BGR24,
#endif
		ctx->pCodecCtx->width,
		ctx->pCodecCtx->height
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 6, 0)
		, 1
#endif
		);

    ctx->buffer.resize(numBytes);

    // Assign appropriate parts of buffer to image planes in pFrameRGB
    avpicture_fill(
		(AVPicture *)ctx->pFrameRGB,
		&ctx->buffer[0],
		m_grab_as_grayscale ?    // BGR vs. RGB for OpenCV
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,00,0)
			AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24,
#else
			PIX_FMT_GRAY8 : PIX_FMT_BGR24,
#endif
		ctx->pCodecCtx->width,
		ctx->pCodecCtx->height);

	return true; // OK.
#else
	return false;
#endif
}

/* --------------------------------------------------------
					close
   -------------------------------------------------------- */
void CFFMPEG_InputStream::close()
{
#if MRPT_HAS_FFMPEG
    if (!this->isOpen()) return;

    TFFMPEGContext *ctx = MY_FFMPEG_STATE;

    // Close the codec
    if (ctx->pCodecCtx)
    {
		avcodec_close(ctx->pCodecCtx);
		ctx->pCodecCtx=nullptr;
    }

    // Close the video file
    if (ctx->pFormatCtx)
    {
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,35,0)
		avformat_close_input(&ctx->pFormatCtx);
#else
		av_close_input_file(ctx->pFormatCtx);
#endif
		ctx->pFormatCtx = nullptr;
    }

    // Free frames memory:
    ctx->buffer.clear();

    if (ctx->pFrameRGB)
    {
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,46,0)
		av_frame_free(&ctx->pFrameRGB);
#else
		av_free(ctx->pFrameRGB);
#endif
		ctx->pFrameRGB=nullptr;
    }
    if (ctx->pFrame)
    {
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,46,0)
		av_frame_free(&ctx->pFrame);
#else
		av_free(ctx->pFrame);
#endif
		ctx->pFrame = nullptr;
    }

    if (ctx->img_convert_ctx)
    {
        sws_freeContext( ctx->img_convert_ctx );
        ctx->img_convert_ctx = nullptr;
    }

#endif
}

/* --------------------------------------------------------
					retrieveFrame
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::retrieveFrame( mrpt::utils::CImage &out_img )
{
#if MRPT_HAS_FFMPEG
    if (!this->isOpen()) return false;

    TFFMPEGContext *ctx = MY_FFMPEG_STATE;

    AVPacket        packet;
    int             frameFinished;

    while(av_read_frame(ctx->pFormatCtx, &packet)>=0)
    {
        // Is this a packet from the video stream?
        if(packet.stream_index==ctx->videoStream)
        {
            // Decode video frame
#if LIBAVCODEC_VERSION_MAJOR>52 || (LIBAVCODEC_VERSION_MAJOR==52 && LIBAVCODEC_VERSION_MINOR>=72)
            avcodec_decode_video2(
				ctx->pCodecCtx,
				ctx->pFrame,
				&frameFinished,
				&packet);
#else
            avcodec_decode_video(
				ctx->pCodecCtx,
				ctx->pFrame,
				&frameFinished,
				packet.data,
				packet.size);
#endif
            // Did we get a video frame?
            if(frameFinished)
            {
                // Convert the image from its native format to RGB:
				ctx->img_convert_ctx = sws_getCachedContext(
					ctx->img_convert_ctx,
					ctx->pCodecCtx->width,
					ctx->pCodecCtx->height,
					ctx->pCodecCtx->pix_fmt,
					ctx->pCodecCtx->width,
					ctx->pCodecCtx->height,
					m_grab_as_grayscale ?    // BGR vs. RGB for OpenCV
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,00,0)
						AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24,
#else
						PIX_FMT_GRAY8 : PIX_FMT_BGR24,
#endif
					SWS_BICUBIC,
					nullptr, nullptr, nullptr);

				sws_scale(
					ctx->img_convert_ctx,
					ctx->pFrame->data,
					ctx->pFrame->linesize,0,
					ctx->pCodecCtx->height,
					ctx->pFrameRGB->data,
					ctx->pFrameRGB->linesize);

				//std::cout << "[retrieveFrame] Generating image: " << ctx->pCodecCtx->width << "x" << ctx->pCodecCtx->height << std::endl;
				//std::cout << "  linsize: " << ctx->pFrameRGB->linesize[0] << std::endl;

				if( ctx->pFrameRGB->linesize[0]!= ((m_grab_as_grayscale ? 1:3)*ctx->pCodecCtx->width) )
					THROW_EXCEPTION("FIXME: linesize!=width case not handled yet.")

				out_img.loadFromMemoryBuffer(
					ctx->pCodecCtx->width,
					ctx->pCodecCtx->height,
					!m_grab_as_grayscale, // Color
					ctx->pFrameRGB->data[0]
					);

				// Free the packet that was allocated by av_read_frame
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 16, 0)
				av_free_packet(&packet);
#else
				av_packet_unref(&packet);
#endif
				return true;
            }
        }

        // Free the packet that was allocated by av_read_frame
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 16, 0)
        av_free_packet(&packet);
#else
        av_packet_unref(&packet);
#endif
    }

	return false; // Error reading/ EOF
#else
	return false;
#endif
}

/* --------------------------------------------------------
					getVideoFPS
   -------------------------------------------------------- */
double CFFMPEG_InputStream::getVideoFPS() const
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return -1;

	TFFMPEGContext *ctx = MY_FFMPEG_STATE;
	if (!ctx) return -1;
	if (!ctx->pCodecCtx) return -1;

	return static_cast<double>(ctx->pCodecCtx->time_base.den) / ctx->pCodecCtx->time_base.num;
#else
	return false;
#endif
}
<|endoftext|>


<commit_before>#ifndef MESOS_SCHED_HPP
#define MESOS_SCHED_HPP

#include <string>
#include <map>
#include <vector>

#include <mesos.hpp>

namespace mesos {

class SchedulerDriver;

namespace internal {
class SchedulerProcess;
class MasterDetector;
class Configuration;
}

/**
 * Callback interface to be implemented by new frameworks' schedulers.
 */
class Scheduler
{
public:
  virtual ~Scheduler() {}

  // Callbacks for getting framework properties.
  virtual std::string getFrameworkName(SchedulerDriver* driver) = 0;

  virtual ExecutorInfo getExecutorInfo(SchedulerDriver* driver) = 0;

  // Callbacks for various Mesos events.

  virtual void registered(SchedulerDriver* driver,
                          const FrameworkID& frameworkId) = 0;

  virtual void resourceOffer(SchedulerDriver* driver,
                             const OfferID& offerId,
                             const std::vector<SlaveOffer>& offers) = 0;

  virtual void offerRescinded(SchedulerDriver* driver,
                              const OfferID& offerId) = 0;

  virtual void statusUpdate(SchedulerDriver* driver,
                            const TaskStatus& status) = 0;

  virtual void frameworkMessage(SchedulerDriver* driver,
                                const FrameworkMessage& message) = 0;

  virtual void slaveLost(SchedulerDriver* driver,
                         const SlaveID& sid) = 0;

  virtual void error(SchedulerDriver* driver,
                     int code,
                     const std::string& message) = 0;
};

/**
 * Abstract interface for driving a scheduler connected to Mesos.
 * This interface is used both to manage the scheduler's lifecycle (start it,
 * stop it, or wait for it to finish) and to send commands from the user
 * framework to Mesos (such as replies to offers). Concrete implementations
 * of SchedulerDriver will take a Scheduler as a parameter in order to make
 * callbacks into it on various events.
 */
class SchedulerDriver
{
public:
  virtual ~SchedulerDriver() {}

  // Lifecycle methods.
  virtual int start() = 0;
  virtual int stop() = 0;
  virtual int join() = 0;
  virtual int run() = 0; // Start and then join driver.

  // Communication methods.

  virtual int sendFrameworkMessage(const FrameworkMessage& message) = 0;

  virtual int killTask(const TaskID& taskId) = 0;

  virtual int replyToOffer(const OfferID& offerId,
			   const std::vector<TaskDescription>& tasks,
			   const std::map<std::string, std::string>& params) = 0;

  virtual int replyToOffer(const OfferID& offerId,
                           const std::vector<TaskDescription>& tasks)
  {
    return replyToOffer(offerId, tasks, std::map<std::string, std::string>());
  }

  virtual int reviveOffers() = 0;
};

/**
 * Concrete implementation of SchedulerDriver that communicates with
 * a Mesos master.
 */
class MesosSchedulerDriver : public SchedulerDriver
{
public:
  /**
   * Create a scheduler driver with a given Mesos master URL.
   * Additional Mesos config options are read from the environment, as well
   * as any config files found through it.
   *
   * @param sched scheduler to make callbacks into
   * @param url Mesos master URL
   * @param frameworkId optional framework ID for registering
   *        redundant schedulers for the same framework
   */
  MesosSchedulerDriver(Scheduler* sched,
                       const std::string& url,
                       const FrameworkID& frameworkId = FrameworkID());

  /**
   * Create a scheduler driver with a configuration, which the master URL
   * and possibly other options are read from.
   * Additional Mesos config options are read from the environment, as well
   * as any config files given through conf or found in the environment.
   *
   * @param sched scheduler to make callbacks into
   * @param params Map containing configuration options
   * @param frameworkId optional framework ID for registering
   *        redundant schedulers for the same framework
   */
  MesosSchedulerDriver(Scheduler* sched,
                       const std::map<std::string, std::string>& params,
		       const FrameworkID& frameworkId = FrameworkID());

#ifndef SWIG
  /**
   * Create a scheduler driver with a config read from command-line arguments.
   * Additional Mesos config options are read from the environment, as well
   * as any config files given through conf or found in the environment.
   *
   * This constructor is not available through SWIG since it's difficult
   * for it to properly map arrays to an argc/argv pair.
   *
   * @param sched scheduler to make callbacks into
   * @param argc argument count
   * @param argv argument values (argument 0 is expected to be program name
   *             and will not be looked at for options)
   * @param frameworkId optional framework ID for registering
   *        redundant schedulers for the same framework
   */
  MesosSchedulerDriver(Scheduler* sched,
		       int argc,
                       char** argv,
		       const FrameworkID& frameworkId = FrameworkID());
#endif

  virtual ~MesosSchedulerDriver();

  // Lifecycle methods.
  virtual int start();
  virtual int stop();
  virtual int join();
  virtual int run(); // Start and then join driver.

  // Communication methods.
  virtual int sendFrameworkMessage(const FrameworkMessage& message);

  virtual int killTask(const TaskID& taskId);

  virtual int replyToOffer(const OfferID& offerId,
			   const std::vector<TaskDescription>& tasks,
			   const std::map<std::string, std::string>& params);

  virtual int replyToOffer(const OfferID& offerId,
                           const std::vector<TaskDescription>& tasks)
  {
    return replyToOffer(offerId, tasks, std::map<std::string, std::string>());
  }

  virtual int reviveOffers();

private:
  // Initialization method used by constructors
  void init(Scheduler* sched,
            internal::Configuration* conf,
            const FrameworkID& frameworkId);

  // Internal utility method to report an error to the scheduler
  void error(int code, const std::string& message);

  Scheduler* sched;
  std::string url;
  FrameworkID frameworkId;

  // Libprocess process for communicating with master
  internal::SchedulerProcess* process;

  // Coordination between masters
  internal::MasterDetector* detector;

  // Configuration options.
  // TODO(benh|matei): Does this still need to be a pointer?
  internal::Configuration* conf;

  // Are we currently registered with the master
  bool running;

  // Mutex to enforce all non-callbacks are execute serially
  pthread_mutex_t mutex;

  // Condition variable for waiting until driver terminates
  pthread_cond_t cond;
};

} /* namespace mesos { */

#endif /* MESOS_SCHED_HPP */
<commit_msg>Fixed some tabs<commit_after>#ifndef MESOS_SCHED_HPP
#define MESOS_SCHED_HPP

#include <string>
#include <map>
#include <vector>

#include <mesos.hpp>

namespace mesos {

class SchedulerDriver;

namespace internal {
class SchedulerProcess;
class MasterDetector;
class Configuration;
}

/**
 * Callback interface to be implemented by new frameworks' schedulers.
 */
class Scheduler
{
public:
  virtual ~Scheduler() {}

  // Callbacks for getting framework properties.
  virtual std::string getFrameworkName(SchedulerDriver* driver) = 0;

  virtual ExecutorInfo getExecutorInfo(SchedulerDriver* driver) = 0;

  // Callbacks for various Mesos events.

  virtual void registered(SchedulerDriver* driver,
                          const FrameworkID& frameworkId) = 0;

  virtual void resourceOffer(SchedulerDriver* driver,
                             const OfferID& offerId,
                             const std::vector<SlaveOffer>& offers) = 0;

  virtual void offerRescinded(SchedulerDriver* driver,
                              const OfferID& offerId) = 0;

  virtual void statusUpdate(SchedulerDriver* driver,
                            const TaskStatus& status) = 0;

  virtual void frameworkMessage(SchedulerDriver* driver,
                                const FrameworkMessage& message) = 0;

  virtual void slaveLost(SchedulerDriver* driver,
                         const SlaveID& sid) = 0;

  virtual void error(SchedulerDriver* driver,
                     int code,
                     const std::string& message) = 0;
};

/**
 * Abstract interface for driving a scheduler connected to Mesos.
 * This interface is used both to manage the scheduler's lifecycle (start it,
 * stop it, or wait for it to finish) and to send commands from the user
 * framework to Mesos (such as replies to offers). Concrete implementations
 * of SchedulerDriver will take a Scheduler as a parameter in order to make
 * callbacks into it on various events.
 */
class SchedulerDriver
{
public:
  virtual ~SchedulerDriver() {}

  // Lifecycle methods.
  virtual int start() = 0;
  virtual int stop() = 0;
  virtual int join() = 0;
  virtual int run() = 0; // Start and then join driver.

  // Communication methods.

  virtual int sendFrameworkMessage(const FrameworkMessage& message) = 0;

  virtual int killTask(const TaskID& taskId) = 0;

  virtual int replyToOffer(const OfferID& offerId,
                           const std::vector<TaskDescription>& tasks,
                           const std::map<std::string, std::string>& params) = 0;

  virtual int replyToOffer(const OfferID& offerId,
                           const std::vector<TaskDescription>& tasks)
  {
    return replyToOffer(offerId, tasks, std::map<std::string, std::string>());
  }

  virtual int reviveOffers() = 0;
};

/**
 * Concrete implementation of SchedulerDriver that communicates with
 * a Mesos master.
 */
class MesosSchedulerDriver : public SchedulerDriver
{
public:
  /**
   * Create a scheduler driver with a given Mesos master URL.
   * Additional Mesos config options are read from the environment, as well
   * as any config files found through it.
   *
   * @param sched scheduler to make callbacks into
   * @param url Mesos master URL
   * @param frameworkId optional framework ID for registering
   *        redundant schedulers for the same framework
   */
  MesosSchedulerDriver(Scheduler* sched,
                       const std::string& url,
                       const FrameworkID& frameworkId = FrameworkID());

  /**
   * Create a scheduler driver with a configuration, which the master URL
   * and possibly other options are read from.
   * Additional Mesos config options are read from the environment, as well
   * as any config files given through conf or found in the environment.
   *
   * @param sched scheduler to make callbacks into
   * @param params Map containing configuration options
   * @param frameworkId optional framework ID for registering
   *        redundant schedulers for the same framework
   */
  MesosSchedulerDriver(Scheduler* sched,
                       const std::map<std::string, std::string>& params,
                       const FrameworkID& frameworkId = FrameworkID());

#ifndef SWIG
  /**
   * Create a scheduler driver with a config read from command-line arguments.
   * Additional Mesos config options are read from the environment, as well
   * as any config files given through conf or found in the environment.
   *
   * This constructor is not available through SWIG since it's difficult
   * for it to properly map arrays to an argc/argv pair.
   *
   * @param sched scheduler to make callbacks into
   * @param argc argument count
   * @param argv argument values (argument 0 is expected to be program name
   *             and will not be looked at for options)
   * @param frameworkId optional framework ID for registering
   *        redundant schedulers for the same framework
   */
  MesosSchedulerDriver(Scheduler* sched,
                       int argc,
                       char** argv,
                       const FrameworkID& frameworkId = FrameworkID());
#endif

  virtual ~MesosSchedulerDriver();

  // Lifecycle methods.
  virtual int start();
  virtual int stop();
  virtual int join();
  virtual int run(); // Start and then join driver.

  // Communication methods.
  virtual int sendFrameworkMessage(const FrameworkMessage& message);

  virtual int killTask(const TaskID& taskId);

  virtual int replyToOffer(const OfferID& offerId,
                           const std::vector<TaskDescription>& tasks,
                           const std::map<std::string, std::string>& params);

  virtual int replyToOffer(const OfferID& offerId,
                           const std::vector<TaskDescription>& tasks)
  {
    return replyToOffer(offerId, tasks, std::map<std::string, std::string>());
  }

  virtual int reviveOffers();

private:
  // Initialization method used by constructors
  void init(Scheduler* sched,
            internal::Configuration* conf,
            const FrameworkID& frameworkId);

  // Internal utility method to report an error to the scheduler
  void error(int code, const std::string& message);

  Scheduler* sched;
  std::string url;
  FrameworkID frameworkId;

  // Libprocess process for communicating with master
  internal::SchedulerProcess* process;

  // Coordination between masters
  internal::MasterDetector* detector;

  // Configuration options.
  // TODO(benh|matei): Does this still need to be a pointer?
  internal::Configuration* conf;

  // Are we currently registered with the master
  bool running;

  // Mutex to enforce all non-callbacks are execute serially
  pthread_mutex_t mutex;

  // Condition variable for waiting until driver terminates
  pthread_cond_t cond;
};

} /* namespace mesos { */

#endif /* MESOS_SCHED_HPP */
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "Util.hxx"

#include <rtl/ustrbuf.hxx>

using namespace ::connectivity;

using namespace ::rtl;

using namespace ::com::sun::star;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::uno;

OUString firebird::sanitizeIdentifier(const OUString& rIdentifier)
{
    OUString sRet = rIdentifier.trim();
    assert(sRet.getLength() <= 31); // Firebird identifiers cannot be longer than this.

    return sRet;
}

void firebird::evaluateStatusVector(ISC_STATUS_ARRAY& aStatusVector,
                                    const OUString& aCause,
                                    const uno::Reference< XInterface >& _rxContext)
    throw(SQLException)
{
    if (aStatusVector[0]==1 && aStatusVector[1]) // indicates error
    {
        OUStringBuffer buf;
        char msg[512]; // Size is based on suggestion in docs.
        const ISC_STATUS* pStatus = (const ISC_STATUS*) &aStatusVector;

        buf.appendAscii("firebird_sdbc error:");
        while(fb_interpret(msg, sizeof(msg), &pStatus))
        {
            // TODO: verify encoding
            buf.appendAscii("\n*");
            buf.append(OUString(msg, strlen(msg), RTL_TEXTENCODING_UTF8));
        }
        buf.appendAscii("\ncaused by\n'").append(aCause).appendAscii("'\n");

        OUString error = buf.makeStringAndClear();
        SAL_WARN("connectivity.firebird", error);

        throw SQLException( error, _rxContext, OUString(), 1, Any() );
    }
}

sal_Int32 firebird::getColumnTypeFromFBType(short aType)
{
    aType &= ~1; // Remove last bit -- it is used to denote whether column
                 // can store Null, not needed for type determination
    switch (aType)
    {
    case SQL_TEXT:
        return DataType::CHAR;
    case SQL_VARYING:
        return DataType::VARCHAR;
    case SQL_SHORT:
        return DataType::SMALLINT;
    case SQL_LONG:
        return DataType::INTEGER;
    case SQL_FLOAT:
        return DataType::FLOAT;
    case SQL_DOUBLE:
        return DataType::DOUBLE;
    case SQL_D_FLOAT:
        return DataType::DOUBLE;
    case SQL_TIMESTAMP:
        return DataType::TIMESTAMP;
    case SQL_BLOB:
        return DataType::BLOB;
    case SQL_ARRAY:
        return DataType::ARRAY;
    case SQL_TYPE_TIME:
        return DataType::TIME;
    case SQL_TYPE_DATE:
        return DataType::DATE;
    case SQL_INT64:
        return DataType::BIGINT;
    case SQL_NULL:
        return DataType::SQLNULL;
    case SQL_QUAD:      // Is a "Blob ID" according to the docs
        return 0;       // TODO: verify
    default:
        assert(false); // Should never happen
        return 0;
    }
}

OUString firebird::getColumnTypeNameFromFBType(short aType)
{
    aType &= ~1; // Remove last bit -- it is used to denote whether column
                // can store Null, not needed for type determination
    switch (aType)
    {
    case SQL_TEXT:
        return OUString("SQL_TEXT");
    case SQL_VARYING:
        return OUString("SQL_VARYING");
    case SQL_SHORT:
        return OUString("SQL_SHORT");
    case SQL_LONG:
        return OUString("SQL_LONG");
    case SQL_FLOAT:
        return OUString("SQL_FLOAT");
    case SQL_DOUBLE:
        return OUString("SQL_DOUBLE");
    case SQL_D_FLOAT:
        return OUString("SQL_D_FLOAT");
    case SQL_TIMESTAMP:
        return OUString("SQL_TIMESTAMP");
    case SQL_BLOB:
        return OUString("SQL_BLOB");
    case SQL_ARRAY:
        return OUString("SQL_ARRAY");
    case SQL_TYPE_TIME:
        return OUString("SQL_TYPE_TIME");
    case SQL_TYPE_DATE:
        return OUString("SQL_TYPE_DATE");
    case SQL_INT64:
        return OUString("SQL_INT64");
    case SQL_NULL:
        return OUString("SQL_NULL");
    case SQL_QUAD:
        return OUString("SQL_QUAD");
    default:
        assert(false); // Should never happen
        return OUString();
    }
}

short firebird::getFBTypeFromBlrType(short blrType)
{
    switch (blrType)
    {
    case blr_text:
        return SQL_TEXT;
    case blr_text2:
        assert(false);
        return 0; // No idea if this should be supported
    case blr_varying:
        return SQL_VARYING;
    case blr_varying2:
        assert(false);
        return 0; // No idea if this should be supported
    case blr_short:
        return SQL_SHORT;
    case blr_long:
        return SQL_LONG;
    case blr_float:
        return SQL_FLOAT;
    case blr_double:
        return SQL_DOUBLE;
    case blr_d_float:
        return SQL_D_FLOAT;
    case blr_timestamp:
        return SQL_TIMESTAMP;
    case blr_blob:
        return SQL_BLOB;
//     case blr_SQL_ARRAY:
//         return OUString("SQL_ARRAY");
    case blr_sql_time:
        return SQL_TYPE_TIME;
    case blr_sql_date:
        return SQL_TYPE_DATE;
    case blr_int64:
        return SQL_INT64;
//     case SQL_NULL:
//         return OUString("SQL_NULL");
    case blr_quad:
        return SQL_QUAD;
    default:
        // If this happens we have hit one of the extra types in ibase.h
        // look up blr_* for a list, e.g. blr_domain_name, blr_not_nullable etc.
        assert(false);
        return 0;
    }
}

void firebird::mallocSQLVAR(XSQLDA* pSqlda)
{
    // TODO: confirm the sizings below.
    XSQLVAR* pVar = pSqlda->sqlvar;
    for (int i=0; i < pSqlda->sqld; i++, pVar++)
    {
        int dtype = (pVar->sqltype & ~1); /* drop flag bit for now */
        switch(dtype) {
        case SQL_TEXT:
            pVar->sqldata = (char *)malloc(sizeof(char)*pVar->sqllen);
            break;
        case SQL_VARYING:
            pVar->sqldata = (char *)malloc(sizeof(char)*pVar->sqllen + 2);
            break;
        case SQL_SHORT:
            pVar->sqldata = (char *)malloc(sizeof(short));
            break;
        case SQL_LONG:
            pVar->sqldata = (char *)malloc(sizeof(long));
            break;
        case SQL_FLOAT:
            pVar->sqldata = (char *)malloc(sizeof(float));
            break;
        case SQL_DOUBLE:
            pVar->sqldata = (char *)malloc(sizeof(double));
            break;
        case SQL_D_FLOAT:
            pVar->sqldata = (char *)malloc(sizeof(double));
            break;
        case SQL_TIMESTAMP:
            pVar->sqldata = (char*) malloc(sizeof(ISC_TIMESTAMP));
            break;
        case SQL_BLOB:
            pVar->sqldata = (char*) malloc(sizeof(ISC_QUAD));
            break;
        case SQL_ARRAY:
            assert(false); // TODO: implement
            break;
        case SQL_TYPE_TIME:
            pVar->sqldata = (char*) malloc(sizeof(ISC_TIME));
            break;
        case SQL_TYPE_DATE:
            pVar->sqldata = (char*) malloc(sizeof(ISC_DATE));
            break;
        case SQL_INT64:
            pVar->sqldata = (char *)malloc(sizeof(sal_Int64));
            break;
        case SQL_NULL:
            assert(false); // TODO: implement
            break;
        case SQL_QUAD:
            assert(false); // TODO: implement
            break;
        default:
            SAL_WARN("connectivity.firebird", "Unknown type: " << dtype);
            assert(false);
            break;
        }
        if (pVar->sqltype & 1)
        {
            /* allocate variable to hold NULL status */
            pVar->sqlind = (short *)malloc(sizeof(short));
        }
    }
}

void firebird::freeSQLVAR(XSQLDA* pSqlda)
{
    XSQLVAR* pVar = pSqlda->sqlvar;
    for (int i=0; i < pSqlda->sqld; i++, pVar++)
    {
        int dtype = (pVar->sqltype & ~1); /* drop flag bit for now */
        switch(dtype) {
        case SQL_TEXT:
        case SQL_VARYING:
        case SQL_SHORT:
        case SQL_LONG:
        case SQL_FLOAT:
        case SQL_DOUBLE:
        case SQL_D_FLOAT:
        case SQL_TIMESTAMP:
        case SQL_BLOB:
        case SQL_INT64:
        case SQL_TYPE_TIME:
        case SQL_TYPE_DATE:
            free(pVar->sqldata);
            break;
        case SQL_ARRAY:
            assert(false); // TODO: implement
            break;
        case SQL_NULL:
            assert(false); // TODO: implement
            break;
        case SQL_QUAD:
            assert(false); // TODO: implement
            break;
        default:
            SAL_WARN("connectivity.firebird", "Unknown type: " << dtype);
            assert(false);
            break;
        }

        if (pVar->sqltype & 1)
        {
            free(pVar->sqlind);
        }
    }
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>Firebird: Use explicit integer sizes.<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "Util.hxx"

#include <rtl/ustrbuf.hxx>

using namespace ::connectivity;

using namespace ::rtl;

using namespace ::com::sun::star;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::uno;

OUString firebird::sanitizeIdentifier(const OUString& rIdentifier)
{
    OUString sRet = rIdentifier.trim();
    assert(sRet.getLength() <= 31); // Firebird identifiers cannot be longer than this.

    return sRet;
}

void firebird::evaluateStatusVector(ISC_STATUS_ARRAY& aStatusVector,
                                    const OUString& aCause,
                                    const uno::Reference< XInterface >& _rxContext)
    throw(SQLException)
{
    if (aStatusVector[0]==1 && aStatusVector[1]) // indicates error
    {
        OUStringBuffer buf;
        char msg[512]; // Size is based on suggestion in docs.
        const ISC_STATUS* pStatus = (const ISC_STATUS*) &aStatusVector;

        buf.appendAscii("firebird_sdbc error:");
        while(fb_interpret(msg, sizeof(msg), &pStatus))
        {
            // TODO: verify encoding
            buf.appendAscii("\n*");
            buf.append(OUString(msg, strlen(msg), RTL_TEXTENCODING_UTF8));
        }
        buf.appendAscii("\ncaused by\n'").append(aCause).appendAscii("'\n");

        OUString error = buf.makeStringAndClear();
        SAL_WARN("connectivity.firebird", error);

        throw SQLException( error, _rxContext, OUString(), 1, Any() );
    }
}

sal_Int32 firebird::getColumnTypeFromFBType(short aType)
{
    aType &= ~1; // Remove last bit -- it is used to denote whether column
                 // can store Null, not needed for type determination
    switch (aType)
    {
    case SQL_TEXT:
        return DataType::CHAR;
    case SQL_VARYING:
        return DataType::VARCHAR;
    case SQL_SHORT:
        return DataType::SMALLINT;
    case SQL_LONG:
        return DataType::INTEGER;
    case SQL_FLOAT:
        return DataType::FLOAT;
    case SQL_DOUBLE:
        return DataType::DOUBLE;
    case SQL_D_FLOAT:
        return DataType::DOUBLE;
    case SQL_TIMESTAMP:
        return DataType::TIMESTAMP;
    case SQL_BLOB:
        return DataType::BLOB;
    case SQL_ARRAY:
        return DataType::ARRAY;
    case SQL_TYPE_TIME:
        return DataType::TIME;
    case SQL_TYPE_DATE:
        return DataType::DATE;
    case SQL_INT64:
        return DataType::BIGINT;
    case SQL_NULL:
        return DataType::SQLNULL;
    case SQL_QUAD:      // Is a "Blob ID" according to the docs
        return 0;       // TODO: verify
    default:
        assert(false); // Should never happen
        return 0;
    }
}

OUString firebird::getColumnTypeNameFromFBType(short aType)
{
    aType &= ~1; // Remove last bit -- it is used to denote whether column
                // can store Null, not needed for type determination
    switch (aType)
    {
    case SQL_TEXT:
        return OUString("SQL_TEXT");
    case SQL_VARYING:
        return OUString("SQL_VARYING");
    case SQL_SHORT:
        return OUString("SQL_SHORT");
    case SQL_LONG:
        return OUString("SQL_LONG");
    case SQL_FLOAT:
        return OUString("SQL_FLOAT");
    case SQL_DOUBLE:
        return OUString("SQL_DOUBLE");
    case SQL_D_FLOAT:
        return OUString("SQL_D_FLOAT");
    case SQL_TIMESTAMP:
        return OUString("SQL_TIMESTAMP");
    case SQL_BLOB:
        return OUString("SQL_BLOB");
    case SQL_ARRAY:
        return OUString("SQL_ARRAY");
    case SQL_TYPE_TIME:
        return OUString("SQL_TYPE_TIME");
    case SQL_TYPE_DATE:
        return OUString("SQL_TYPE_DATE");
    case SQL_INT64:
        return OUString("SQL_INT64");
    case SQL_NULL:
        return OUString("SQL_NULL");
    case SQL_QUAD:
        return OUString("SQL_QUAD");
    default:
        assert(false); // Should never happen
        return OUString();
    }
}

short firebird::getFBTypeFromBlrType(short blrType)
{
    switch (blrType)
    {
    case blr_text:
        return SQL_TEXT;
    case blr_text2:
        assert(false);
        return 0; // No idea if this should be supported
    case blr_varying:
        return SQL_VARYING;
    case blr_varying2:
        assert(false);
        return 0; // No idea if this should be supported
    case blr_short:
        return SQL_SHORT;
    case blr_long:
        return SQL_LONG;
    case blr_float:
        return SQL_FLOAT;
    case blr_double:
        return SQL_DOUBLE;
    case blr_d_float:
        return SQL_D_FLOAT;
    case blr_timestamp:
        return SQL_TIMESTAMP;
    case blr_blob:
        return SQL_BLOB;
//     case blr_SQL_ARRAY:
//         return OUString("SQL_ARRAY");
    case blr_sql_time:
        return SQL_TYPE_TIME;
    case blr_sql_date:
        return SQL_TYPE_DATE;
    case blr_int64:
        return SQL_INT64;
//     case SQL_NULL:
//         return OUString("SQL_NULL");
    case blr_quad:
        return SQL_QUAD;
    default:
        // If this happens we have hit one of the extra types in ibase.h
        // look up blr_* for a list, e.g. blr_domain_name, blr_not_nullable etc.
        assert(false);
        return 0;
    }
}

void firebird::mallocSQLVAR(XSQLDA* pSqlda)
{
    // TODO: confirm the sizings below.
    XSQLVAR* pVar = pSqlda->sqlvar;
    for (int i=0; i < pSqlda->sqld; i++, pVar++)
    {
        int dtype = (pVar->sqltype & ~1); /* drop flag bit for now */
        switch(dtype) {
        case SQL_TEXT:
            pVar->sqldata = (char *)malloc(sizeof(char)*pVar->sqllen);
            break;
        case SQL_VARYING:
            pVar->sqldata = (char *)malloc(sizeof(char)*pVar->sqllen + 2);
            break;
        case SQL_SHORT:
            pVar->sqldata = (char*) malloc(sizeof(sal_Int16));
            break;
        case SQL_LONG:
            pVar->sqldata = (char*) malloc(sizeof(sal_Int32));
            break;
        case SQL_FLOAT:
            pVar->sqldata = (char *)malloc(sizeof(float));
            break;
        case SQL_DOUBLE:
            pVar->sqldata = (char *)malloc(sizeof(double));
            break;
        case SQL_D_FLOAT:
            pVar->sqldata = (char *)malloc(sizeof(double));
            break;
        case SQL_TIMESTAMP:
            pVar->sqldata = (char*) malloc(sizeof(ISC_TIMESTAMP));
            break;
        case SQL_BLOB:
            pVar->sqldata = (char*) malloc(sizeof(ISC_QUAD));
            break;
        case SQL_ARRAY:
            assert(false); // TODO: implement
            break;
        case SQL_TYPE_TIME:
            pVar->sqldata = (char*) malloc(sizeof(ISC_TIME));
            break;
        case SQL_TYPE_DATE:
            pVar->sqldata = (char*) malloc(sizeof(ISC_DATE));
            break;
        case SQL_INT64:
            pVar->sqldata = (char *)malloc(sizeof(sal_Int64));
            break;
        case SQL_NULL:
            assert(false); // TODO: implement
            break;
        case SQL_QUAD:
            assert(false); // TODO: implement
            break;
        default:
            SAL_WARN("connectivity.firebird", "Unknown type: " << dtype);
            assert(false);
            break;
        }
        if (pVar->sqltype & 1)
        {
            /* allocate variable to hold NULL status */
            pVar->sqlind = (short *)malloc(sizeof(short));
        }
    }
}

void firebird::freeSQLVAR(XSQLDA* pSqlda)
{
    XSQLVAR* pVar = pSqlda->sqlvar;
    for (int i=0; i < pSqlda->sqld; i++, pVar++)
    {
        int dtype = (pVar->sqltype & ~1); /* drop flag bit for now */
        switch(dtype) {
        case SQL_TEXT:
        case SQL_VARYING:
        case SQL_SHORT:
        case SQL_LONG:
        case SQL_FLOAT:
        case SQL_DOUBLE:
        case SQL_D_FLOAT:
        case SQL_TIMESTAMP:
        case SQL_BLOB:
        case SQL_INT64:
        case SQL_TYPE_TIME:
        case SQL_TYPE_DATE:
            free(pVar->sqldata);
            break;
        case SQL_ARRAY:
            assert(false); // TODO: implement
            break;
        case SQL_NULL:
            assert(false); // TODO: implement
            break;
        case SQL_QUAD:
            assert(false); // TODO: implement
            break;
        default:
            SAL_WARN("connectivity.firebird", "Unknown type: " << dtype);
            assert(false);
            break;
        }

        if (pVar->sqltype & 1)
        {
            free(pVar->sqlind);
        }
    }
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>