299 lines
7.2 KiB
C++
299 lines
7.2 KiB
C++
/*
|
|
* Copyright (C) 2012 Open Source Robotics Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
#include "gazebo/gazebo_config.h"
|
|
#include "gazebo/common/Console.hh"
|
|
#include "gazebo/common/Video.hh"
|
|
#include "gazebo/common/ffmpeg_inc.h"
|
|
|
|
using namespace gazebo;
|
|
using namespace common;
|
|
|
|
/// \brief Destination audio video frame
|
|
/// TODO Do not merge forward. Declared here for gazebo7 ABI compatibility
|
|
AVFrame *avFrameDst;
|
|
|
|
/////////////////////////////////////////////////
|
|
// #ifdef HAVE_FFMPEG
|
|
// static void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize,
|
|
// char *filename)
|
|
// {
|
|
// FILE *f;
|
|
// int i;
|
|
//
|
|
// f = fopen(filename, "w");
|
|
// fprintf(f, "P6\n%d %d\n%d\n", xsize, ysize, 255);
|
|
// for(i = 0; i < ysize; ++i)
|
|
// fwrite(buf + i * wrap, 1, xsize * 3, f);
|
|
// fclose(f);
|
|
// }
|
|
// #endif
|
|
|
|
/////////////////////////////////////////////////
|
|
Video::Video()
|
|
{
|
|
this->formatCtx = NULL;
|
|
this->codecCtx = NULL;
|
|
this->swsCtx = NULL;
|
|
this->avFrame = NULL;
|
|
this->videoStream = -1;
|
|
|
|
this->pic = NULL;
|
|
avFrameDst = NULL;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
Video::~Video()
|
|
{
|
|
this->Cleanup();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Video::Cleanup()
|
|
{
|
|
#ifdef HAVE_FFMPEG
|
|
// Free the YUV frame
|
|
av_free(this->avFrame);
|
|
|
|
// Close the video file
|
|
avformat_close_input(&this->formatCtx);
|
|
|
|
// Close the codec
|
|
avcodec_close(this->codecCtx);
|
|
|
|
av_free(avFrameDst);
|
|
#endif
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
#ifdef HAVE_FFMPEG
|
|
bool Video::Load(const std::string &_filename)
|
|
{
|
|
AVCodec *codec = NULL;
|
|
this->videoStream = -1;
|
|
|
|
if (this->formatCtx || this->avFrame || this->codecCtx)
|
|
this->Cleanup();
|
|
|
|
this->avFrame = common::AVFrameAlloc();
|
|
|
|
// Open video file
|
|
if (avformat_open_input(&this->formatCtx, _filename.c_str(), NULL, NULL) < 0)
|
|
{
|
|
gzerr << "Unable to read video file[" << _filename << "]\n";
|
|
return false;
|
|
}
|
|
|
|
// Retrieve stream information
|
|
if (avformat_find_stream_info(this->formatCtx, NULL) < 0)
|
|
{
|
|
gzerr << "Couldn't find stream information\n";
|
|
return false;
|
|
}
|
|
|
|
// Find the first video stream
|
|
for (unsigned int i = 0; i < this->formatCtx->nb_streams; ++i)
|
|
{
|
|
#ifndef _WIN32
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
#endif
|
|
if (this->formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
|
|
#ifndef _WIN32
|
|
# pragma GCC diagnostic pop
|
|
#endif
|
|
{
|
|
this->videoStream = static_cast<int>(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (this->videoStream == -1)
|
|
{
|
|
gzerr << "Unable to find a video stream\n";
|
|
return false;
|
|
}
|
|
|
|
// Get a pointer to the codec context for the video stream
|
|
#ifndef _WIN32
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
#endif
|
|
this->codecCtx = this->formatCtx->streams[this->videoStream]->codec;
|
|
#ifndef _WIN32
|
|
# pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
// Find the decoder for the video stream
|
|
codec = avcodec_find_decoder(this->codecCtx->codec_id);
|
|
if (codec == NULL)
|
|
{
|
|
gzerr << "Codec not found\n";
|
|
return false;
|
|
}
|
|
|
|
// Inform the codec that we can handle truncated bitstreams -- i.e.,
|
|
// bitstreams where frame boundaries can fall in the middle of packets
|
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56, 60, 100)
|
|
if (codec->capabilities & AV_CODEC_CAP_TRUNCATED)
|
|
this->codecCtx->flags |= AV_CODEC_FLAG_TRUNCATED;
|
|
#else
|
|
if (codec->capabilities & CODEC_CAP_TRUNCATED)
|
|
this->codecCtx->flags |= CODEC_FLAG_TRUNCATED;
|
|
#endif
|
|
|
|
// Open codec
|
|
if (avcodec_open2(this->codecCtx, codec, NULL) < 0)
|
|
{
|
|
gzerr << "Could not open codec\n";
|
|
return false;
|
|
}
|
|
|
|
this->swsCtx = sws_getContext(
|
|
this->codecCtx->width,
|
|
this->codecCtx->height,
|
|
this->codecCtx->pix_fmt,
|
|
this->codecCtx->width,
|
|
this->codecCtx->height,
|
|
AV_PIX_FMT_RGB24,
|
|
SWS_BICUBIC, NULL, NULL, NULL);
|
|
|
|
if (this->swsCtx == NULL)
|
|
{
|
|
gzerr << "Error while calling sws_getContext\n";
|
|
return false;
|
|
}
|
|
|
|
avFrameDst = common::AVFrameAlloc();
|
|
avFrameDst->format = this->codecCtx->pix_fmt;
|
|
avFrameDst->width = this->codecCtx->width;
|
|
avFrameDst->height = this->codecCtx->height;
|
|
av_image_alloc(avFrameDst->data, avFrameDst->linesize,
|
|
this->codecCtx->width, this->codecCtx->height, this->codecCtx->pix_fmt,
|
|
1);
|
|
|
|
// DEBUG: Will save all the frames
|
|
/*Image img;
|
|
char buf[1024];
|
|
int frame = 0;
|
|
|
|
// the decoding loop, running until EOF
|
|
while (this->GetNextFrame(img))
|
|
{
|
|
printf("WH[%d %d]\n",this->codecCtx->width, this->codecCtx->height);
|
|
snprintf(buf, sizeof(buf), "/tmp/test_%3d.png", frame++);
|
|
img.SavePNG(buf);
|
|
}
|
|
printf("Done\n");
|
|
*/
|
|
|
|
return true;
|
|
}
|
|
#else
|
|
bool Video::Load(const std::string &/*_filename*/)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////
|
|
#ifdef HAVE_FFMPEG
|
|
bool Video::GetNextFrame(unsigned char **_buffer)
|
|
{
|
|
AVPacket packet, tmpPacket;
|
|
int frameAvailable = 0;
|
|
|
|
av_init_packet(&packet);
|
|
|
|
// Read a frame.
|
|
if (av_read_frame(this->formatCtx, &packet) < 0)
|
|
return false;
|
|
|
|
if (packet.stream_index == this->videoStream)
|
|
{
|
|
tmpPacket.data = packet.data;
|
|
tmpPacket.size = packet.size;
|
|
|
|
// Process all the data in the frame
|
|
while (tmpPacket.size > 0)
|
|
{
|
|
// sending data to libavcodec
|
|
#ifndef _WIN32
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
#endif
|
|
int processedLength = avcodec_decode_video2(this->codecCtx, this->avFrame,
|
|
&frameAvailable, &tmpPacket);
|
|
#ifndef _WIN32
|
|
# pragma GCC diagnostic pop
|
|
#endif
|
|
if (processedLength < 0)
|
|
{
|
|
gzerr << "Error while processing the data\n";
|
|
break;
|
|
}
|
|
|
|
tmpPacket.data = tmpPacket.data + processedLength;
|
|
tmpPacket.size = tmpPacket.size - processedLength;
|
|
|
|
// processing the image if available
|
|
if (frameAvailable)
|
|
{
|
|
sws_scale(swsCtx, this->avFrame->data, this->avFrame->linesize, 0,
|
|
this->codecCtx->height, avFrameDst->data,
|
|
avFrameDst->linesize);
|
|
|
|
memcpy(*_buffer, avFrameDst->data[0],
|
|
this->codecCtx->height * (this->codecCtx->width*3));
|
|
|
|
// Debug:
|
|
// pgm_save(this->pic.data[0], this->pic.linesize[0],
|
|
// this->codecCtx->width, this->codecCtx->height, buf);
|
|
}
|
|
}
|
|
}
|
|
AVPacketUnref(&packet);
|
|
|
|
return true;
|
|
}
|
|
#else
|
|
bool Video::GetNextFrame(unsigned char ** /*_buffer*/)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////
|
|
int Video::GetWidth() const
|
|
{
|
|
#ifdef HAVE_FFMPEG
|
|
return this->codecCtx->width;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
int Video::GetHeight() const
|
|
{
|
|
#ifdef HAVE_FFMPEG
|
|
return this->codecCtx->height;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|