pxmlw6n2f/Gazebo_Distributed_TCP/gazebo/common/Video.cc

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
}