mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 10:22:45 -04:00
2unix
This commit is contained in:
parent
a12727a957
commit
9e41cd84a3
File diff suppressed because it is too large
Load Diff
@ -1,311 +1,311 @@
|
|||||||
// Filename: ffmpegAudioCursor.cxx
|
// Filename: ffmpegAudioCursor.cxx
|
||||||
// Created by: jyelon (01Aug2007)
|
// Created by: jyelon (01Aug2007)
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// PANDA 3D SOFTWARE
|
// PANDA 3D SOFTWARE
|
||||||
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||||
//
|
//
|
||||||
// All use of this software is subject to the terms of the revised BSD
|
// 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
|
// license. You should have received a copy of this license along
|
||||||
// with this source code in a file named "LICENSE."
|
// with this source code in a file named "LICENSE."
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#ifdef HAVE_FFMPEG
|
#ifdef HAVE_FFMPEG
|
||||||
|
|
||||||
#include "ffmpegAudioCursor.h"
|
#include "ffmpegAudioCursor.h"
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "libavcodec/avcodec.h"
|
#include "libavcodec/avcodec.h"
|
||||||
#include "libavformat/avformat.h"
|
#include "libavformat/avformat.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeHandle FfmpegAudioCursor::_type_handle;
|
TypeHandle FfmpegAudioCursor::_type_handle;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegAudioCursor::Constructor
|
// Function: FfmpegAudioCursor::Constructor
|
||||||
// Access: Protected
|
// Access: Protected
|
||||||
// Description: xxx
|
// Description: xxx
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
FfmpegAudioCursor::
|
FfmpegAudioCursor::
|
||||||
FfmpegAudioCursor(FfmpegAudio *src) :
|
FfmpegAudioCursor(FfmpegAudio *src) :
|
||||||
MovieAudioCursor(src),
|
MovieAudioCursor(src),
|
||||||
_filename(src->_filename),
|
_filename(src->_filename),
|
||||||
_packet(0),
|
_packet(0),
|
||||||
_packet_data(0),
|
_packet_data(0),
|
||||||
_format_ctx(0),
|
_format_ctx(0),
|
||||||
_audio_ctx(0),
|
_audio_ctx(0),
|
||||||
_buffer(0),
|
_buffer(0),
|
||||||
_buffer_alloc(0)
|
_buffer_alloc(0)
|
||||||
{
|
{
|
||||||
string url = "pandavfs:";
|
string url = "pandavfs:";
|
||||||
url += _filename;
|
url += _filename;
|
||||||
if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
|
if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (av_find_stream_info(_format_ctx)<0) {
|
if (av_find_stream_info(_format_ctx)<0) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the audio stream
|
// Find the audio stream
|
||||||
for(int i=0; i<_format_ctx->nb_streams; i++) {
|
for(int i=0; i<_format_ctx->nb_streams; i++) {
|
||||||
if(_format_ctx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO) {
|
if(_format_ctx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO) {
|
||||||
_audio_index = i;
|
_audio_index = i;
|
||||||
_audio_ctx = _format_ctx->streams[i]->codec;
|
_audio_ctx = _format_ctx->streams[i]->codec;
|
||||||
_audio_timebase = av_q2d(_format_ctx->streams[i]->time_base);
|
_audio_timebase = av_q2d(_format_ctx->streams[i]->time_base);
|
||||||
_audio_rate = _audio_ctx->sample_rate;
|
_audio_rate = _audio_ctx->sample_rate;
|
||||||
_audio_channels = _audio_ctx->channels;
|
_audio_channels = _audio_ctx->channels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_audio_ctx == 0) {
|
if (_audio_ctx == 0) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVCodec *pAudioCodec=avcodec_find_decoder(_audio_ctx->codec_id);
|
AVCodec *pAudioCodec=avcodec_find_decoder(_audio_ctx->codec_id);
|
||||||
if(pAudioCodec == 0) {
|
if(pAudioCodec == 0) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(avcodec_open(_audio_ctx, pAudioCodec)<0) {
|
if(avcodec_open(_audio_ctx, pAudioCodec)<0) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
|
_length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
|
||||||
_can_seek = true;
|
_can_seek = true;
|
||||||
_can_seek_fast = true;
|
_can_seek_fast = true;
|
||||||
|
|
||||||
_packet = new AVPacket;
|
_packet = new AVPacket;
|
||||||
_buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE / 2;
|
_buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE / 2;
|
||||||
_buffer_alloc = new PN_int16[_buffer_size + 128];
|
_buffer_alloc = new PN_int16[_buffer_size + 128];
|
||||||
if ((_packet == 0)||(_buffer_alloc == 0)) {
|
if ((_packet == 0)||(_buffer_alloc == 0)) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memset(_packet, 0, sizeof(AVPacket));
|
memset(_packet, 0, sizeof(AVPacket));
|
||||||
|
|
||||||
// Align the buffer to a 16-byte boundary
|
// Align the buffer to a 16-byte boundary
|
||||||
// The ffmpeg codec likes this, because it uses SSE/SSE2.
|
// The ffmpeg codec likes this, because it uses SSE/SSE2.
|
||||||
_buffer = _buffer_alloc;
|
_buffer = _buffer_alloc;
|
||||||
while (((size_t)_buffer) & 15) {
|
while (((size_t)_buffer) & 15) {
|
||||||
_buffer += 1;
|
_buffer += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch_packet();
|
fetch_packet();
|
||||||
_initial_dts = _packet->dts;
|
_initial_dts = _packet->dts;
|
||||||
_last_seek = 0;
|
_last_seek = 0;
|
||||||
_samples_read = 0;
|
_samples_read = 0;
|
||||||
_buffer_head = 0;
|
_buffer_head = 0;
|
||||||
_buffer_tail = 0;
|
_buffer_tail = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegAudioCursor::Destructor
|
// Function: FfmpegAudioCursor::Destructor
|
||||||
// Access: Protected, Virtual
|
// Access: Protected, Virtual
|
||||||
// Description: xxx
|
// Description: xxx
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
FfmpegAudioCursor::
|
FfmpegAudioCursor::
|
||||||
~FfmpegAudioCursor() {
|
~FfmpegAudioCursor() {
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegAudioCursor::cleanup
|
// Function: FfmpegAudioCursor::cleanup
|
||||||
// Access: Public
|
// Access: Public
|
||||||
// Description: Reset to a standard inactive state.
|
// Description: Reset to a standard inactive state.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void FfmpegAudioCursor::
|
void FfmpegAudioCursor::
|
||||||
cleanup() {
|
cleanup() {
|
||||||
if (_packet) {
|
if (_packet) {
|
||||||
if (_packet->data) {
|
if (_packet->data) {
|
||||||
av_free_packet(_packet);
|
av_free_packet(_packet);
|
||||||
}
|
}
|
||||||
delete _packet;
|
delete _packet;
|
||||||
_packet = 0;
|
_packet = 0;
|
||||||
}
|
}
|
||||||
if (_buffer_alloc) {
|
if (_buffer_alloc) {
|
||||||
delete[] _buffer_alloc;
|
delete[] _buffer_alloc;
|
||||||
_buffer_alloc = 0;
|
_buffer_alloc = 0;
|
||||||
_buffer = 0;
|
_buffer = 0;
|
||||||
}
|
}
|
||||||
if ((_audio_ctx)&&(_audio_ctx->codec)) {
|
if ((_audio_ctx)&&(_audio_ctx->codec)) {
|
||||||
avcodec_close(_audio_ctx);
|
avcodec_close(_audio_ctx);
|
||||||
}
|
}
|
||||||
_audio_ctx = 0;
|
_audio_ctx = 0;
|
||||||
if (_format_ctx) {
|
if (_format_ctx) {
|
||||||
av_close_input_file(_format_ctx);
|
av_close_input_file(_format_ctx);
|
||||||
_format_ctx = 0;
|
_format_ctx = 0;
|
||||||
}
|
}
|
||||||
_audio_ctx = 0;
|
_audio_ctx = 0;
|
||||||
_audio_index = -1;
|
_audio_index = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegAudioCursor::fetch_packet
|
// Function: FfmpegAudioCursor::fetch_packet
|
||||||
// Access: Protected
|
// Access: Protected
|
||||||
// Description: Fetches an audio packet and stores it in the
|
// Description: Fetches an audio packet and stores it in the
|
||||||
// packet buffer. Also sets packet_size and packet_data.
|
// packet buffer. Also sets packet_size and packet_data.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void FfmpegAudioCursor::
|
void FfmpegAudioCursor::
|
||||||
fetch_packet() {
|
fetch_packet() {
|
||||||
if (_packet->data) {
|
if (_packet->data) {
|
||||||
av_free_packet(_packet);
|
av_free_packet(_packet);
|
||||||
}
|
}
|
||||||
while (av_read_frame(_format_ctx, _packet) >= 0) {
|
while (av_read_frame(_format_ctx, _packet) >= 0) {
|
||||||
if (_packet->stream_index == _audio_index) {
|
if (_packet->stream_index == _audio_index) {
|
||||||
_packet_size = _packet->size;
|
_packet_size = _packet->size;
|
||||||
_packet_data = _packet->data;
|
_packet_data = _packet->data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
av_free_packet(_packet);
|
av_free_packet(_packet);
|
||||||
}
|
}
|
||||||
_packet->data = 0;
|
_packet->data = 0;
|
||||||
_packet_size = 0;
|
_packet_size = 0;
|
||||||
_packet_data = 0;
|
_packet_data = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegAudioCursor::reload_buffer
|
// Function: FfmpegAudioCursor::reload_buffer
|
||||||
// Access: Protected
|
// Access: Protected
|
||||||
// Description: Reloads the audio buffer by decoding audio packets
|
// Description: Reloads the audio buffer by decoding audio packets
|
||||||
// until one of those audio packets finally yields
|
// until one of those audio packets finally yields
|
||||||
// some samples. If we encounter the end of the
|
// some samples. If we encounter the end of the
|
||||||
// stream, we synthesize silence.
|
// stream, we synthesize silence.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void FfmpegAudioCursor::
|
void FfmpegAudioCursor::
|
||||||
reload_buffer() {
|
reload_buffer() {
|
||||||
|
|
||||||
|
|
||||||
while (_buffer_head == _buffer_tail) {
|
while (_buffer_head == _buffer_tail) {
|
||||||
// If we're out of packets, generate silence.
|
// If we're out of packets, generate silence.
|
||||||
if (_packet->data == 0) {
|
if (_packet->data == 0) {
|
||||||
_buffer_head = 0;
|
_buffer_head = 0;
|
||||||
_buffer_tail = _buffer_size;
|
_buffer_tail = _buffer_size;
|
||||||
memset(_buffer, 0, _buffer_size * 2);
|
memset(_buffer, 0, _buffer_size * 2);
|
||||||
return;
|
return;
|
||||||
} else if (_packet_size > 0) {
|
} else if (_packet_size > 0) {
|
||||||
int bufsize = _buffer_size * 2;
|
int bufsize = _buffer_size * 2;
|
||||||
#if LIBAVCODEC_VERSION_INT < 3414272
|
#if LIBAVCODEC_VERSION_INT < 3414272
|
||||||
#if LIBAVCODEC_VERSION_INT < 3349504
|
#if LIBAVCODEC_VERSION_INT < 3349504
|
||||||
int len = avcodec_decode_audio(_audio_ctx, _buffer, &bufsize,
|
int len = avcodec_decode_audio(_audio_ctx, _buffer, &bufsize,
|
||||||
_packet_data, _packet_size);
|
_packet_data, _packet_size);
|
||||||
movies_debug("avcodec_decode_audio returned " << len);
|
movies_debug("avcodec_decode_audio returned " << len);
|
||||||
#else
|
#else
|
||||||
int len = avcodec_decode_audio2(_audio_ctx, _buffer, &bufsize,
|
int len = avcodec_decode_audio2(_audio_ctx, _buffer, &bufsize,
|
||||||
_packet_data, _packet_size);
|
_packet_data, _packet_size);
|
||||||
movies_debug("avcodec_decode_audio2 returned " << len);
|
movies_debug("avcodec_decode_audio2 returned " << len);
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
AVPacket pkt;
|
AVPacket pkt;
|
||||||
av_init_packet(&pkt);
|
av_init_packet(&pkt);
|
||||||
pkt.data = _packet_data;
|
pkt.data = _packet_data;
|
||||||
pkt.size = _packet_size;
|
pkt.size = _packet_size;
|
||||||
int len = avcodec_decode_audio3(_audio_ctx, _buffer, &bufsize, &pkt);
|
int len = avcodec_decode_audio3(_audio_ctx, _buffer, &bufsize, &pkt);
|
||||||
movies_debug("avcodec_decode_audio3 returned " << len);
|
movies_debug("avcodec_decode_audio3 returned " << len);
|
||||||
av_free_packet(&pkt); // Not sure about this
|
av_free_packet(&pkt); // Not sure about this
|
||||||
#endif
|
#endif
|
||||||
if (len <= 0) {
|
if (len <= 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_packet_data += len;
|
_packet_data += len;
|
||||||
_packet_size -= len;
|
_packet_size -= len;
|
||||||
if (bufsize > 0) {
|
if (bufsize > 0) {
|
||||||
_buffer_head = 0;
|
_buffer_head = 0;
|
||||||
_buffer_tail = (bufsize/2);
|
_buffer_tail = (bufsize/2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fetch_packet();
|
fetch_packet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegAudioCursor::seek
|
// Function: FfmpegAudioCursor::seek
|
||||||
// Access: Protected
|
// Access: Protected
|
||||||
// Description: Seeks to a target location. Afterward, the
|
// Description: Seeks to a target location. Afterward, the
|
||||||
// packet_time is guaranteed to be less than or
|
// packet_time is guaranteed to be less than or
|
||||||
// equal to the specified time.
|
// equal to the specified time.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void FfmpegAudioCursor::
|
void FfmpegAudioCursor::
|
||||||
seek(double t) {
|
seek(double t) {
|
||||||
PN_int64 target_ts = (PN_int64)(t / _audio_timebase);
|
PN_int64 target_ts = (PN_int64)(t / _audio_timebase);
|
||||||
if (target_ts < (PN_int64)(_initial_dts)) {
|
if (target_ts < (PN_int64)(_initial_dts)) {
|
||||||
// Attempts to seek before the first packet will fail.
|
// Attempts to seek before the first packet will fail.
|
||||||
target_ts = _initial_dts;
|
target_ts = _initial_dts;
|
||||||
}
|
}
|
||||||
if (av_seek_frame(_format_ctx, _audio_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
|
if (av_seek_frame(_format_ctx, _audio_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
|
||||||
movies_cat.error() << "Seek failure. Shutting down movie.\n";
|
movies_cat.error() << "Seek failure. Shutting down movie.\n";
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
avcodec_close(_audio_ctx);
|
avcodec_close(_audio_ctx);
|
||||||
AVCodec *pAudioCodec=avcodec_find_decoder(_audio_ctx->codec_id);
|
AVCodec *pAudioCodec=avcodec_find_decoder(_audio_ctx->codec_id);
|
||||||
if(pAudioCodec == 0) {
|
if(pAudioCodec == 0) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(avcodec_open(_audio_ctx, pAudioCodec)<0) {
|
if(avcodec_open(_audio_ctx, pAudioCodec)<0) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_buffer_head = 0;
|
_buffer_head = 0;
|
||||||
_buffer_tail = 0;
|
_buffer_tail = 0;
|
||||||
fetch_packet();
|
fetch_packet();
|
||||||
double ts = _packet->dts * _audio_timebase;
|
double ts = _packet->dts * _audio_timebase;
|
||||||
if (t > ts) {
|
if (t > ts) {
|
||||||
int skip = (int)((t-ts) * _audio_rate);
|
int skip = (int)((t-ts) * _audio_rate);
|
||||||
read_samples(skip, 0);
|
read_samples(skip, 0);
|
||||||
}
|
}
|
||||||
_last_seek = t;
|
_last_seek = t;
|
||||||
_samples_read = 0;
|
_samples_read = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegAudioCursor::read_samples
|
// Function: FfmpegAudioCursor::read_samples
|
||||||
// Access: Public, Virtual
|
// Access: Public, Virtual
|
||||||
// Description: Read audio samples from the stream. N is the
|
// Description: Read audio samples from the stream. N is the
|
||||||
// number of samples you wish to read. Your buffer
|
// number of samples you wish to read. Your buffer
|
||||||
// must be equal in size to N * channels.
|
// must be equal in size to N * channels.
|
||||||
// Multiple-channel audio will be interleaved.
|
// Multiple-channel audio will be interleaved.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void FfmpegAudioCursor::
|
void FfmpegAudioCursor::
|
||||||
read_samples(int n, PN_int16 *data) {
|
read_samples(int n, PN_int16 *data) {
|
||||||
|
|
||||||
//movies_debug("here!!! FfmpegAudioCursor n="<<n);
|
//movies_debug("here!!! FfmpegAudioCursor n="<<n);
|
||||||
|
|
||||||
int desired = n * _audio_channels;
|
int desired = n * _audio_channels;
|
||||||
|
|
||||||
// give up after 100 tries to fetch data
|
// give up after 100 tries to fetch data
|
||||||
int give_up_after = 100;
|
int give_up_after = 100;
|
||||||
|
|
||||||
while (desired && give_up_after > 0) {
|
while (desired && give_up_after > 0) {
|
||||||
|
|
||||||
if (_buffer_head == _buffer_tail) {
|
if (_buffer_head == _buffer_tail) {
|
||||||
reload_buffer();
|
reload_buffer();
|
||||||
give_up_after --;
|
give_up_after --;
|
||||||
movies_debug("reload_buffer will give up in "<<give_up_after);
|
movies_debug("reload_buffer will give up in "<<give_up_after);
|
||||||
}
|
}
|
||||||
int available = _buffer_tail - _buffer_head;
|
int available = _buffer_tail - _buffer_head;
|
||||||
int ncopy = (desired > available) ? available : desired;
|
int ncopy = (desired > available) ? available : desired;
|
||||||
if (ncopy) {
|
if (ncopy) {
|
||||||
if (data != 0) {
|
if (data != 0) {
|
||||||
memcpy(data, _buffer + _buffer_head, ncopy * 2);
|
memcpy(data, _buffer + _buffer_head, ncopy * 2);
|
||||||
data += ncopy;
|
data += ncopy;
|
||||||
}
|
}
|
||||||
desired -= ncopy;
|
desired -= ncopy;
|
||||||
_buffer_head += ncopy;
|
_buffer_head += ncopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
_samples_read += n;
|
_samples_read += n;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#endif // HAVE_FFMPEG
|
#endif // HAVE_FFMPEG
|
||||||
|
@ -1,384 +1,384 @@
|
|||||||
// Filename: ffmpegVideoCursor.cxx
|
// Filename: ffmpegVideoCursor.cxx
|
||||||
// Created by: jyelon (01Aug2007)
|
// Created by: jyelon (01Aug2007)
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// PANDA 3D SOFTWARE
|
// PANDA 3D SOFTWARE
|
||||||
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||||
//
|
//
|
||||||
// All use of this software is subject to the terms of the revised BSD
|
// 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
|
// license. You should have received a copy of this license along
|
||||||
// with this source code in a file named "LICENSE."
|
// with this source code in a file named "LICENSE."
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#ifdef HAVE_FFMPEG
|
#ifdef HAVE_FFMPEG
|
||||||
|
|
||||||
#include "ffmpegVideoCursor.h"
|
#include "ffmpegVideoCursor.h"
|
||||||
#include "config_movies.h"
|
#include "config_movies.h"
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "libavcodec/avcodec.h"
|
#include "libavcodec/avcodec.h"
|
||||||
#include "libavformat/avformat.h"
|
#include "libavformat/avformat.h"
|
||||||
#ifdef HAVE_SWSCALE
|
#ifdef HAVE_SWSCALE
|
||||||
#include "libswscale/swscale.h"
|
#include "libswscale/swscale.h"
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#include "pStatCollector.h"
|
#include "pStatCollector.h"
|
||||||
#include "pStatTimer.h"
|
#include "pStatTimer.h"
|
||||||
|
|
||||||
TypeHandle FfmpegVideoCursor::_type_handle;
|
TypeHandle FfmpegVideoCursor::_type_handle;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegVideoCursor::Constructor
|
// Function: FfmpegVideoCursor::Constructor
|
||||||
// Access: Public
|
// Access: Public
|
||||||
// Description: xxx
|
// Description: xxx
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
FfmpegVideoCursor::
|
FfmpegVideoCursor::
|
||||||
FfmpegVideoCursor(FfmpegVideo *src) :
|
FfmpegVideoCursor(FfmpegVideo *src) :
|
||||||
MovieVideoCursor(src),
|
MovieVideoCursor(src),
|
||||||
_filename(src->_filename),
|
_filename(src->_filename),
|
||||||
_format_ctx(0),
|
_format_ctx(0),
|
||||||
_video_index(-1),
|
_video_index(-1),
|
||||||
_video_ctx(0),
|
_video_ctx(0),
|
||||||
_frame(0),
|
_frame(0),
|
||||||
_frame_out(0),
|
_frame_out(0),
|
||||||
_packet(0),
|
_packet(0),
|
||||||
_min_fseek(3.0)
|
_min_fseek(3.0)
|
||||||
{
|
{
|
||||||
string url = "pandavfs:";
|
string url = "pandavfs:";
|
||||||
url += _filename;
|
url += _filename;
|
||||||
if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
|
if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (av_find_stream_info(_format_ctx)<0) {
|
if (av_find_stream_info(_format_ctx)<0) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the video stream
|
// Find the video stream
|
||||||
for(int i=0; i<_format_ctx->nb_streams; i++) {
|
for(int i=0; i<_format_ctx->nb_streams; i++) {
|
||||||
if(_format_ctx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
|
if(_format_ctx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
|
||||||
_video_index = i;
|
_video_index = i;
|
||||||
_video_ctx = _format_ctx->streams[i]->codec;
|
_video_ctx = _format_ctx->streams[i]->codec;
|
||||||
_video_timebase = av_q2d(_format_ctx->streams[i]->time_base);
|
_video_timebase = av_q2d(_format_ctx->streams[i]->time_base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_video_ctx == 0) {
|
if (_video_ctx == 0) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id);
|
AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id);
|
||||||
if(pVideoCodec == 0) {
|
if(pVideoCodec == 0) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(avcodec_open(_video_ctx, pVideoCodec)<0) {
|
if(avcodec_open(_video_ctx, pVideoCodec)<0) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_size_x = _video_ctx->width;
|
_size_x = _video_ctx->width;
|
||||||
_size_y = _video_ctx->height;
|
_size_y = _video_ctx->height;
|
||||||
_num_components = 3; // Don't know how to implement RGBA movies yet.
|
_num_components = 3; // Don't know how to implement RGBA movies yet.
|
||||||
_length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
|
_length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
|
||||||
_can_seek = true;
|
_can_seek = true;
|
||||||
_can_seek_fast = true;
|
_can_seek_fast = true;
|
||||||
|
|
||||||
_packet = new AVPacket;
|
_packet = new AVPacket;
|
||||||
_frame = avcodec_alloc_frame();
|
_frame = avcodec_alloc_frame();
|
||||||
_frame_out = avcodec_alloc_frame();
|
_frame_out = avcodec_alloc_frame();
|
||||||
if ((_packet == 0)||(_frame == 0)||(_frame_out == 0)) {
|
if ((_packet == 0)||(_frame == 0)||(_frame_out == 0)) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memset(_packet, 0, sizeof(AVPacket));
|
memset(_packet, 0, sizeof(AVPacket));
|
||||||
|
|
||||||
fetch_packet(0.0);
|
fetch_packet(0.0);
|
||||||
_initial_dts = _packet->dts;
|
_initial_dts = _packet->dts;
|
||||||
_packet_time = 0.0;
|
_packet_time = 0.0;
|
||||||
_last_start = -1.0;
|
_last_start = -1.0;
|
||||||
_next_start = 0.0;
|
_next_start = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegVideoCursor::Destructor
|
// Function: FfmpegVideoCursor::Destructor
|
||||||
// Access: Public
|
// Access: Public
|
||||||
// Description: xxx
|
// Description: xxx
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
FfmpegVideoCursor::
|
FfmpegVideoCursor::
|
||||||
~FfmpegVideoCursor() {
|
~FfmpegVideoCursor() {
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegVideoCursor::cleanup
|
// Function: FfmpegVideoCursor::cleanup
|
||||||
// Access: Public
|
// Access: Public
|
||||||
// Description: Reset to a standard inactive state.
|
// Description: Reset to a standard inactive state.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void FfmpegVideoCursor::
|
void FfmpegVideoCursor::
|
||||||
cleanup() {
|
cleanup() {
|
||||||
if (_frame) {
|
if (_frame) {
|
||||||
av_free(_frame);
|
av_free(_frame);
|
||||||
_frame = 0;
|
_frame = 0;
|
||||||
}
|
}
|
||||||
if (_frame_out) {
|
if (_frame_out) {
|
||||||
_frame_out->data[0] = 0;
|
_frame_out->data[0] = 0;
|
||||||
av_free(_frame_out);
|
av_free(_frame_out);
|
||||||
_frame_out = 0;
|
_frame_out = 0;
|
||||||
}
|
}
|
||||||
if (_packet) {
|
if (_packet) {
|
||||||
if (_packet->data) {
|
if (_packet->data) {
|
||||||
av_free_packet(_packet);
|
av_free_packet(_packet);
|
||||||
}
|
}
|
||||||
delete _packet;
|
delete _packet;
|
||||||
_packet = 0;
|
_packet = 0;
|
||||||
}
|
}
|
||||||
if ((_video_ctx)&&(_video_ctx->codec)) {
|
if ((_video_ctx)&&(_video_ctx->codec)) {
|
||||||
avcodec_close(_video_ctx);
|
avcodec_close(_video_ctx);
|
||||||
}
|
}
|
||||||
_video_ctx = 0;
|
_video_ctx = 0;
|
||||||
if (_format_ctx) {
|
if (_format_ctx) {
|
||||||
av_close_input_file(_format_ctx);
|
av_close_input_file(_format_ctx);
|
||||||
_format_ctx = 0;
|
_format_ctx = 0;
|
||||||
}
|
}
|
||||||
_video_ctx = 0;
|
_video_ctx = 0;
|
||||||
_video_index = -1;
|
_video_index = -1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegVideoCursor::export_frame
|
// Function: FfmpegVideoCursor::export_frame
|
||||||
// Access: Public, Virtual
|
// Access: Public, Virtual
|
||||||
// Description: Exports the contents of the frame buffer into the
|
// Description: Exports the contents of the frame buffer into the
|
||||||
// user's target buffer.
|
// user's target buffer.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
static PStatCollector export_frame_collector("*:FFMPEG Convert Video to BGR");
|
static PStatCollector export_frame_collector("*:FFMPEG Convert Video to BGR");
|
||||||
void FfmpegVideoCursor::
|
void FfmpegVideoCursor::
|
||||||
export_frame(unsigned char *data, bool bgra, int bufx) {
|
export_frame(unsigned char *data, bool bgra, int bufx) {
|
||||||
PStatTimer timer(export_frame_collector);
|
PStatTimer timer(export_frame_collector);
|
||||||
if (bgra) {
|
if (bgra) {
|
||||||
_frame_out->data[0] = data + ((_size_y - 1) * bufx * 4);
|
_frame_out->data[0] = data + ((_size_y - 1) * bufx * 4);
|
||||||
_frame_out->linesize[0] = bufx * -4;
|
_frame_out->linesize[0] = bufx * -4;
|
||||||
#ifdef HAVE_SWSCALE
|
#ifdef HAVE_SWSCALE
|
||||||
struct SwsContext *convert_ctx = sws_getContext(_size_x, _size_y,
|
struct SwsContext *convert_ctx = sws_getContext(_size_x, _size_y,
|
||||||
_video_ctx->pix_fmt, _size_x, _size_y,
|
_video_ctx->pix_fmt, _size_x, _size_y,
|
||||||
PIX_FMT_BGRA, 2, NULL, NULL, NULL);
|
PIX_FMT_BGRA, 2, NULL, NULL, NULL);
|
||||||
nassertv(convert_ctx != NULL);
|
nassertv(convert_ctx != NULL);
|
||||||
sws_scale(convert_ctx, _frame->data, _frame->linesize,
|
sws_scale(convert_ctx, _frame->data, _frame->linesize,
|
||||||
0, _size_y, _frame_out->data, _frame_out->linesize);
|
0, _size_y, _frame_out->data, _frame_out->linesize);
|
||||||
sws_freeContext(convert_ctx);
|
sws_freeContext(convert_ctx);
|
||||||
#else
|
#else
|
||||||
img_convert((AVPicture *)_frame_out, PIX_FMT_BGRA,
|
img_convert((AVPicture *)_frame_out, PIX_FMT_BGRA,
|
||||||
(AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
|
(AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
_frame_out->data[0] = data + ((_size_y - 1) * bufx * 3);
|
_frame_out->data[0] = data + ((_size_y - 1) * bufx * 3);
|
||||||
_frame_out->linesize[0] = bufx * -3;
|
_frame_out->linesize[0] = bufx * -3;
|
||||||
#ifdef HAVE_SWSCALE
|
#ifdef HAVE_SWSCALE
|
||||||
struct SwsContext *convert_ctx = sws_getContext(_size_x, _size_y,
|
struct SwsContext *convert_ctx = sws_getContext(_size_x, _size_y,
|
||||||
_video_ctx->pix_fmt, _size_x, _size_y,
|
_video_ctx->pix_fmt, _size_x, _size_y,
|
||||||
PIX_FMT_BGR24, 2, NULL, NULL, NULL);
|
PIX_FMT_BGR24, 2, NULL, NULL, NULL);
|
||||||
nassertv(convert_ctx != NULL);
|
nassertv(convert_ctx != NULL);
|
||||||
sws_scale(convert_ctx, _frame->data, _frame->linesize,
|
sws_scale(convert_ctx, _frame->data, _frame->linesize,
|
||||||
0, _size_y, _frame_out->data, _frame_out->linesize);
|
0, _size_y, _frame_out->data, _frame_out->linesize);
|
||||||
sws_freeContext(convert_ctx);
|
sws_freeContext(convert_ctx);
|
||||||
#else
|
#else
|
||||||
img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24,
|
img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24,
|
||||||
(AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
|
(AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegVideoCursor::fetch_packet
|
// Function: FfmpegVideoCursor::fetch_packet
|
||||||
// Access: Protected
|
// Access: Protected
|
||||||
// Description: Fetches a video packet and stores it in the
|
// Description: Fetches a video packet and stores it in the
|
||||||
// packet buffer. Sets packet_time to the packet's
|
// packet buffer. Sets packet_time to the packet's
|
||||||
// timestamp. If a packet could not be read, the
|
// timestamp. If a packet could not be read, the
|
||||||
// packet is cleared and the packet_time is set to
|
// packet is cleared and the packet_time is set to
|
||||||
// the specified default value.
|
// the specified default value.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void FfmpegVideoCursor::
|
void FfmpegVideoCursor::
|
||||||
fetch_packet(double default_time) {
|
fetch_packet(double default_time) {
|
||||||
if (_packet->data) {
|
if (_packet->data) {
|
||||||
av_free_packet(_packet);
|
av_free_packet(_packet);
|
||||||
}
|
}
|
||||||
while (av_read_frame(_format_ctx, _packet) >= 0) {
|
while (av_read_frame(_format_ctx, _packet) >= 0) {
|
||||||
if (_packet->stream_index == _video_index) {
|
if (_packet->stream_index == _video_index) {
|
||||||
_packet_time = _packet->dts * _video_timebase;
|
_packet_time = _packet->dts * _video_timebase;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
av_free_packet(_packet);
|
av_free_packet(_packet);
|
||||||
}
|
}
|
||||||
_packet->data = 0;
|
_packet->data = 0;
|
||||||
_packet_time = default_time;
|
_packet_time = default_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegVideoCursor::fetch_frame
|
// Function: FfmpegVideoCursor::fetch_frame
|
||||||
// Access: Protected
|
// Access: Protected
|
||||||
// Description: Fetches a frame from the stream and stores it in
|
// Description: Fetches a frame from the stream and stores it in
|
||||||
// the frame buffer. Sets last_start and next_start
|
// the frame buffer. Sets last_start and next_start
|
||||||
// to indicate the extents of the frame.
|
// to indicate the extents of the frame.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void FfmpegVideoCursor::
|
void FfmpegVideoCursor::
|
||||||
fetch_frame() {
|
fetch_frame() {
|
||||||
int finished = 0;
|
int finished = 0;
|
||||||
_last_start = _packet_time;
|
_last_start = _packet_time;
|
||||||
while (!finished && _packet->data) {
|
while (!finished && _packet->data) {
|
||||||
#if LIBAVCODEC_VERSION_INT < 3414272
|
#if LIBAVCODEC_VERSION_INT < 3414272
|
||||||
avcodec_decode_video(_video_ctx, _frame,
|
avcodec_decode_video(_video_ctx, _frame,
|
||||||
&finished, _packet->data, _packet->size);
|
&finished, _packet->data, _packet->size);
|
||||||
#else
|
#else
|
||||||
avcodec_decode_video2(_video_ctx, _frame, &finished, _packet);
|
avcodec_decode_video2(_video_ctx, _frame, &finished, _packet);
|
||||||
#endif
|
#endif
|
||||||
fetch_packet(_last_start + 1.0);
|
fetch_packet(_last_start + 1.0);
|
||||||
}
|
}
|
||||||
_next_start = _packet_time;
|
_next_start = _packet_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegVideoCursor::seek
|
// Function: FfmpegVideoCursor::seek
|
||||||
// Access: Protected
|
// Access: Protected
|
||||||
// Description: Seeks to a target location. Afterward, the
|
// Description: Seeks to a target location. Afterward, the
|
||||||
// packet_time is guaranteed to be less than or
|
// packet_time is guaranteed to be less than or
|
||||||
// equal to the specified time.
|
// equal to the specified time.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void FfmpegVideoCursor::
|
void FfmpegVideoCursor::
|
||||||
seek(double t) {
|
seek(double t) {
|
||||||
PN_int64 target_ts = (PN_int64)(t / _video_timebase);
|
PN_int64 target_ts = (PN_int64)(t / _video_timebase);
|
||||||
if (target_ts < (PN_int64)(_initial_dts)) {
|
if (target_ts < (PN_int64)(_initial_dts)) {
|
||||||
// Attempts to seek before the first packet will fail.
|
// Attempts to seek before the first packet will fail.
|
||||||
target_ts = _initial_dts;
|
target_ts = _initial_dts;
|
||||||
}
|
}
|
||||||
if (av_seek_frame(_format_ctx, _video_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
|
if (av_seek_frame(_format_ctx, _video_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
|
||||||
if (t >= _packet_time) {
|
if (t >= _packet_time) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
movies_cat.error() << "Seek failure. Shutting down movie.\n";
|
movies_cat.error() << "Seek failure. Shutting down movie.\n";
|
||||||
cleanup();
|
cleanup();
|
||||||
_packet_time = t;
|
_packet_time = t;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
avcodec_close(_video_ctx);
|
avcodec_close(_video_ctx);
|
||||||
AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id);
|
AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id);
|
||||||
if(pVideoCodec == 0) {
|
if(pVideoCodec == 0) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(avcodec_open(_video_ctx, pVideoCodec)<0) {
|
if(avcodec_open(_video_ctx, pVideoCodec)<0) {
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fetch_packet(t);
|
fetch_packet(t);
|
||||||
if (_packet_time > t) {
|
if (_packet_time > t) {
|
||||||
_packet_time = t;
|
_packet_time = t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegVideoCursor::fetch_time
|
// Function: FfmpegVideoCursor::fetch_time
|
||||||
// Access: Public, Virtual
|
// Access: Public, Virtual
|
||||||
// Description: Advance until the specified time is in the
|
// Description: Advance until the specified time is in the
|
||||||
// export buffer.
|
// export buffer.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void FfmpegVideoCursor::
|
void FfmpegVideoCursor::
|
||||||
fetch_time(double time) {
|
fetch_time(double time) {
|
||||||
if (time < _last_start) {
|
if (time < _last_start) {
|
||||||
// Time is in the past.
|
// Time is in the past.
|
||||||
seek(time);
|
seek(time);
|
||||||
while (_packet_time <= time) {
|
while (_packet_time <= time) {
|
||||||
fetch_frame();
|
fetch_frame();
|
||||||
}
|
}
|
||||||
} else if (time < _next_start) {
|
} else if (time < _next_start) {
|
||||||
// Time is in the present: already have the frame.
|
// Time is in the present: already have the frame.
|
||||||
} else if (time < _next_start + _min_fseek) {
|
} else if (time < _next_start + _min_fseek) {
|
||||||
// Time is in the near future.
|
// Time is in the near future.
|
||||||
while ((_packet_time <= time) && (_packet->data)) {
|
while ((_packet_time <= time) && (_packet->data)) {
|
||||||
fetch_frame();
|
fetch_frame();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Time is in the far future. Seek forward, then read.
|
// Time is in the far future. Seek forward, then read.
|
||||||
// There's a danger here: because keyframes are spaced
|
// There's a danger here: because keyframes are spaced
|
||||||
// unpredictably, trying to seek forward could actually
|
// unpredictably, trying to seek forward could actually
|
||||||
// move us backward in the stream! This must be avoided.
|
// move us backward in the stream! This must be avoided.
|
||||||
// So the rule is, try the seek. If it hurts us by moving
|
// So the rule is, try the seek. If it hurts us by moving
|
||||||
// us backward, we increase the minimum threshold distance
|
// us backward, we increase the minimum threshold distance
|
||||||
// for forward-seeking in the future.
|
// for forward-seeking in the future.
|
||||||
|
|
||||||
double base = _packet_time;
|
double base = _packet_time;
|
||||||
seek(time);
|
seek(time);
|
||||||
if (_packet_time < base) {
|
if (_packet_time < base) {
|
||||||
_min_fseek += (base - _packet_time);
|
_min_fseek += (base - _packet_time);
|
||||||
}
|
}
|
||||||
while (_packet_time <= time) {
|
while (_packet_time <= time) {
|
||||||
fetch_frame();
|
fetch_frame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegVideoCursor::fetch_into_texture
|
// Function: FfmpegVideoCursor::fetch_into_texture
|
||||||
// Access: Public, Virtual
|
// Access: Public, Virtual
|
||||||
// Description: See MovieVideoCursor::fetch_into_texture.
|
// Description: See MovieVideoCursor::fetch_into_texture.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
static PStatCollector fetch_into_texture_pcollector("*:FFMPEG Video Decoding");
|
static PStatCollector fetch_into_texture_pcollector("*:FFMPEG Video Decoding");
|
||||||
void FfmpegVideoCursor::
|
void FfmpegVideoCursor::
|
||||||
fetch_into_texture(double time, Texture *t, int page) {
|
fetch_into_texture(double time, Texture *t, int page) {
|
||||||
PStatTimer timer(fetch_into_texture_pcollector);
|
PStatTimer timer(fetch_into_texture_pcollector);
|
||||||
|
|
||||||
nassertv(t->get_x_size() >= size_x());
|
nassertv(t->get_x_size() >= size_x());
|
||||||
nassertv(t->get_y_size() >= size_y());
|
nassertv(t->get_y_size() >= size_y());
|
||||||
nassertv((t->get_num_components() == 3) || (t->get_num_components() == 4));
|
nassertv((t->get_num_components() == 3) || (t->get_num_components() == 4));
|
||||||
nassertv(t->get_component_width() == 1);
|
nassertv(t->get_component_width() == 1);
|
||||||
nassertv(page < t->get_z_size());
|
nassertv(page < t->get_z_size());
|
||||||
|
|
||||||
PTA_uchar img = t->modify_ram_image();
|
PTA_uchar img = t->modify_ram_image();
|
||||||
|
|
||||||
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
|
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
|
||||||
|
|
||||||
// If there was an error at any point, synthesize black.
|
// If there was an error at any point, synthesize black.
|
||||||
if (_format_ctx==0) {
|
if (_format_ctx==0) {
|
||||||
if (data) {
|
if (data) {
|
||||||
memset(data,0,t->get_x_size() * t->get_y_size() * t->get_num_components());
|
memset(data,0,t->get_x_size() * t->get_y_size() * t->get_num_components());
|
||||||
}
|
}
|
||||||
_last_start = time;
|
_last_start = time;
|
||||||
_next_start = time + 1.0;
|
_next_start = time + 1.0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch_time(time);
|
fetch_time(time);
|
||||||
export_frame(data, (t->get_num_components()==4), t->get_x_size());
|
export_frame(data, (t->get_num_components()==4), t->get_x_size());
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegVideoCursor::fetch_into_buffer
|
// Function: FfmpegVideoCursor::fetch_into_buffer
|
||||||
// Access: Public, Virtual
|
// Access: Public, Virtual
|
||||||
// Description: See MovieVideoCursor::fetch_into_buffer.
|
// Description: See MovieVideoCursor::fetch_into_buffer.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
static PStatCollector fetch_into_buffer_pcollector("*:FFMPEG Video Decoding");
|
static PStatCollector fetch_into_buffer_pcollector("*:FFMPEG Video Decoding");
|
||||||
void FfmpegVideoCursor::
|
void FfmpegVideoCursor::
|
||||||
fetch_into_buffer(double time, unsigned char *data, bool bgra) {
|
fetch_into_buffer(double time, unsigned char *data, bool bgra) {
|
||||||
PStatTimer timer(fetch_into_buffer_pcollector);
|
PStatTimer timer(fetch_into_buffer_pcollector);
|
||||||
|
|
||||||
// If there was an error at any point, synthesize black.
|
// If there was an error at any point, synthesize black.
|
||||||
if (_format_ctx==0) {
|
if (_format_ctx==0) {
|
||||||
if (data) {
|
if (data) {
|
||||||
memset(data,0,size_x()*size_y()*(bgra?4:3));
|
memset(data,0,size_x()*size_y()*(bgra?4:3));
|
||||||
}
|
}
|
||||||
_last_start = time;
|
_last_start = time;
|
||||||
_next_start = time + 1.0;
|
_next_start = time + 1.0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch_time(time);
|
fetch_time(time);
|
||||||
export_frame(data, bgra, _size_x);
|
export_frame(data, bgra, _size_x);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#endif // HAVE_FFMPEG
|
#endif // HAVE_FFMPEG
|
||||||
|
@ -1,176 +1,176 @@
|
|||||||
// Filename: ffmpegVirtualFile.cxx
|
// Filename: ffmpegVirtualFile.cxx
|
||||||
// Created by: jyelon (02Jul07)
|
// Created by: jyelon (02Jul07)
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// PANDA 3D SOFTWARE
|
// PANDA 3D SOFTWARE
|
||||||
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||||
//
|
//
|
||||||
// All use of this software is subject to the terms of the revised BSD
|
// 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
|
// license. You should have received a copy of this license along
|
||||||
// with this source code in a file named "LICENSE."
|
// with this source code in a file named "LICENSE."
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#ifdef HAVE_FFMPEG
|
#ifdef HAVE_FFMPEG
|
||||||
|
|
||||||
#include "pandabase.h"
|
#include "pandabase.h"
|
||||||
#include "config_movies.h"
|
#include "config_movies.h"
|
||||||
#include "ffmpegVirtualFile.h"
|
#include "ffmpegVirtualFile.h"
|
||||||
#include "virtualFileSystem.h"
|
#include "virtualFileSystem.h"
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "libavformat/avio.h"
|
#include "libavformat/avio.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef AVSEEK_SIZE
|
#ifndef AVSEEK_SIZE
|
||||||
#define AVSEEK_SIZE 0x10000
|
#define AVSEEK_SIZE 0x10000
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// These functions need to use C calling conventions.
|
// These functions need to use C calling conventions.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
extern "C" {
|
extern "C" {
|
||||||
static int pandavfs_open(URLContext *h, const char *filename, int flags);
|
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_read(URLContext *h, unsigned char *buf, int size);
|
||||||
static int pandavfs_write(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 PN_int64 pandavfs_seek(URLContext *h, PN_int64 pos, int whence);
|
||||||
static int pandavfs_close(URLContext *h);
|
static int pandavfs_close(URLContext *h);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: pandavfs_open
|
// Function: pandavfs_open
|
||||||
// Access: Static Function
|
// Access: Static Function
|
||||||
// Description: A hook to open a panda VFS file.
|
// Description: A hook to open a panda VFS file.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
static int
|
static int
|
||||||
pandavfs_open(URLContext *h, const char *filename, int flags) {
|
pandavfs_open(URLContext *h, const char *filename, int flags) {
|
||||||
if (flags != 0) {
|
if (flags != 0) {
|
||||||
movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
|
movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
filename += 9; // Skip over "pandavfs:"
|
filename += 9; // Skip over "pandavfs:"
|
||||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||||
istream *s = vfs->open_read_file(filename, true);
|
istream *s = vfs->open_read_file(filename, true);
|
||||||
if (s == 0) {
|
if (s == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
// Test whether seek works.
|
// Test whether seek works.
|
||||||
s->seekg(1, ios::beg);
|
s->seekg(1, ios::beg);
|
||||||
int tel1 = s->tellg();
|
int tel1 = s->tellg();
|
||||||
s->seekg(0, ios::beg);
|
s->seekg(0, ios::beg);
|
||||||
int tel2 = s->tellg();
|
int tel2 = s->tellg();
|
||||||
if (s->fail() || (tel1!=1) || (tel2!=0)) {
|
if (s->fail() || (tel1!=1) || (tel2!=0)) {
|
||||||
movies_cat.error() << "cannot play movie (not seekable): " << h->filename << "\n";
|
movies_cat.error() << "cannot play movie (not seekable): " << h->filename << "\n";
|
||||||
delete s;
|
delete s;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
h->priv_data = s;
|
h->priv_data = s;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: pandavfs_read
|
// Function: pandavfs_read
|
||||||
// Access: Static Function
|
// Access: Static Function
|
||||||
// Description: A hook to read a panda VFS file.
|
// Description: A hook to read a panda VFS file.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
static int
|
static int
|
||||||
pandavfs_read(URLContext *h, unsigned char *buf, int size) {
|
pandavfs_read(URLContext *h, unsigned char *buf, int size) {
|
||||||
istream *s = (istream*)(h->priv_data);
|
istream *s = (istream*)(h->priv_data);
|
||||||
s->read((char*)buf, size);
|
s->read((char*)buf, size);
|
||||||
int gc = s->gcount();
|
int gc = s->gcount();
|
||||||
s->clear();
|
s->clear();
|
||||||
return gc;
|
return gc;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: pandavfs_write
|
// Function: pandavfs_write
|
||||||
// Access: Static Function
|
// Access: Static Function
|
||||||
// Description: A hook to write a panda VFS file.
|
// Description: A hook to write a panda VFS file.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
static int
|
static int
|
||||||
pandavfs_write(URLContext *h, unsigned char *buf, int size) {
|
pandavfs_write(URLContext *h, unsigned char *buf, int size) {
|
||||||
movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
|
movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: pandavfs_seek
|
// Function: pandavfs_seek
|
||||||
// Access: Static Function
|
// Access: Static Function
|
||||||
// Description: A hook to seek a panda VFS file.
|
// Description: A hook to seek a panda VFS file.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
static PN_int64
|
static PN_int64
|
||||||
pandavfs_seek(URLContext *h, PN_int64 pos, int whence) {
|
pandavfs_seek(URLContext *h, PN_int64 pos, int whence) {
|
||||||
istream *s = (istream*)(h->priv_data);
|
istream *s = (istream*)(h->priv_data);
|
||||||
switch(whence) {
|
switch(whence) {
|
||||||
case SEEK_SET: s->seekg(pos, ios::beg); break;
|
case SEEK_SET: s->seekg(pos, ios::beg); break;
|
||||||
case SEEK_CUR: s->seekg(pos, ios::cur); break;
|
case SEEK_CUR: s->seekg(pos, ios::cur); break;
|
||||||
case SEEK_END: s->seekg(pos, ios::end); break;
|
case SEEK_END: s->seekg(pos, ios::end); break;
|
||||||
case AVSEEK_SIZE: {
|
case AVSEEK_SIZE: {
|
||||||
s->seekg(0, ios::cur);
|
s->seekg(0, ios::cur);
|
||||||
int p = s->tellg();
|
int p = s->tellg();
|
||||||
s->seekg(-1, ios::end);
|
s->seekg(-1, ios::end);
|
||||||
int size = s->tellg();
|
int size = s->tellg();
|
||||||
if (size < 0) {
|
if (size < 0) {
|
||||||
movies_cat.error() << "Failed to determine filesize in ffmpegVirtualFile\n";
|
movies_cat.error() << "Failed to determine filesize in ffmpegVirtualFile\n";
|
||||||
s->clear();
|
s->clear();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
size++;
|
size++;
|
||||||
s->seekg(p, ios::beg);
|
s->seekg(p, ios::beg);
|
||||||
s->clear();
|
s->clear();
|
||||||
return size; }
|
return size; }
|
||||||
default:
|
default:
|
||||||
movies_cat.error() << "Illegal parameter to seek in ffmpegVirtualFile\n";
|
movies_cat.error() << "Illegal parameter to seek in ffmpegVirtualFile\n";
|
||||||
s->clear();
|
s->clear();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
s->clear();
|
s->clear();
|
||||||
int tl = s->tellg();
|
int tl = s->tellg();
|
||||||
return tl;
|
return tl;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: pandavfs_close
|
// Function: pandavfs_close
|
||||||
// Access: Static Function
|
// Access: Static Function
|
||||||
// Description: A hook to close a panda VFS file.
|
// Description: A hook to close a panda VFS file.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
static int
|
static int
|
||||||
pandavfs_close(URLContext *h) {
|
pandavfs_close(URLContext *h) {
|
||||||
istream *s = (istream*)(h->priv_data);
|
istream *s = (istream*)(h->priv_data);
|
||||||
delete s;
|
delete s;
|
||||||
h->priv_data = 0;
|
h->priv_data = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegVirtualFile::register_protocol
|
// Function: FfmpegVirtualFile::register_protocol
|
||||||
// Access: Public, Static
|
// Access: Public, Static
|
||||||
// Description: Enables ffmpeg to access panda's VFS.
|
// Description: Enables ffmpeg to access panda's VFS.
|
||||||
//
|
//
|
||||||
// After calling this method, ffmpeg will be
|
// After calling this method, ffmpeg will be
|
||||||
// able to open "URLs" that look like this:
|
// able to open "URLs" that look like this:
|
||||||
//
|
//
|
||||||
// pandavfs:/c/mygame/foo.avi
|
// pandavfs:/c/mygame/foo.avi
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void FfmpegVirtualFile::
|
void FfmpegVirtualFile::
|
||||||
register_protocol() {
|
register_protocol() {
|
||||||
static bool initialized = false;
|
static bool initialized = false;
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
static URLProtocol protocol;
|
static URLProtocol protocol;
|
||||||
protocol.name = "pandavfs";
|
protocol.name = "pandavfs";
|
||||||
protocol.url_open = pandavfs_open;
|
protocol.url_open = pandavfs_open;
|
||||||
protocol.url_read = pandavfs_read;
|
protocol.url_read = pandavfs_read;
|
||||||
protocol.url_write = pandavfs_write;
|
protocol.url_write = pandavfs_write;
|
||||||
protocol.url_seek = pandavfs_seek;
|
protocol.url_seek = pandavfs_seek;
|
||||||
protocol.url_close = pandavfs_close;
|
protocol.url_close = pandavfs_close;
|
||||||
#if LIBAVFORMAT_VERSION_INT < 3415296
|
#if LIBAVFORMAT_VERSION_INT < 3415296
|
||||||
::register_protocol(&protocol);
|
::register_protocol(&protocol);
|
||||||
#else
|
#else
|
||||||
av_register_protocol(&protocol);
|
av_register_protocol(&protocol);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_FFMPEG
|
#endif // HAVE_FFMPEG
|
||||||
|
Loading…
x
Reference in New Issue
Block a user