This commit is contained in:
rdb 2009-07-06 15:20:23 +00:00
parent a12727a957
commit 9e41cd84a3
4 changed files with 1692 additions and 1692 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,311 +1,311 @@
// Filename: ffmpegAudioCursor.cxx
// Created by: jyelon (01Aug2007)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifdef HAVE_FFMPEG
#include "ffmpegAudioCursor.h"
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
}
TypeHandle FfmpegAudioCursor::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::Constructor
// Access: Protected
// Description: xxx
////////////////////////////////////////////////////////////////////
FfmpegAudioCursor::
FfmpegAudioCursor(FfmpegAudio *src) :
MovieAudioCursor(src),
_filename(src->_filename),
_packet(0),
_packet_data(0),
_format_ctx(0),
_audio_ctx(0),
_buffer(0),
_buffer_alloc(0)
{
string url = "pandavfs:";
url += _filename;
if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
cleanup();
return;
}
if (av_find_stream_info(_format_ctx)<0) {
cleanup();
return;
}
// Find the audio stream
for(int i=0; i<_format_ctx->nb_streams; i++) {
if(_format_ctx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO) {
_audio_index = i;
_audio_ctx = _format_ctx->streams[i]->codec;
_audio_timebase = av_q2d(_format_ctx->streams[i]->time_base);
_audio_rate = _audio_ctx->sample_rate;
_audio_channels = _audio_ctx->channels;
}
}
if (_audio_ctx == 0) {
cleanup();
return;
}
AVCodec *pAudioCodec=avcodec_find_decoder(_audio_ctx->codec_id);
if(pAudioCodec == 0) {
cleanup();
return;
}
if(avcodec_open(_audio_ctx, pAudioCodec)<0) {
cleanup();
return;
}
_length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
_can_seek = true;
_can_seek_fast = true;
_packet = new AVPacket;
_buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE / 2;
_buffer_alloc = new PN_int16[_buffer_size + 128];
if ((_packet == 0)||(_buffer_alloc == 0)) {
cleanup();
return;
}
memset(_packet, 0, sizeof(AVPacket));
// Align the buffer to a 16-byte boundary
// The ffmpeg codec likes this, because it uses SSE/SSE2.
_buffer = _buffer_alloc;
while (((size_t)_buffer) & 15) {
_buffer += 1;
}
fetch_packet();
_initial_dts = _packet->dts;
_last_seek = 0;
_samples_read = 0;
_buffer_head = 0;
_buffer_tail = 0;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::Destructor
// Access: Protected, Virtual
// Description: xxx
////////////////////////////////////////////////////////////////////
FfmpegAudioCursor::
~FfmpegAudioCursor() {
cleanup();
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::cleanup
// Access: Public
// Description: Reset to a standard inactive state.
////////////////////////////////////////////////////////////////////
void FfmpegAudioCursor::
cleanup() {
if (_packet) {
if (_packet->data) {
av_free_packet(_packet);
}
delete _packet;
_packet = 0;
}
if (_buffer_alloc) {
delete[] _buffer_alloc;
_buffer_alloc = 0;
_buffer = 0;
}
if ((_audio_ctx)&&(_audio_ctx->codec)) {
avcodec_close(_audio_ctx);
}
_audio_ctx = 0;
if (_format_ctx) {
av_close_input_file(_format_ctx);
_format_ctx = 0;
}
_audio_ctx = 0;
_audio_index = -1;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::fetch_packet
// Access: Protected
// Description: Fetches an audio packet and stores it in the
// packet buffer. Also sets packet_size and packet_data.
////////////////////////////////////////////////////////////////////
void FfmpegAudioCursor::
fetch_packet() {
if (_packet->data) {
av_free_packet(_packet);
}
while (av_read_frame(_format_ctx, _packet) >= 0) {
if (_packet->stream_index == _audio_index) {
_packet_size = _packet->size;
_packet_data = _packet->data;
return;
}
av_free_packet(_packet);
}
_packet->data = 0;
_packet_size = 0;
_packet_data = 0;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::reload_buffer
// Access: Protected
// Description: Reloads the audio buffer by decoding audio packets
// until one of those audio packets finally yields
// some samples. If we encounter the end of the
// stream, we synthesize silence.
////////////////////////////////////////////////////////////////////
void FfmpegAudioCursor::
reload_buffer() {
while (_buffer_head == _buffer_tail) {
// If we're out of packets, generate silence.
if (_packet->data == 0) {
_buffer_head = 0;
_buffer_tail = _buffer_size;
memset(_buffer, 0, _buffer_size * 2);
return;
} else if (_packet_size > 0) {
int bufsize = _buffer_size * 2;
#if LIBAVCODEC_VERSION_INT < 3414272
#if LIBAVCODEC_VERSION_INT < 3349504
int len = avcodec_decode_audio(_audio_ctx, _buffer, &bufsize,
_packet_data, _packet_size);
movies_debug("avcodec_decode_audio returned " << len);
#else
int len = avcodec_decode_audio2(_audio_ctx, _buffer, &bufsize,
_packet_data, _packet_size);
movies_debug("avcodec_decode_audio2 returned " << len);
#endif
#else
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = _packet_data;
pkt.size = _packet_size;
int len = avcodec_decode_audio3(_audio_ctx, _buffer, &bufsize, &pkt);
movies_debug("avcodec_decode_audio3 returned " << len);
av_free_packet(&pkt); // Not sure about this
#endif
if (len <= 0) {
break;
}
_packet_data += len;
_packet_size -= len;
if (bufsize > 0) {
_buffer_head = 0;
_buffer_tail = (bufsize/2);
return;
}
} else {
fetch_packet();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::seek
// Access: Protected
// Description: Seeks to a target location. Afterward, the
// packet_time is guaranteed to be less than or
// equal to the specified time.
////////////////////////////////////////////////////////////////////
void FfmpegAudioCursor::
seek(double t) {
PN_int64 target_ts = (PN_int64)(t / _audio_timebase);
if (target_ts < (PN_int64)(_initial_dts)) {
// Attempts to seek before the first packet will fail.
target_ts = _initial_dts;
}
if (av_seek_frame(_format_ctx, _audio_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
movies_cat.error() << "Seek failure. Shutting down movie.\n";
cleanup();
return;
}
avcodec_close(_audio_ctx);
AVCodec *pAudioCodec=avcodec_find_decoder(_audio_ctx->codec_id);
if(pAudioCodec == 0) {
cleanup();
return;
}
if(avcodec_open(_audio_ctx, pAudioCodec)<0) {
cleanup();
return;
}
_buffer_head = 0;
_buffer_tail = 0;
fetch_packet();
double ts = _packet->dts * _audio_timebase;
if (t > ts) {
int skip = (int)((t-ts) * _audio_rate);
read_samples(skip, 0);
}
_last_seek = t;
_samples_read = 0;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::read_samples
// Access: Public, Virtual
// Description: Read audio samples from the stream. N is the
// number of samples you wish to read. Your buffer
// must be equal in size to N * channels.
// Multiple-channel audio will be interleaved.
////////////////////////////////////////////////////////////////////
void FfmpegAudioCursor::
read_samples(int n, PN_int16 *data) {
//movies_debug("here!!! FfmpegAudioCursor n="<<n);
int desired = n * _audio_channels;
// give up after 100 tries to fetch data
int give_up_after = 100;
while (desired && give_up_after > 0) {
if (_buffer_head == _buffer_tail) {
reload_buffer();
give_up_after --;
movies_debug("reload_buffer will give up in "<<give_up_after);
}
int available = _buffer_tail - _buffer_head;
int ncopy = (desired > available) ? available : desired;
if (ncopy) {
if (data != 0) {
memcpy(data, _buffer + _buffer_head, ncopy * 2);
data += ncopy;
}
desired -= ncopy;
_buffer_head += ncopy;
}
}
_samples_read += n;
}
////////////////////////////////////////////////////////////////////
#endif // HAVE_FFMPEG
// Filename: ffmpegAudioCursor.cxx
// Created by: jyelon (01Aug2007)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifdef HAVE_FFMPEG
#include "ffmpegAudioCursor.h"
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
}
TypeHandle FfmpegAudioCursor::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::Constructor
// Access: Protected
// Description: xxx
////////////////////////////////////////////////////////////////////
FfmpegAudioCursor::
FfmpegAudioCursor(FfmpegAudio *src) :
MovieAudioCursor(src),
_filename(src->_filename),
_packet(0),
_packet_data(0),
_format_ctx(0),
_audio_ctx(0),
_buffer(0),
_buffer_alloc(0)
{
string url = "pandavfs:";
url += _filename;
if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
cleanup();
return;
}
if (av_find_stream_info(_format_ctx)<0) {
cleanup();
return;
}
// Find the audio stream
for(int i=0; i<_format_ctx->nb_streams; i++) {
if(_format_ctx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO) {
_audio_index = i;
_audio_ctx = _format_ctx->streams[i]->codec;
_audio_timebase = av_q2d(_format_ctx->streams[i]->time_base);
_audio_rate = _audio_ctx->sample_rate;
_audio_channels = _audio_ctx->channels;
}
}
if (_audio_ctx == 0) {
cleanup();
return;
}
AVCodec *pAudioCodec=avcodec_find_decoder(_audio_ctx->codec_id);
if(pAudioCodec == 0) {
cleanup();
return;
}
if(avcodec_open(_audio_ctx, pAudioCodec)<0) {
cleanup();
return;
}
_length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
_can_seek = true;
_can_seek_fast = true;
_packet = new AVPacket;
_buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE / 2;
_buffer_alloc = new PN_int16[_buffer_size + 128];
if ((_packet == 0)||(_buffer_alloc == 0)) {
cleanup();
return;
}
memset(_packet, 0, sizeof(AVPacket));
// Align the buffer to a 16-byte boundary
// The ffmpeg codec likes this, because it uses SSE/SSE2.
_buffer = _buffer_alloc;
while (((size_t)_buffer) & 15) {
_buffer += 1;
}
fetch_packet();
_initial_dts = _packet->dts;
_last_seek = 0;
_samples_read = 0;
_buffer_head = 0;
_buffer_tail = 0;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::Destructor
// Access: Protected, Virtual
// Description: xxx
////////////////////////////////////////////////////////////////////
FfmpegAudioCursor::
~FfmpegAudioCursor() {
cleanup();
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::cleanup
// Access: Public
// Description: Reset to a standard inactive state.
////////////////////////////////////////////////////////////////////
void FfmpegAudioCursor::
cleanup() {
if (_packet) {
if (_packet->data) {
av_free_packet(_packet);
}
delete _packet;
_packet = 0;
}
if (_buffer_alloc) {
delete[] _buffer_alloc;
_buffer_alloc = 0;
_buffer = 0;
}
if ((_audio_ctx)&&(_audio_ctx->codec)) {
avcodec_close(_audio_ctx);
}
_audio_ctx = 0;
if (_format_ctx) {
av_close_input_file(_format_ctx);
_format_ctx = 0;
}
_audio_ctx = 0;
_audio_index = -1;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::fetch_packet
// Access: Protected
// Description: Fetches an audio packet and stores it in the
// packet buffer. Also sets packet_size and packet_data.
////////////////////////////////////////////////////////////////////
void FfmpegAudioCursor::
fetch_packet() {
if (_packet->data) {
av_free_packet(_packet);
}
while (av_read_frame(_format_ctx, _packet) >= 0) {
if (_packet->stream_index == _audio_index) {
_packet_size = _packet->size;
_packet_data = _packet->data;
return;
}
av_free_packet(_packet);
}
_packet->data = 0;
_packet_size = 0;
_packet_data = 0;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::reload_buffer
// Access: Protected
// Description: Reloads the audio buffer by decoding audio packets
// until one of those audio packets finally yields
// some samples. If we encounter the end of the
// stream, we synthesize silence.
////////////////////////////////////////////////////////////////////
void FfmpegAudioCursor::
reload_buffer() {
while (_buffer_head == _buffer_tail) {
// If we're out of packets, generate silence.
if (_packet->data == 0) {
_buffer_head = 0;
_buffer_tail = _buffer_size;
memset(_buffer, 0, _buffer_size * 2);
return;
} else if (_packet_size > 0) {
int bufsize = _buffer_size * 2;
#if LIBAVCODEC_VERSION_INT < 3414272
#if LIBAVCODEC_VERSION_INT < 3349504
int len = avcodec_decode_audio(_audio_ctx, _buffer, &bufsize,
_packet_data, _packet_size);
movies_debug("avcodec_decode_audio returned " << len);
#else
int len = avcodec_decode_audio2(_audio_ctx, _buffer, &bufsize,
_packet_data, _packet_size);
movies_debug("avcodec_decode_audio2 returned " << len);
#endif
#else
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = _packet_data;
pkt.size = _packet_size;
int len = avcodec_decode_audio3(_audio_ctx, _buffer, &bufsize, &pkt);
movies_debug("avcodec_decode_audio3 returned " << len);
av_free_packet(&pkt); // Not sure about this
#endif
if (len <= 0) {
break;
}
_packet_data += len;
_packet_size -= len;
if (bufsize > 0) {
_buffer_head = 0;
_buffer_tail = (bufsize/2);
return;
}
} else {
fetch_packet();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::seek
// Access: Protected
// Description: Seeks to a target location. Afterward, the
// packet_time is guaranteed to be less than or
// equal to the specified time.
////////////////////////////////////////////////////////////////////
void FfmpegAudioCursor::
seek(double t) {
PN_int64 target_ts = (PN_int64)(t / _audio_timebase);
if (target_ts < (PN_int64)(_initial_dts)) {
// Attempts to seek before the first packet will fail.
target_ts = _initial_dts;
}
if (av_seek_frame(_format_ctx, _audio_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
movies_cat.error() << "Seek failure. Shutting down movie.\n";
cleanup();
return;
}
avcodec_close(_audio_ctx);
AVCodec *pAudioCodec=avcodec_find_decoder(_audio_ctx->codec_id);
if(pAudioCodec == 0) {
cleanup();
return;
}
if(avcodec_open(_audio_ctx, pAudioCodec)<0) {
cleanup();
return;
}
_buffer_head = 0;
_buffer_tail = 0;
fetch_packet();
double ts = _packet->dts * _audio_timebase;
if (t > ts) {
int skip = (int)((t-ts) * _audio_rate);
read_samples(skip, 0);
}
_last_seek = t;
_samples_read = 0;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::read_samples
// Access: Public, Virtual
// Description: Read audio samples from the stream. N is the
// number of samples you wish to read. Your buffer
// must be equal in size to N * channels.
// Multiple-channel audio will be interleaved.
////////////////////////////////////////////////////////////////////
void FfmpegAudioCursor::
read_samples(int n, PN_int16 *data) {
//movies_debug("here!!! FfmpegAudioCursor n="<<n);
int desired = n * _audio_channels;
// give up after 100 tries to fetch data
int give_up_after = 100;
while (desired && give_up_after > 0) {
if (_buffer_head == _buffer_tail) {
reload_buffer();
give_up_after --;
movies_debug("reload_buffer will give up in "<<give_up_after);
}
int available = _buffer_tail - _buffer_head;
int ncopy = (desired > available) ? available : desired;
if (ncopy) {
if (data != 0) {
memcpy(data, _buffer + _buffer_head, ncopy * 2);
data += ncopy;
}
desired -= ncopy;
_buffer_head += ncopy;
}
}
_samples_read += n;
}
////////////////////////////////////////////////////////////////////
#endif // HAVE_FFMPEG

View File

@ -1,384 +1,384 @@
// Filename: ffmpegVideoCursor.cxx
// Created by: jyelon (01Aug2007)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifdef HAVE_FFMPEG
#include "ffmpegVideoCursor.h"
#include "config_movies.h"
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#ifdef HAVE_SWSCALE
#include "libswscale/swscale.h"
#endif
}
#include "pStatCollector.h"
#include "pStatTimer.h"
TypeHandle FfmpegVideoCursor::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::Constructor
// Access: Public
// Description: xxx
////////////////////////////////////////////////////////////////////
FfmpegVideoCursor::
FfmpegVideoCursor(FfmpegVideo *src) :
MovieVideoCursor(src),
_filename(src->_filename),
_format_ctx(0),
_video_index(-1),
_video_ctx(0),
_frame(0),
_frame_out(0),
_packet(0),
_min_fseek(3.0)
{
string url = "pandavfs:";
url += _filename;
if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
cleanup();
return;
}
if (av_find_stream_info(_format_ctx)<0) {
cleanup();
return;
}
// Find the video stream
for(int i=0; i<_format_ctx->nb_streams; i++) {
if(_format_ctx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
_video_index = i;
_video_ctx = _format_ctx->streams[i]->codec;
_video_timebase = av_q2d(_format_ctx->streams[i]->time_base);
}
}
if (_video_ctx == 0) {
cleanup();
return;
}
AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id);
if(pVideoCodec == 0) {
cleanup();
return;
}
if(avcodec_open(_video_ctx, pVideoCodec)<0) {
cleanup();
return;
}
_size_x = _video_ctx->width;
_size_y = _video_ctx->height;
_num_components = 3; // Don't know how to implement RGBA movies yet.
_length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
_can_seek = true;
_can_seek_fast = true;
_packet = new AVPacket;
_frame = avcodec_alloc_frame();
_frame_out = avcodec_alloc_frame();
if ((_packet == 0)||(_frame == 0)||(_frame_out == 0)) {
cleanup();
return;
}
memset(_packet, 0, sizeof(AVPacket));
fetch_packet(0.0);
_initial_dts = _packet->dts;
_packet_time = 0.0;
_last_start = -1.0;
_next_start = 0.0;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::Destructor
// Access: Public
// Description: xxx
////////////////////////////////////////////////////////////////////
FfmpegVideoCursor::
~FfmpegVideoCursor() {
cleanup();
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::cleanup
// Access: Public
// Description: Reset to a standard inactive state.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
cleanup() {
if (_frame) {
av_free(_frame);
_frame = 0;
}
if (_frame_out) {
_frame_out->data[0] = 0;
av_free(_frame_out);
_frame_out = 0;
}
if (_packet) {
if (_packet->data) {
av_free_packet(_packet);
}
delete _packet;
_packet = 0;
}
if ((_video_ctx)&&(_video_ctx->codec)) {
avcodec_close(_video_ctx);
}
_video_ctx = 0;
if (_format_ctx) {
av_close_input_file(_format_ctx);
_format_ctx = 0;
}
_video_ctx = 0;
_video_index = -1;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::export_frame
// Access: Public, Virtual
// Description: Exports the contents of the frame buffer into the
// user's target buffer.
////////////////////////////////////////////////////////////////////
static PStatCollector export_frame_collector("*:FFMPEG Convert Video to BGR");
void FfmpegVideoCursor::
export_frame(unsigned char *data, bool bgra, int bufx) {
PStatTimer timer(export_frame_collector);
if (bgra) {
_frame_out->data[0] = data + ((_size_y - 1) * bufx * 4);
_frame_out->linesize[0] = bufx * -4;
#ifdef HAVE_SWSCALE
struct SwsContext *convert_ctx = sws_getContext(_size_x, _size_y,
_video_ctx->pix_fmt, _size_x, _size_y,
PIX_FMT_BGRA, 2, NULL, NULL, NULL);
nassertv(convert_ctx != NULL);
sws_scale(convert_ctx, _frame->data, _frame->linesize,
0, _size_y, _frame_out->data, _frame_out->linesize);
sws_freeContext(convert_ctx);
#else
img_convert((AVPicture *)_frame_out, PIX_FMT_BGRA,
(AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
#endif
} else {
_frame_out->data[0] = data + ((_size_y - 1) * bufx * 3);
_frame_out->linesize[0] = bufx * -3;
#ifdef HAVE_SWSCALE
struct SwsContext *convert_ctx = sws_getContext(_size_x, _size_y,
_video_ctx->pix_fmt, _size_x, _size_y,
PIX_FMT_BGR24, 2, NULL, NULL, NULL);
nassertv(convert_ctx != NULL);
sws_scale(convert_ctx, _frame->data, _frame->linesize,
0, _size_y, _frame_out->data, _frame_out->linesize);
sws_freeContext(convert_ctx);
#else
img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24,
(AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
#endif
}
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::fetch_packet
// Access: Protected
// Description: Fetches a video packet and stores it in the
// packet buffer. Sets packet_time to the packet's
// timestamp. If a packet could not be read, the
// packet is cleared and the packet_time is set to
// the specified default value.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
fetch_packet(double default_time) {
if (_packet->data) {
av_free_packet(_packet);
}
while (av_read_frame(_format_ctx, _packet) >= 0) {
if (_packet->stream_index == _video_index) {
_packet_time = _packet->dts * _video_timebase;
return;
}
av_free_packet(_packet);
}
_packet->data = 0;
_packet_time = default_time;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::fetch_frame
// Access: Protected
// Description: Fetches a frame from the stream and stores it in
// the frame buffer. Sets last_start and next_start
// to indicate the extents of the frame.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
fetch_frame() {
int finished = 0;
_last_start = _packet_time;
while (!finished && _packet->data) {
#if LIBAVCODEC_VERSION_INT < 3414272
avcodec_decode_video(_video_ctx, _frame,
&finished, _packet->data, _packet->size);
#else
avcodec_decode_video2(_video_ctx, _frame, &finished, _packet);
#endif
fetch_packet(_last_start + 1.0);
}
_next_start = _packet_time;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::seek
// Access: Protected
// Description: Seeks to a target location. Afterward, the
// packet_time is guaranteed to be less than or
// equal to the specified time.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
seek(double t) {
PN_int64 target_ts = (PN_int64)(t / _video_timebase);
if (target_ts < (PN_int64)(_initial_dts)) {
// Attempts to seek before the first packet will fail.
target_ts = _initial_dts;
}
if (av_seek_frame(_format_ctx, _video_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
if (t >= _packet_time) {
return;
}
movies_cat.error() << "Seek failure. Shutting down movie.\n";
cleanup();
_packet_time = t;
return;
}
avcodec_close(_video_ctx);
AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id);
if(pVideoCodec == 0) {
cleanup();
return;
}
if(avcodec_open(_video_ctx, pVideoCodec)<0) {
cleanup();
return;
}
fetch_packet(t);
if (_packet_time > t) {
_packet_time = t;
}
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::fetch_time
// Access: Public, Virtual
// Description: Advance until the specified time is in the
// export buffer.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
fetch_time(double time) {
if (time < _last_start) {
// Time is in the past.
seek(time);
while (_packet_time <= time) {
fetch_frame();
}
} else if (time < _next_start) {
// Time is in the present: already have the frame.
} else if (time < _next_start + _min_fseek) {
// Time is in the near future.
while ((_packet_time <= time) && (_packet->data)) {
fetch_frame();
}
} else {
// Time is in the far future. Seek forward, then read.
// There's a danger here: because keyframes are spaced
// unpredictably, trying to seek forward could actually
// move us backward in the stream! This must be avoided.
// So the rule is, try the seek. If it hurts us by moving
// us backward, we increase the minimum threshold distance
// for forward-seeking in the future.
double base = _packet_time;
seek(time);
if (_packet_time < base) {
_min_fseek += (base - _packet_time);
}
while (_packet_time <= time) {
fetch_frame();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::fetch_into_texture
// Access: Public, Virtual
// Description: See MovieVideoCursor::fetch_into_texture.
////////////////////////////////////////////////////////////////////
static PStatCollector fetch_into_texture_pcollector("*:FFMPEG Video Decoding");
void FfmpegVideoCursor::
fetch_into_texture(double time, Texture *t, int page) {
PStatTimer timer(fetch_into_texture_pcollector);
nassertv(t->get_x_size() >= size_x());
nassertv(t->get_y_size() >= size_y());
nassertv((t->get_num_components() == 3) || (t->get_num_components() == 4));
nassertv(t->get_component_width() == 1);
nassertv(page < t->get_z_size());
PTA_uchar img = t->modify_ram_image();
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
// If there was an error at any point, synthesize black.
if (_format_ctx==0) {
if (data) {
memset(data,0,t->get_x_size() * t->get_y_size() * t->get_num_components());
}
_last_start = time;
_next_start = time + 1.0;
return;
}
fetch_time(time);
export_frame(data, (t->get_num_components()==4), t->get_x_size());
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::fetch_into_buffer
// Access: Public, Virtual
// Description: See MovieVideoCursor::fetch_into_buffer.
////////////////////////////////////////////////////////////////////
static PStatCollector fetch_into_buffer_pcollector("*:FFMPEG Video Decoding");
void FfmpegVideoCursor::
fetch_into_buffer(double time, unsigned char *data, bool bgra) {
PStatTimer timer(fetch_into_buffer_pcollector);
// If there was an error at any point, synthesize black.
if (_format_ctx==0) {
if (data) {
memset(data,0,size_x()*size_y()*(bgra?4:3));
}
_last_start = time;
_next_start = time + 1.0;
return;
}
fetch_time(time);
export_frame(data, bgra, _size_x);
}
////////////////////////////////////////////////////////////////////
#endif // HAVE_FFMPEG
// Filename: ffmpegVideoCursor.cxx
// Created by: jyelon (01Aug2007)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifdef HAVE_FFMPEG
#include "ffmpegVideoCursor.h"
#include "config_movies.h"
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#ifdef HAVE_SWSCALE
#include "libswscale/swscale.h"
#endif
}
#include "pStatCollector.h"
#include "pStatTimer.h"
TypeHandle FfmpegVideoCursor::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::Constructor
// Access: Public
// Description: xxx
////////////////////////////////////////////////////////////////////
FfmpegVideoCursor::
FfmpegVideoCursor(FfmpegVideo *src) :
MovieVideoCursor(src),
_filename(src->_filename),
_format_ctx(0),
_video_index(-1),
_video_ctx(0),
_frame(0),
_frame_out(0),
_packet(0),
_min_fseek(3.0)
{
string url = "pandavfs:";
url += _filename;
if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
cleanup();
return;
}
if (av_find_stream_info(_format_ctx)<0) {
cleanup();
return;
}
// Find the video stream
for(int i=0; i<_format_ctx->nb_streams; i++) {
if(_format_ctx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
_video_index = i;
_video_ctx = _format_ctx->streams[i]->codec;
_video_timebase = av_q2d(_format_ctx->streams[i]->time_base);
}
}
if (_video_ctx == 0) {
cleanup();
return;
}
AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id);
if(pVideoCodec == 0) {
cleanup();
return;
}
if(avcodec_open(_video_ctx, pVideoCodec)<0) {
cleanup();
return;
}
_size_x = _video_ctx->width;
_size_y = _video_ctx->height;
_num_components = 3; // Don't know how to implement RGBA movies yet.
_length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
_can_seek = true;
_can_seek_fast = true;
_packet = new AVPacket;
_frame = avcodec_alloc_frame();
_frame_out = avcodec_alloc_frame();
if ((_packet == 0)||(_frame == 0)||(_frame_out == 0)) {
cleanup();
return;
}
memset(_packet, 0, sizeof(AVPacket));
fetch_packet(0.0);
_initial_dts = _packet->dts;
_packet_time = 0.0;
_last_start = -1.0;
_next_start = 0.0;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::Destructor
// Access: Public
// Description: xxx
////////////////////////////////////////////////////////////////////
FfmpegVideoCursor::
~FfmpegVideoCursor() {
cleanup();
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::cleanup
// Access: Public
// Description: Reset to a standard inactive state.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
cleanup() {
if (_frame) {
av_free(_frame);
_frame = 0;
}
if (_frame_out) {
_frame_out->data[0] = 0;
av_free(_frame_out);
_frame_out = 0;
}
if (_packet) {
if (_packet->data) {
av_free_packet(_packet);
}
delete _packet;
_packet = 0;
}
if ((_video_ctx)&&(_video_ctx->codec)) {
avcodec_close(_video_ctx);
}
_video_ctx = 0;
if (_format_ctx) {
av_close_input_file(_format_ctx);
_format_ctx = 0;
}
_video_ctx = 0;
_video_index = -1;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::export_frame
// Access: Public, Virtual
// Description: Exports the contents of the frame buffer into the
// user's target buffer.
////////////////////////////////////////////////////////////////////
static PStatCollector export_frame_collector("*:FFMPEG Convert Video to BGR");
void FfmpegVideoCursor::
export_frame(unsigned char *data, bool bgra, int bufx) {
PStatTimer timer(export_frame_collector);
if (bgra) {
_frame_out->data[0] = data + ((_size_y - 1) * bufx * 4);
_frame_out->linesize[0] = bufx * -4;
#ifdef HAVE_SWSCALE
struct SwsContext *convert_ctx = sws_getContext(_size_x, _size_y,
_video_ctx->pix_fmt, _size_x, _size_y,
PIX_FMT_BGRA, 2, NULL, NULL, NULL);
nassertv(convert_ctx != NULL);
sws_scale(convert_ctx, _frame->data, _frame->linesize,
0, _size_y, _frame_out->data, _frame_out->linesize);
sws_freeContext(convert_ctx);
#else
img_convert((AVPicture *)_frame_out, PIX_FMT_BGRA,
(AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
#endif
} else {
_frame_out->data[0] = data + ((_size_y - 1) * bufx * 3);
_frame_out->linesize[0] = bufx * -3;
#ifdef HAVE_SWSCALE
struct SwsContext *convert_ctx = sws_getContext(_size_x, _size_y,
_video_ctx->pix_fmt, _size_x, _size_y,
PIX_FMT_BGR24, 2, NULL, NULL, NULL);
nassertv(convert_ctx != NULL);
sws_scale(convert_ctx, _frame->data, _frame->linesize,
0, _size_y, _frame_out->data, _frame_out->linesize);
sws_freeContext(convert_ctx);
#else
img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24,
(AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
#endif
}
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::fetch_packet
// Access: Protected
// Description: Fetches a video packet and stores it in the
// packet buffer. Sets packet_time to the packet's
// timestamp. If a packet could not be read, the
// packet is cleared and the packet_time is set to
// the specified default value.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
fetch_packet(double default_time) {
if (_packet->data) {
av_free_packet(_packet);
}
while (av_read_frame(_format_ctx, _packet) >= 0) {
if (_packet->stream_index == _video_index) {
_packet_time = _packet->dts * _video_timebase;
return;
}
av_free_packet(_packet);
}
_packet->data = 0;
_packet_time = default_time;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::fetch_frame
// Access: Protected
// Description: Fetches a frame from the stream and stores it in
// the frame buffer. Sets last_start and next_start
// to indicate the extents of the frame.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
fetch_frame() {
int finished = 0;
_last_start = _packet_time;
while (!finished && _packet->data) {
#if LIBAVCODEC_VERSION_INT < 3414272
avcodec_decode_video(_video_ctx, _frame,
&finished, _packet->data, _packet->size);
#else
avcodec_decode_video2(_video_ctx, _frame, &finished, _packet);
#endif
fetch_packet(_last_start + 1.0);
}
_next_start = _packet_time;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::seek
// Access: Protected
// Description: Seeks to a target location. Afterward, the
// packet_time is guaranteed to be less than or
// equal to the specified time.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
seek(double t) {
PN_int64 target_ts = (PN_int64)(t / _video_timebase);
if (target_ts < (PN_int64)(_initial_dts)) {
// Attempts to seek before the first packet will fail.
target_ts = _initial_dts;
}
if (av_seek_frame(_format_ctx, _video_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
if (t >= _packet_time) {
return;
}
movies_cat.error() << "Seek failure. Shutting down movie.\n";
cleanup();
_packet_time = t;
return;
}
avcodec_close(_video_ctx);
AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id);
if(pVideoCodec == 0) {
cleanup();
return;
}
if(avcodec_open(_video_ctx, pVideoCodec)<0) {
cleanup();
return;
}
fetch_packet(t);
if (_packet_time > t) {
_packet_time = t;
}
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::fetch_time
// Access: Public, Virtual
// Description: Advance until the specified time is in the
// export buffer.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
fetch_time(double time) {
if (time < _last_start) {
// Time is in the past.
seek(time);
while (_packet_time <= time) {
fetch_frame();
}
} else if (time < _next_start) {
// Time is in the present: already have the frame.
} else if (time < _next_start + _min_fseek) {
// Time is in the near future.
while ((_packet_time <= time) && (_packet->data)) {
fetch_frame();
}
} else {
// Time is in the far future. Seek forward, then read.
// There's a danger here: because keyframes are spaced
// unpredictably, trying to seek forward could actually
// move us backward in the stream! This must be avoided.
// So the rule is, try the seek. If it hurts us by moving
// us backward, we increase the minimum threshold distance
// for forward-seeking in the future.
double base = _packet_time;
seek(time);
if (_packet_time < base) {
_min_fseek += (base - _packet_time);
}
while (_packet_time <= time) {
fetch_frame();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::fetch_into_texture
// Access: Public, Virtual
// Description: See MovieVideoCursor::fetch_into_texture.
////////////////////////////////////////////////////////////////////
static PStatCollector fetch_into_texture_pcollector("*:FFMPEG Video Decoding");
void FfmpegVideoCursor::
fetch_into_texture(double time, Texture *t, int page) {
PStatTimer timer(fetch_into_texture_pcollector);
nassertv(t->get_x_size() >= size_x());
nassertv(t->get_y_size() >= size_y());
nassertv((t->get_num_components() == 3) || (t->get_num_components() == 4));
nassertv(t->get_component_width() == 1);
nassertv(page < t->get_z_size());
PTA_uchar img = t->modify_ram_image();
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
// If there was an error at any point, synthesize black.
if (_format_ctx==0) {
if (data) {
memset(data,0,t->get_x_size() * t->get_y_size() * t->get_num_components());
}
_last_start = time;
_next_start = time + 1.0;
return;
}
fetch_time(time);
export_frame(data, (t->get_num_components()==4), t->get_x_size());
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::fetch_into_buffer
// Access: Public, Virtual
// Description: See MovieVideoCursor::fetch_into_buffer.
////////////////////////////////////////////////////////////////////
static PStatCollector fetch_into_buffer_pcollector("*:FFMPEG Video Decoding");
void FfmpegVideoCursor::
fetch_into_buffer(double time, unsigned char *data, bool bgra) {
PStatTimer timer(fetch_into_buffer_pcollector);
// If there was an error at any point, synthesize black.
if (_format_ctx==0) {
if (data) {
memset(data,0,size_x()*size_y()*(bgra?4:3));
}
_last_start = time;
_next_start = time + 1.0;
return;
}
fetch_time(time);
export_frame(data, bgra, _size_x);
}
////////////////////////////////////////////////////////////////////
#endif // HAVE_FFMPEG

View File

@ -1,176 +1,176 @@
// Filename: ffmpegVirtualFile.cxx
// Created by: jyelon (02Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifdef HAVE_FFMPEG
#include "pandabase.h"
#include "config_movies.h"
#include "ffmpegVirtualFile.h"
#include "virtualFileSystem.h"
extern "C" {
#include "libavformat/avio.h"
}
#ifndef AVSEEK_SIZE
#define AVSEEK_SIZE 0x10000
#endif
////////////////////////////////////////////////////////////////////
// These functions need to use C calling conventions.
////////////////////////////////////////////////////////////////////
extern "C" {
static int pandavfs_open(URLContext *h, const char *filename, int flags);
static int pandavfs_read(URLContext *h, unsigned char *buf, int size);
static int pandavfs_write(URLContext *h, unsigned char *buf, int size);
static PN_int64 pandavfs_seek(URLContext *h, PN_int64 pos, int whence);
static int pandavfs_close(URLContext *h);
}
////////////////////////////////////////////////////////////////////
// Function: pandavfs_open
// Access: Static Function
// Description: A hook to open a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_open(URLContext *h, const char *filename, int flags) {
if (flags != 0) {
movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
return -1;
}
filename += 9; // Skip over "pandavfs:"
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
istream *s = vfs->open_read_file(filename, true);
if (s == 0) {
return -1;
}
// Test whether seek works.
s->seekg(1, ios::beg);
int tel1 = s->tellg();
s->seekg(0, ios::beg);
int tel2 = s->tellg();
if (s->fail() || (tel1!=1) || (tel2!=0)) {
movies_cat.error() << "cannot play movie (not seekable): " << h->filename << "\n";
delete s;
return -1;
}
h->priv_data = s;
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: pandavfs_read
// Access: Static Function
// Description: A hook to read a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_read(URLContext *h, unsigned char *buf, int size) {
istream *s = (istream*)(h->priv_data);
s->read((char*)buf, size);
int gc = s->gcount();
s->clear();
return gc;
}
////////////////////////////////////////////////////////////////////
// Function: pandavfs_write
// Access: Static Function
// Description: A hook to write a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_write(URLContext *h, unsigned char *buf, int size) {
movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
return -1;
}
////////////////////////////////////////////////////////////////////
// Function: pandavfs_seek
// Access: Static Function
// Description: A hook to seek a panda VFS file.
////////////////////////////////////////////////////////////////////
static PN_int64
pandavfs_seek(URLContext *h, PN_int64 pos, int whence) {
istream *s = (istream*)(h->priv_data);
switch(whence) {
case SEEK_SET: s->seekg(pos, ios::beg); break;
case SEEK_CUR: s->seekg(pos, ios::cur); break;
case SEEK_END: s->seekg(pos, ios::end); break;
case AVSEEK_SIZE: {
s->seekg(0, ios::cur);
int p = s->tellg();
s->seekg(-1, ios::end);
int size = s->tellg();
if (size < 0) {
movies_cat.error() << "Failed to determine filesize in ffmpegVirtualFile\n";
s->clear();
return -1;
}
size++;
s->seekg(p, ios::beg);
s->clear();
return size; }
default:
movies_cat.error() << "Illegal parameter to seek in ffmpegVirtualFile\n";
s->clear();
return -1;
}
s->clear();
int tl = s->tellg();
return tl;
}
////////////////////////////////////////////////////////////////////
// Function: pandavfs_close
// Access: Static Function
// Description: A hook to close a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_close(URLContext *h) {
istream *s = (istream*)(h->priv_data);
delete s;
h->priv_data = 0;
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVirtualFile::register_protocol
// Access: Public, Static
// Description: Enables ffmpeg to access panda's VFS.
//
// After calling this method, ffmpeg will be
// able to open "URLs" that look like this:
//
// pandavfs:/c/mygame/foo.avi
//
////////////////////////////////////////////////////////////////////
void FfmpegVirtualFile::
register_protocol() {
static bool initialized = false;
if (initialized) {
return;
}
static URLProtocol protocol;
protocol.name = "pandavfs";
protocol.url_open = pandavfs_open;
protocol.url_read = pandavfs_read;
protocol.url_write = pandavfs_write;
protocol.url_seek = pandavfs_seek;
protocol.url_close = pandavfs_close;
#if LIBAVFORMAT_VERSION_INT < 3415296
::register_protocol(&protocol);
#else
av_register_protocol(&protocol);
#endif
}
#endif // HAVE_FFMPEG
// Filename: ffmpegVirtualFile.cxx
// Created by: jyelon (02Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifdef HAVE_FFMPEG
#include "pandabase.h"
#include "config_movies.h"
#include "ffmpegVirtualFile.h"
#include "virtualFileSystem.h"
extern "C" {
#include "libavformat/avio.h"
}
#ifndef AVSEEK_SIZE
#define AVSEEK_SIZE 0x10000
#endif
////////////////////////////////////////////////////////////////////
// These functions need to use C calling conventions.
////////////////////////////////////////////////////////////////////
extern "C" {
static int pandavfs_open(URLContext *h, const char *filename, int flags);
static int pandavfs_read(URLContext *h, unsigned char *buf, int size);
static int pandavfs_write(URLContext *h, unsigned char *buf, int size);
static PN_int64 pandavfs_seek(URLContext *h, PN_int64 pos, int whence);
static int pandavfs_close(URLContext *h);
}
////////////////////////////////////////////////////////////////////
// Function: pandavfs_open
// Access: Static Function
// Description: A hook to open a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_open(URLContext *h, const char *filename, int flags) {
if (flags != 0) {
movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
return -1;
}
filename += 9; // Skip over "pandavfs:"
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
istream *s = vfs->open_read_file(filename, true);
if (s == 0) {
return -1;
}
// Test whether seek works.
s->seekg(1, ios::beg);
int tel1 = s->tellg();
s->seekg(0, ios::beg);
int tel2 = s->tellg();
if (s->fail() || (tel1!=1) || (tel2!=0)) {
movies_cat.error() << "cannot play movie (not seekable): " << h->filename << "\n";
delete s;
return -1;
}
h->priv_data = s;
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: pandavfs_read
// Access: Static Function
// Description: A hook to read a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_read(URLContext *h, unsigned char *buf, int size) {
istream *s = (istream*)(h->priv_data);
s->read((char*)buf, size);
int gc = s->gcount();
s->clear();
return gc;
}
////////////////////////////////////////////////////////////////////
// Function: pandavfs_write
// Access: Static Function
// Description: A hook to write a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_write(URLContext *h, unsigned char *buf, int size) {
movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
return -1;
}
////////////////////////////////////////////////////////////////////
// Function: pandavfs_seek
// Access: Static Function
// Description: A hook to seek a panda VFS file.
////////////////////////////////////////////////////////////////////
static PN_int64
pandavfs_seek(URLContext *h, PN_int64 pos, int whence) {
istream *s = (istream*)(h->priv_data);
switch(whence) {
case SEEK_SET: s->seekg(pos, ios::beg); break;
case SEEK_CUR: s->seekg(pos, ios::cur); break;
case SEEK_END: s->seekg(pos, ios::end); break;
case AVSEEK_SIZE: {
s->seekg(0, ios::cur);
int p = s->tellg();
s->seekg(-1, ios::end);
int size = s->tellg();
if (size < 0) {
movies_cat.error() << "Failed to determine filesize in ffmpegVirtualFile\n";
s->clear();
return -1;
}
size++;
s->seekg(p, ios::beg);
s->clear();
return size; }
default:
movies_cat.error() << "Illegal parameter to seek in ffmpegVirtualFile\n";
s->clear();
return -1;
}
s->clear();
int tl = s->tellg();
return tl;
}
////////////////////////////////////////////////////////////////////
// Function: pandavfs_close
// Access: Static Function
// Description: A hook to close a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_close(URLContext *h) {
istream *s = (istream*)(h->priv_data);
delete s;
h->priv_data = 0;
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVirtualFile::register_protocol
// Access: Public, Static
// Description: Enables ffmpeg to access panda's VFS.
//
// After calling this method, ffmpeg will be
// able to open "URLs" that look like this:
//
// pandavfs:/c/mygame/foo.avi
//
////////////////////////////////////////////////////////////////////
void FfmpegVirtualFile::
register_protocol() {
static bool initialized = false;
if (initialized) {
return;
}
static URLProtocol protocol;
protocol.name = "pandavfs";
protocol.url_open = pandavfs_open;
protocol.url_read = pandavfs_read;
protocol.url_write = pandavfs_write;
protocol.url_seek = pandavfs_seek;
protocol.url_close = pandavfs_close;
#if LIBAVFORMAT_VERSION_INT < 3415296
::register_protocol(&protocol);
#else
av_register_protocol(&protocol);
#endif
}
#endif // HAVE_FFMPEG