From 3280833b97e3b67ea9d778758c4cd653a8453da5 Mon Sep 17 00:00:00 2001
From: Shaji Khan <quotablecode@gmail.com>
Date: Wed, 20 Mar 2024 16:51:18 +0530
Subject: [PATCH] Bento4 muxer

---
 app/src/main/cpp/CMakeLists.txt   |   1 +
 app/src/main/cpp/Engine.cpp       |   1 +
 app/src/main/cpp/MP4.cpp          | 175 ++++++++++++++++++++----------
 app/src/main/cpp/MP4.h            |  39 ++++---
 app/src/main/cpp/Meter.cpp        |  21 +++-
 app/src/main/cpp/Meter.h          |   7 +-
 app/src/main/cpp/logging_macros.h |   1 +
 app/src/main/cpp/utils.h          |   9 ++
 8 files changed, 168 insertions(+), 86 deletions(-)
 create mode 100644 app/src/main/cpp/utils.h

diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt
index 27cc652f..96b4c972 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -122,6 +122,7 @@ target_link_libraries( # Specifies the target library.
         oboe::oboe
         libsndfile
         libopusenc
+        libap4
         libopus
         libmp3lame
         libfaac
diff --git a/app/src/main/cpp/Engine.cpp b/app/src/main/cpp/Engine.cpp
index 99dd4b8d..bb3cd484 100644
--- a/app/src/main/cpp/Engine.cpp
+++ b/app/src/main/cpp/Engine.cpp
@@ -87,6 +87,7 @@ bool Engine::setEffectOn(bool isOn) {
                 }
                  */
 
+                meter->lastRecordedFileName = fileWriter->filename;
                 meter->enable();
                 meter->start();
                 meter->faacInit(mSampleRate, bufferSizeInFrames);
diff --git a/app/src/main/cpp/MP4.cpp b/app/src/main/cpp/MP4.cpp
index 7085edad..e054c4f6 100644
--- a/app/src/main/cpp/MP4.cpp
+++ b/app/src/main/cpp/MP4.cpp
@@ -12,85 +12,140 @@ MP4::MakeDsi(unsigned int sampling_frequency_index, unsigned int channel_configu
     dsi[1] = ((sampling_frequency_index&1)<<7) | (channel_configuration<<3);
 }
 
-void MP4::openFile (std::string _filename) {
-    filename = _filename ;
-    result = AP4_FileByteStream::Create(filename.c_str(), AP4_FileByteStream::STREAM_MODE_WRITE, output);
+void MP4::aacToMP4 () {
+    fclose(tmpfile);
 
+    AP4_Result result;
+    AP4_ByteStream* input = NULL;
+    result = AP4_FileByteStream::Create(tmp.c_str(), AP4_FileByteStream::STREAM_MODE_READ, input);
     if (AP4_FAILED(result)) {
-        LOGE("ERROR: cannot open output (%s) %d\n", filename.c_str(), result);
+        LOGE("ERROR: cannot open input (%s) %d\n", tmp.c_str(), result);
         return ;
     }
 
-    AP4_AacFrame frame;
-    result = parser.FindFrame(frame);
-    if (AP4_SUCCEEDED(result)) {
-        LOGI("AAC frame [%06d]: size = %d, %d kHz, %d ch\n",
-                  sample_count,
-                  frame.m_Info.m_FrameLength,
-                  frame.m_Info.m_SamplingFrequency,
-                  frame.m_Info.m_ChannelConfiguration);
-    } else {
-        HERE LOGE("[mp4] failed to find frame");
-        return;
+    // open the output
+    AP4_ByteStream* output = NULL;
+    result = AP4_FileByteStream::Create(lastRecordedFile.c_str(), AP4_FileByteStream::STREAM_MODE_WRITE, output);
+    if (AP4_FAILED(result)) {
+        LOGE("ERROR: cannot open output (%s) %d\n",lastRecordedFile.c_str(), result);
+        return ;
     }
 
-    initialized = true;
-    sample_table = new AP4_SyntheticSampleTable();
-    AP4_DataBuffer dsi;
-    unsigned char aac_dsi[2];
-    MakeDsi(frame.m_Info.m_SamplingFrequencyIndex, frame.m_Info.m_ChannelConfiguration, aac_dsi);
-    dsi.SetData(aac_dsi, 2);
-
-    AP4_MpegAudioSampleDescription* sample_description =
-            new AP4_MpegAudioSampleDescription(
-                    AP4_OTI_MPEG4_AUDIO,   // object type
-                    frame.m_Info.m_SamplingFrequency,
-                    16,                    // sample size
-                    frame.m_Info.m_ChannelConfiguration,
-                    &dsi,                  // decoder info
-                    6144,                  // buffer size
-                    256000,                // max bitrate
-                    128000);               // average bitrate
-    sample_description_index = sample_table->GetSampleDescriptionCount();
-    sample_table->AddSampleDescription(sample_description);
-    sample_rate = frame.m_Info.m_SamplingFrequency;
-
-    AP4_MemoryByteStream* sample_data = new AP4_MemoryByteStream(frame.m_Info.m_FrameLength);
-    frame.m_Source->ReadBytes(sample_data->UseData(), frame.m_Info.m_FrameLength);
-    sample_table->AddSample(*sample_data, 0, frame.m_Info.m_FrameLength, 1024, sample_description_index, 0, 0, true);
-    sample_data->Release();
-    sample_count++;
-
-    movie = new AP4_Movie();
-    track = new AP4_Track(AP4_Track::TYPE_AUDIO,
-                          sample_table,
-                          0,     // track id
-                          sample_rate,       // movie time scale
-                          sample_count*1024, // track duration
-                          sample_rate,       // media time scale
-                          sample_count*1024, // media duration
-                          "eng", // language
-                          0, 0); // width, height
+    // create a sample table
+    AP4_SyntheticSampleTable* sample_table = new AP4_SyntheticSampleTable();
+
+    // create an ADTS parser
+    AP4_AdtsParser parser;
+    bool           initialized = false;
+    unsigned int   sample_description_index = 0;
+
+    // read from the input, feed, and get AAC frames
+    AP4_UI32     sample_rate = 0;
+    AP4_Cardinal sample_count = 0;
+    bool eos = false;
+    for(;;) {
+        // try to get a frame
+        AP4_AacFrame frame;
+        result = parser.FindFrame(frame);
+        if (AP4_SUCCEEDED(result)) {
+            LOGD("AAC frame [%06d]: size = %d, %d kHz, %d ch\n",
+                      sample_count,
+                      frame.m_Info.m_FrameLength,
+                      frame.m_Info.m_SamplingFrequency,
+                      frame.m_Info.m_ChannelConfiguration);
+            if (!initialized) {
+                initialized = true;
+
+                // create a sample description for our samples
+                AP4_DataBuffer dsi;
+                unsigned char aac_dsi[2];
+                MakeDsi(frame.m_Info.m_SamplingFrequencyIndex, frame.m_Info.m_ChannelConfiguration, aac_dsi);
+                dsi.SetData(aac_dsi, 2);
+                AP4_MpegAudioSampleDescription* sample_description =
+                        new AP4_MpegAudioSampleDescription(
+                                AP4_OTI_MPEG4_AUDIO,   // object type
+                                frame.m_Info.m_SamplingFrequency,
+                                16,                    // sample size
+                                frame.m_Info.m_ChannelConfiguration,
+                                &dsi,                  // decoder info
+                                6144,                  // buffer size
+                                128000,                // max bitrate
+                                128000);               // average bitrate
+                sample_description_index = sample_table->GetSampleDescriptionCount();
+                sample_table->AddSampleDescription(sample_description);
+                sample_rate = frame.m_Info.m_SamplingFrequency;
+            }
+
+            AP4_MemoryByteStream* sample_data = new AP4_MemoryByteStream(frame.m_Info.m_FrameLength);
+            frame.m_Source->ReadBytes(sample_data->UseData(), frame.m_Info.m_FrameLength);
+            sample_table->AddSample(*sample_data, 0, frame.m_Info.m_FrameLength, 1024, sample_description_index, 0, 0, true);
+            sample_data->Release();
+            sample_count++;
+        } else if (!eos) {
+            // read some data and feed the parser
+            AP4_UI08 input_buffer[4096];
+            AP4_Size to_read = parser.GetBytesFree();
+            if (to_read) {
+                AP4_Size bytes_read = 0;
+                if (to_read > sizeof(input_buffer)) to_read = sizeof(input_buffer);
+                result = input->ReadPartial(input_buffer, to_read, bytes_read);
+                if (AP4_SUCCEEDED(result)) {
+                    AP4_Size to_feed = bytes_read;
+                    result = parser.Feed(input_buffer, &to_feed);
+                    if (AP4_FAILED(result)) {
+                        LOGE("ERROR: parser.Feed() failed (%d)\n", result);
+                        return;
+                    }
+                } else {
+                    if (result == AP4_ERROR_EOS) {
+                        eos = true;
+                        parser.Feed(NULL, 0, true);
+                    }
+                }
+            }
+        } else {
+            LOGE ("[mp4] unable to find a frame or do anything !!!");
+            abort();
+            break;
+        }
+    }
 
+    // create a movie
+    AP4_Movie* movie = new AP4_Movie();
+
+    // create an audio track
+    AP4_Track* track = new AP4_Track(AP4_Track::TYPE_AUDIO,
+                                     sample_table,
+                                     0,     // track id
+                                     sample_rate,       // movie time scale
+                                     sample_count*1024, // track duration
+                                     sample_rate,       // media time scale
+                                     sample_count*1024, // media duration
+                                     "eng", // language
+                                     0, 0); // width, height
+
+    // add the track to the movie
     movie->AddTrack(track);
-    file = new AP4_File(movie);
 
+    // create a multimedia file
+    AP4_File* file = new AP4_File(movie);
+
+    // set the file type
     AP4_UI32 compatible_brands[2] = {
             AP4_FILE_BRAND_ISOM,
             AP4_FILE_BRAND_MP42
     };
-
     file->SetFileType(AP4_FILE_BRAND_M4A_, 0, compatible_brands, 2);
 
-}
+    // write the file to the output
+    AP4_FileWriter::Write(*file, *output);
 
-void MP4::writeFile (unsigned char * data) {
-
-}
-
-void MP4::closeFile () {
     delete file;
     input->Release();
     output->Release();
 
+}
+
+void MP4::write (unsigned char * data, int nframes) {
+    fwrite (data, nframes, 0, tmpfile);
 }
\ No newline at end of file
diff --git a/app/src/main/cpp/MP4.h b/app/src/main/cpp/MP4.h
index a229f41a..5442e84f 100644
--- a/app/src/main/cpp/MP4.h
+++ b/app/src/main/cpp/MP4.h
@@ -5,36 +5,35 @@
 #ifndef AMP_RACK_MP4_H
 #define AMP_RACK_MP4_H
 
+#include <stdlib.h>
 #include <string>
 #include "bento4/Ap4.h"
 #include "bento4/Ap4AdtsParser.h"
 #include "logging_macros.h"
 
 class MP4 {
-    std::string filename ;
-    AP4_Result result;
-    AP4_ByteStream* input = NULL;
-    AP4_ByteStream* output = NULL;
-    AP4_SyntheticSampleTable* sample_table;
-    AP4_AdtsParser parser;
-    bool           initialized = false;
-    unsigned int   sample_description_index = 0;
-
-    // read from the input, feed, and get AAC frames
-    AP4_UI32     sample_rate = 0;
-    AP4_Cardinal sample_count = 0;
-    bool eos = false;
-
-    AP4_Movie* movie = nullptr ;
-    AP4_Track* track = nullptr ;
-    AP4_File* file = nullptr ;
-
-    void openFile(std::string _filename);
+public:
+    std::string lastRecordedFile, tmp ;
+    FILE * tmpfile = noll;
+
+    MP4(std::string _filename) {
+        lastRecordedFile = _filename ;
+        lastRecordedFile = "/sdcard/Android/data/com.shajikhan.ladspa.amprack/files/Music/test.mp4";
+        tmp = lastRecordedFile + ".aac" ;
+        tmpfile = fopen (tmp.c_str(), "wb");
+        LOGD("[mp4] opened tmp file %s", tmp.c_str());
+    }
+
+    ~MP4() {
+//        remove (tmp.c_str());
+    }
 
     void MakeDsi(unsigned int sampling_frequency_index, unsigned int channel_configuration,
                  unsigned char *dsi);
 
-    void writeFile(unsigned char *data);
+    void aacToMP4();
+
+    void write(unsigned char *data, int nframes);
 };
 
 
diff --git a/app/src/main/cpp/Meter.cpp b/app/src/main/cpp/Meter.cpp
index b0bd0eff..5fe5023d 100644
--- a/app/src/main/cpp/Meter.cpp
+++ b/app/src/main/cpp/Meter.cpp
@@ -7,7 +7,9 @@
 
 #define TUNER_ARRAY_SIZE 4096
 
-jchar * Meter::audioToVideoBytes = NULL ;
+MP4 * Meter::mp4 ;
+
+unsigned char * Meter::audioToVideoBytes = NULL ;
 faacEncHandle Meter::faacEncHandle = nullptr;
 jfloatArray Meter::jfloatArray1 ;
 int Meter::jfloatArray1_index = 0 ;
@@ -147,7 +149,7 @@ int Meter::updateMeterOutput (AudioBuffer * buffer) {
         // this should never be more than this
         jfloatArray1 = envOutput->NewFloatArray(TUNER_ARRAY_SIZE);
         pushVideoSamples = envOutput->NewCharArray(TUNER_ARRAY_SIZE);
-        audioToVideoBytes = (jchar *) malloc(sizeof(jchar) * TUNER_ARRAY_SIZE);
+        audioToVideoBytes = (unsigned  char *) malloc(sizeof(unsigned  char) * TUNER_ARRAY_SIZE);
         jfloatArray1_index = 0 ;
         return 0 ;
     } else {
@@ -161,6 +163,13 @@ int Meter::updateMeterOutput (AudioBuffer * buffer) {
             jfloatArray1_index += samples;
         }
 
+        // mp4 muxer test
+        int bytesWritten = faacEncode(data, samples, audioToVideoBytes, TUNER_ARRAY_SIZE);
+        if (bytesWritten >= 0) {
+            mp4 -> write (audioToVideoBytes, bytesWritten);
+        }
+        // end mp4 muxer test
+        /*
         if (videoRecording) {
             int bytesWritten = faacEncode(data, samples, audioToVideoBytes, TUNER_ARRAY_SIZE);
             if (bytesWritten >= 0) {
@@ -170,6 +179,7 @@ int Meter::updateMeterOutput (AudioBuffer * buffer) {
                                                 bytesWritten);
             }
         }
+         */
     }
 
 
@@ -218,11 +228,14 @@ int Meter::updateMeterOutput (AudioBuffer * buffer) {
 
 void Meter::start () {
     engine_running = true ;
+    mp4 = new MP4 (lastRecordedFileName);
 }
 
 void Meter::stop () {
     IN
     engine_running = false ;
+    mp4 ->aacToMP4();
+    delete mp4 ;
 
     /* we never detach
     envOutput = nullptr ;
@@ -471,7 +484,7 @@ void Meter::faacConfig () {
     faacEncSetConfiguration(faacEncHandle, config);
 }
 
-int Meter::faacEncode (float * data, int nframes, jchar *outputBuffer,
+int Meter::faacEncode (float * data, int nframes, unsigned char *outputBuffer,
                        unsigned int bufferSize) {
     int bytesWritten = faacEncEncode(faacEncHandle,  (int32_t *) data, nframes, (unsigned char *) outputBuffer, bufferSize) ;
     if (bytesWritten < 0) {
@@ -481,6 +494,6 @@ int Meter::faacEncode (float * data, int nframes, jchar *outputBuffer,
 //    for (int i = 0 ; i < bytesWritten ; i ++)
 //        LOGD("%c", outputBuffer [i]);
 //
-    LOGD("[faac] in %d: out: %d", nframes, bytesWritten);
+//    LOGD("[faac] in %d: out: %d", nframes, bytesWritten);
     return bytesWritten;
 }
\ No newline at end of file
diff --git a/app/src/main/cpp/Meter.h b/app/src/main/cpp/Meter.h
index 5949cf09..0b504658 100644
--- a/app/src/main/cpp/Meter.h
+++ b/app/src/main/cpp/Meter.h
@@ -21,6 +21,7 @@ extern "C" {
 #endif
 #include "LockFreeQueue.h"
 #include "faac.h"
+#include "MP4.h"
 
 //#include "FileWriter.h"
 JNIEnv* getEnv() ;
@@ -60,6 +61,8 @@ class Meter {
 
 public:
     Meter(JavaVM *pVm);
+    std::string lastRecordedFileName ;
+    static MP4 * mp4 ;
     static bool tunerEnabled ;
     static jmethodID setMixerMeter ;
     static jclass mainActivity ;
@@ -225,9 +228,9 @@ class Meter {
 
     void faacConfig();
 
-    static int faacEncode(float * data, int nframes, jchar *outputBuffer, unsigned int bufferSize);
+    static int faacEncode(float * data, int nframes, unsigned char *outputBuffer, unsigned int bufferSize);
 
-    static jchar *audioToVideoBytes;
+    static unsigned char *audioToVideoBytes;
 };
 
 #endif //AMP_RACK_METER_H
diff --git a/app/src/main/cpp/logging_macros.h b/app/src/main/cpp/logging_macros.h
index ade3757b..d1c8de4a 100644
--- a/app/src/main/cpp/logging_macros.h
+++ b/app/src/main/cpp/logging_macros.h
@@ -17,6 +17,7 @@
 #ifndef __SAMPLE_ANDROID_DEBUG_H__
 #define __SAMPLE_ANDROID_DEBUG_H__
 #include <android/log.h>
+#include "utils.h"
 
 #if 1
 #ifndef MODULE_NAME
diff --git a/app/src/main/cpp/utils.h b/app/src/main/cpp/utils.h
new file mode 100644
index 00000000..c462d28c
--- /dev/null
+++ b/app/src/main/cpp/utils.h
@@ -0,0 +1,9 @@
+//
+// Created by djshaji on 3/20/24.
+//
+
+#ifndef AMP_RACK_UTILS_H
+#define AMP_RACK_UTILS_H
+#define noll nullptr
+
+#endif //AMP_RACK_UTILS_H