Skip to content

Instantly share code, notes, and snippets.

@YeldhamDev
Last active May 20, 2022 15:52
Show Gist options
  • Save YeldhamDev/f2a7efe47979dc972d408e29f8a99f57 to your computer and use it in GitHub Desktop.
Save YeldhamDev/f2a7efe47979dc972d408e29f8a99f57 to your computer and use it in GitHub Desktop.
#include "ffmpeg_streamer.h"
void FFmpegStreamer::load_path(String p_path, bool p_convert_to_rgb) {
CharString utf8 = p_path.utf8();
const char *cstr = utf8.get_data();
video_player = player_create(cstr, p_convert_to_rgb ? 1 : 0);
Ref<AudioStreamPlaybackFFmpeg> playback = audio_player->get_stream_playback();
playback->set_player(video_player);
first_frame = true;
set_process(true);
}
void FFmpegStreamer::play() {
if (video_player && player_is_playing(video_player) == 0) {
player_play(video_player);
set_process(true);
}
}
void FFmpegStreamer::pause() {
if (video_player && player_is_playing(video_player) == 1) {
player_stop(video_player);
set_process(false);
}
}
bool FFmpegStreamer::is_playing() const {
return player_is_playing(video_player) == 1;
}
Ref<ImageTexture> FFmpegStreamer::get_video_texture() {
return texture;
}
void FFmpegStreamer::set_loop(bool p_enable) {
player_set_loop(video_player, p_enable ? 1 : 0);
}
bool FFmpegStreamer::has_loop() const {
return player_has_loop(video_player) == 1;
}
void FFmpegStreamer::_notification(int p_what) {
if (p_what == NOTIFICATION_PROCESS) {
int state = player_get_state(video_player);
if (state == StateError) {
if (was_loading) {
was_loading = false;
set_process(false);
emit_signal("path_loaded", false);
}
return;
}
else if (state == StateLoading) {
was_loading = true;
}
else if (state == StateReady) {
if (was_loading) {
player_get_video_format(video_player, &width, &height);
data_size = width * height * 3;
was_loading = false;
set_process(false);
emit_signal("path_loaded", true);
return;
}
void *release_ptr = nullptr;
uint8_t* video_data[3];
player_grab_video_frame(video_player, &release_ptr, video_data);
if (release_ptr != nullptr) {
PoolVector<uint8_t> image_data;
image_data.resize(data_size);
memcpy(image_data.write().ptr(), video_data[0], data_size);
image->create(width, height, false, Image::FORMAT_RGB8, image_data);
if (first_frame) {
texture->create_from_image(image);
// FIXME: Implement Audio!
audio_player->play();
first_frame = false;
} else {
texture->set_data(image);
}
player_release_frame(video_player, release_ptr);
}
// FIXME: Implement Audio!
// int frame_size = 0;
// uint8_t *audio_data = nullptr;
// player_grab_audio_frame(video_player, &release_ptr, &audio_data, &frame_size);
//
// if (audio_data != nullptr) {
// player_release_frame(video_player, release_ptr);
// }
}
}
}
void FFmpegStreamer::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_path", "path", "converto_to_rgb"), &FFmpegStreamer::load_path, DEFVAL(true));
ClassDB::bind_method(D_METHOD("play"), &FFmpegStreamer::play);
ClassDB::bind_method(D_METHOD("pause"), &FFmpegStreamer::pause);
ClassDB::bind_method(D_METHOD("is_playing"), &FFmpegStreamer::is_playing);
ClassDB::bind_method(D_METHOD("get_video_texture"), &FFmpegStreamer::get_video_texture);
// ClassDB::bind_method(D_METHOD("get_length"), &FFmpegNode::get_length);
ClassDB::bind_method(D_METHOD("set_loop", "enable"), &FFmpegStreamer::set_loop);
ClassDB::bind_method(D_METHOD("has_loop"), &FFmpegStreamer::has_loop);
// ClassDB::bind_method(D_METHOD("get_playback_position"), &FFmpegNode::get_playback_position);
// ClassDB::bind_method(D_METHOD("seek", "time"), &FFmpegNode::seek);
ADD_SIGNAL(MethodInfo("path_loaded", PropertyInfo(Variant::BOOL, "successful")));
}
FFmpegStreamer::FFmpegStreamer() {
texture = Ref<ImageTexture>(memnew(ImageTexture));
image = Ref<Image>(memnew(Image()));
audio_player = memnew(AudioStreamPlayer);
add_child(audio_player);
audio_stream = Ref<AudioStreamFFmpeg>(memnew(AudioStreamFFmpeg));
audio_player->set_stream(audio_stream);
}
FFmpegStreamer::~FFmpegStreamer() {
if (video_player) {
player_destroy(video_player);
}
}
/*** AudioStreamFFmpeg ***/
Ref<AudioStreamPlayback> AudioStreamFFmpeg::instance_playback() {
Ref<AudioStreamPlaybackFFmpeg> playback;
playback.instance();
playback->base = Ref<AudioStreamFFmpeg>(this);
return playback;
}
String AudioStreamFFmpeg::get_stream_name() const {
return "FFmpeg";
}
void AudioStreamFFmpeg::reset() {
set_position(0);
}
void AudioStreamFFmpeg::set_position(uint64_t p) {
pos = p;
}
void AudioStreamFFmpeg::_bind_methods() {
ClassDB::bind_method(D_METHOD("reset"), &AudioStreamFFmpeg::reset);
ClassDB::bind_method(D_METHOD("get_stream_name"), &AudioStreamFFmpeg::get_stream_name);
}
/*** AudioStreamPlaybackFFmpeg ***/
AudioStreamPlaybackFFmpeg::AudioStreamPlaybackFFmpeg() {
AudioServer::get_singleton()->lock();
audio_buffer = AudioServer::get_singleton()->audio_data_alloc(BUFFER_SIZE);
memset(audio_buffer, 0, BUFFER_SIZE);
AudioServer::get_singleton()->unlock();
}
AudioStreamPlaybackFFmpeg::~AudioStreamPlaybackFFmpeg() {
if (audio_buffer) {
AudioServer::get_singleton()->audio_data_free(audio_buffer);
audio_buffer = nullptr;
}
}
void AudioStreamPlaybackFFmpeg::set_player(MediaPlayerContext *p_player) {
player = p_player;
}
void AudioStreamPlaybackFFmpeg::stop() {
active = false;
base->reset();
}
void AudioStreamPlaybackFFmpeg::start(float p_from_pos) {
seek(p_from_pos);
active = true;
}
void AudioStreamPlaybackFFmpeg::seek(float p_time) {
float max = get_length();
if (p_time < 0) {
p_time = 0;
}
}
void AudioStreamPlaybackFFmpeg::_mix_internal(AudioFrame *p_buffer, int p_frames) {
ERR_FAIL_COND(!active);
if (!active) {
return;
}
void *release_ptr = nullptr;
if (player && player_is_playing(player)) {
int frame_size = 0;
memset(audio_buffer, 0, BUFFER_SIZE);
player_grab_audio_frame(player, &release_ptr, (uint8_t**)audio_buffer, &frame_size);
if (audio_buffer != nullptr) {
// FIXME: Implement Audio!
PoolVector<uint8_t> audio_data;
audio_data.resize(frame_size);
memcpy(audio_data.write().ptr(), audio_buffer, frame_size);
for(int i = 0; i < p_frames; i++) {
float sample = audio_data[i];
p_buffer[i] = AudioFrame(sample, sample);
}
player_release_frame(player, release_ptr);
}
}
}
float AudioStreamPlaybackFFmpeg::get_stream_sampling_rate() {
return float(base->mix_rate);
}
int AudioStreamPlaybackFFmpeg::get_loop_count() const {
return 0;
}
float AudioStreamPlaybackFFmpeg::get_playback_position() const {
return 0.0;
}
float AudioStreamPlaybackFFmpeg::get_length() const {
return 0.0;
}
bool AudioStreamPlaybackFFmpeg::is_playing() const {
return active;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment