77#include <opusfile.h>
88#include <math.h>
99#include "c_utils.h"
10+ #include "libavformat/avformat.h"
1011
1112typedef struct {
1213 int version ;
@@ -687,3 +688,143 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_MediaController_getWaveform(JNI
687688
688689 return result ;
689690}
691+
692+ JNIEXPORT void JNICALL Java_org_telegram_ui_Stories_recorder_FfmpegAudioWaveformLoader_init (JNIEnv * env , jobject obj , jstring pathJStr , jint count ) {
693+ const char * path = (* env )-> GetStringUTFChars (env , pathJStr , 0 );
694+
695+ // Initialize FFmpeg components
696+ av_register_all ();
697+
698+ AVFormatContext * formatContext = avformat_alloc_context ();
699+ if (!formatContext ) {
700+ // Handle error
701+ return ;
702+ }
703+
704+ int res ;
705+ if ((res = avformat_open_input (& formatContext , path , NULL , NULL )) != 0 ) {
706+ LOGD ("avformat_open_input error %s" , av_err2str (res ));
707+ // Handle error
708+ avformat_free_context (formatContext );
709+ return ;
710+ }
711+
712+ if (avformat_find_stream_info (formatContext , NULL ) < 0 ) {
713+ // Handle error
714+ avformat_close_input (& formatContext );
715+ return ;
716+ }
717+
718+ AVCodec * codec = NULL ;
719+ int audioStreamIndex = av_find_best_stream (formatContext , AVMEDIA_TYPE_AUDIO , -1 , -1 , & codec , 0 );
720+ if (audioStreamIndex < 0 ) {
721+ LOGD ("av_find_best_stream error %s" , av_err2str (audioStreamIndex ));
722+ // Handle error
723+ avformat_close_input (& formatContext );
724+ return ;
725+ }
726+
727+ AVCodecContext * codecContext = avcodec_alloc_context3 (codec );
728+ avcodec_parameters_to_context (codecContext , formatContext -> streams [audioStreamIndex ]-> codecpar );
729+
730+ int64_t duration_in_microseconds = formatContext -> duration ;
731+ double duration_in_seconds = (double )duration_in_microseconds / AV_TIME_BASE ;
732+
733+ if (avcodec_open2 (codecContext , codec , NULL ) < 0 ) {
734+ // Handle error
735+ avcodec_free_context (& codecContext );
736+ avformat_close_input (& formatContext );
737+ return ;
738+ }
739+
740+ // Obtain the class and method to callback
741+ jclass cls = (* env )-> GetObjectClass (env , obj );
742+ jmethodID mid = (* env )-> GetMethodID (env , cls , "receiveChunk" , "([SI)V" );
743+
744+ AVFrame * frame = av_frame_alloc ();
745+ AVPacket packet ;
746+
747+ int sampleRate = codecContext -> sample_rate ; // Sample rate from FFmpeg's codec context
748+ int skip = 4 ;
749+ int barWidth = (int ) round ((double ) duration_in_seconds * sampleRate / count / (1 + skip )); // Assuming you have 'duration' and 'count' defined somewhere
750+
751+ short peak = 0 ;
752+ int currentCount = 0 ;
753+ int index = 0 ;
754+ int chunkIndex = 0 ;
755+ short waveformChunkData [32 ]; // Allocate the chunk array
756+
757+ while (av_read_frame (formatContext , & packet ) >= 0 ) {
758+ if (packet .stream_index == audioStreamIndex ) {
759+ // Decode the audio packet
760+ int response = avcodec_send_packet (codecContext , & packet );
761+
762+ while (response >= 0 ) {
763+ response = avcodec_receive_frame (codecContext , frame );
764+ if (response == AVERROR (EAGAIN ) || response == AVERROR_EOF ) {
765+ break ;
766+ } else if (response < 0 ) {
767+ // Handle error
768+ break ;
769+ }
770+
771+ int16_t * samples = (int16_t * ) frame -> data [0 ];
772+ for (int i = 0 ; i < frame -> nb_samples ; i ++ ) {
773+ short value = samples [i ]; // Read the 16-bit PCM sample
774+
775+ if (currentCount >= barWidth ) {
776+ waveformChunkData [index - chunkIndex ] = peak ;
777+ index ++ ;
778+ if (index - chunkIndex >= sizeof (waveformChunkData ) / sizeof (short ) || index >= count ) {
779+ jshortArray waveformData = (* env )-> NewShortArray (env , sizeof (waveformChunkData ) / sizeof (short ));
780+ (* env )-> SetShortArrayRegion (env , waveformData , 0 , sizeof (waveformChunkData ) / sizeof (short ), waveformChunkData );
781+ (* env )-> CallVoidMethod (env , obj , mid , waveformData , sizeof (waveformChunkData ) / sizeof (short ));
782+
783+ // Reset the chunk data
784+ memset (waveformChunkData , 0 , sizeof (waveformChunkData ));
785+ chunkIndex = index ;
786+
787+ // Delete local reference to avoid memory leak
788+ (* env )-> DeleteLocalRef (env , waveformData );
789+ }
790+ peak = 0 ;
791+ currentCount = 0 ;
792+ if (index >= count ) {
793+ break ;
794+ }
795+ }
796+
797+ if (peak < value ) {
798+ peak = value ;
799+ }
800+ currentCount ++ ;
801+
802+ // Skip logic
803+ i += skip ;
804+ if (i >= frame -> nb_samples ) {
805+ break ;
806+ }
807+ }
808+ }
809+ }
810+
811+ av_packet_unref (& packet );
812+
813+ if (index >= count ) {
814+ break ;
815+ }
816+
817+ // Check for stopping flag
818+ jfieldID fid = (* env )-> GetFieldID (env , cls , "running" , "Z" );
819+ jboolean running = (* env )-> GetBooleanField (env , obj , fid );
820+ if (running == JNI_FALSE ) {
821+ break ;
822+ }
823+ }
824+
825+ av_frame_free (& frame );
826+ avcodec_free_context (& codecContext );
827+ avformat_close_input (& formatContext );
828+
829+ (* env )-> ReleaseStringUTFChars (env , pathJStr , path );
830+ }
0 commit comments