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
|
||||
// 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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user