Last active
May 20, 2022 15:52
-
-
Save YeldhamDev/f2a7efe47979dc972d408e29f8a99f57 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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