Skip to content

Commit

Permalink
Merge pull request #5 from sheinbergon/stable-release
Browse files Browse the repository at this point in the history
Added HE/v2 AAC support
  • Loading branch information
sheinbergon authored Mar 26, 2018
2 parents a59c8d1 + 9b5246c commit 86c72ea
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 78 deletions.
43 changes: 10 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# jna-aac-encoder

[![Build Status](https://travis-ci.org/sheinbergon/jna-aac-encoder.svg?branch=master)](https://travis-ci.org/sheinbergon/jna-aac-encoder) [![Coverage Status](https://coveralls.io/repos/github/sheinbergon/jna-aac-encoder/badge.svg)](https://coveralls.io/github/sheinbergon/jna-aac-encoder) [![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.sheinbergon/jna-aac-encoder/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.sheinbergon/jna-aac-encoder)
[![Build Status](https://travis-ci.org/sheinbergon/jna-aac-encoder.svg?branch=master)](https://travis-ci.org/sheinbergon/jna-aac-encoder) [![Coverage Status](https://coveralls.io/repos/github/sheinbergon/jna-aac-encoder/badge.svg)](https://coveralls.io/github/sheinbergon/jna-aac-encoder) [![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)
![Maven metadata URI](https://img.shields.io/maven-metadata/v/http/central.maven.org/maven2/org/sheinbergon/jna-aac-encoder/maven-metadata.xml.svg)

This library provides AAC encoding capabilities for the JVM.
It utilizes the [FDK AAC](https://github.com/mstorsjo/fdk-aac) library via JNA in order to do so.


## License
**Important!** While this library uses LGPL-3, please see
the [FDK AAC license](NOTICE) for additional information
Expand All @@ -18,14 +18,14 @@ Artifacts are available on maven central:

**_Gradle_**
```groovy
compile 'org.sheinbergon:jna-aac-encoder:0.1.1'
compile 'org.sheinbergon:jna-aac-encoder:0.1.2'
```
**_Maven_**
```xml
<dependency>
<groupId>org.sheinbergon</groupId>
<artifactId>jna-aac-encoder</artifactId>
<version>0.1.1</version>
<version>0.1.2</version>
</dependency>
```

Expand All @@ -36,37 +36,16 @@ need to make sure the shared library is installed as part of the runtime OS envi
and accessible to JNA. See [this](https://github.com/java-native-access/jna/blob/master/www/FrequentlyAskedQuestions.md#calling-nativeloadlibrary-causes-an-unsatisfiedlinkerror) link for additional information

To make things easier, cross-compiled artifacts (containing the shared library)
for both Windows (64bit) and Linux(64bit) are provided through the use of maven _classifiers_:
for both Windows(64bit) and Linux(64bit) are provided through the use of *_classifiers_*:

#### Windows (64 bit)
**_Gradle_**
##### Windows(64 bit)
```groovy
compile 'org.sheinbergon:jna-aac-encoder:0.1.1:win32-x86-64'
```
**_Maven_**
```xml
<dependency>
<groupId>org.sheinbergon</groupId>
<artifactId>jna-aac-encoder</artifactId>
<version>0.1.1</version>
<classifier>win32-x86-64</classifier>
</dependency>
compile 'org.sheinbergon:jna-aac-encoder:0.1.2:win32-x86-64'
```
#### Linux (64 bit)
**_Gradle_**
##### Linux(64 bit)
```groovy
compile 'org.sheinbergon:jna-aac-encoder:0.1.1:linux-x86-64'
compile 'org.sheinbergon:jna-aac-encoder:0.1.2:linux-x86-64'
```
**_Maven_**
```xml
<dependency>
<groupId>org.sheinbergon</groupId>
<artifactId>jna-aac-encoder</artifactId>
<version>0.1.1</version>
<classifier>linux-x86-64</classifier>
</dependency>
```

32bit platform won't be supported for now.
OSX/Macos toolchain is a bit trickier, so you'll just have to pre-install the dylib in advance.

Expand All @@ -90,13 +69,11 @@ the encoding process to fail.

Additional restrictions:
* A maximum of 6 audio input/output channels
* Only the AAC-LC(**L**ow **C**omplaxity) encoding profile is suuported

* Only the AAC-LC/HE-AAC/HE-AACv2 encoding profiles are suuported

## Roadmap
* Improved lower-level interface (with examples).
* Performance tests/comparison (JMH).
* Support for AAC HE & HEv2.
* Support additiona WAV audio formats.
* Meta-data insertion.
* MacOS cross-compiling?
Expand Down
2 changes: 1 addition & 1 deletion common.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
group 'org.sheinbergon'
version '0.1.1'
version '0.1.2'

sourceCompatibility = 1.9

Expand Down
6 changes: 6 additions & 0 deletions deploy.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ task win64Jar(type: Jar) {
include '*.dll'
into target
}
from("${projectDir}") {
include 'NOTICE'
}
rename '.+(fdk-aac).+(\\.dll)', '$1$2'
}

Expand All @@ -34,6 +37,9 @@ task linux64Jar(type: Jar) {
include '*.so'
into target
}
from("${projectDir}") {
include 'NOTICE'
}
}

void buildLibrary(source, target) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,15 @@
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Getter
@RequiredArgsConstructor
public enum AACEncodingProfile {
AAC_LC(2, "LC" ),
HE_AAC(5, "HE" ),
HE_AAC_V2(29, "HE_V2" ),
AAC_LD(23, "LD" );
AAC_LC(2, "LC"),
HE_AAC(5, "HE-AAC"),
HE_AAC_V2(29, "HE-AACv2");
//AAC_LD(23, "LD" ) - Not yet supported
//AAC_ELD(39) - Not yet supported

private final int aot;
private final String profile;
}
5 changes: 2 additions & 3 deletions src/main/java/org/sheinbergon/aac/sound/AACFileTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

import javax.sound.sampled.AudioFileFormat;

/**
* Currently, only AAC-LC is supported
*/
public class AACFileTypes {
public final static AudioFileFormat.Type AAC_LC = new AudioFileFormat.Type("AAC_LC", "aac");
public final static AudioFileFormat.Type AAC_HE = new AudioFileFormat.Type("AAC_HE", "aac");
public final static AudioFileFormat.Type AAC_HE_V2 = new AudioFileFormat.Type("AAC_HE_V2", "aac");
}
10 changes: 6 additions & 4 deletions src/main/java/org/sheinbergon/aac/sound/AACFileWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ public final class AACFileWriter extends AudioFileWriter {


private final static Map<AudioFileFormat.Type, AACEncodingProfile> FILE_TYPES_TO_ENCODING_PROFILES = Map.of(
AACFileTypes.AAC_LC, AACEncodingProfile.AAC_LC);

AACFileTypes.AAC_LC, AACEncodingProfile.AAC_LC,
AACFileTypes.AAC_HE, AACEncodingProfile.HE_AAC,
AACFileTypes.AAC_HE_V2, AACEncodingProfile.HE_AAC_V2);

@Override
public AudioFileFormat.Type[] getAudioFileTypes() {
return Stream.of(AACFileTypes.AAC_LC).toArray(AudioFileFormat.Type[]::new);
return Stream.of(AACFileTypes.AAC_LC, AACFileTypes.AAC_HE, AACFileTypes.AAC_HE_V2)
.toArray(AudioFileFormat.Type[]::new);
}

@Override
Expand All @@ -45,7 +47,7 @@ public AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) {
}
}

private static AACEncodingProfile profileByType(AudioFileFormat.Type type) {
static AACEncodingProfile profileByType(AudioFileFormat.Type type) {
return Optional.ofNullable(FILE_TYPES_TO_ENCODING_PROFILES.get(type))
.orElseThrow(() -> new IllegalArgumentException("File type " + type + " is not yet supported"));
}
Expand Down
18 changes: 12 additions & 6 deletions src/test/java/org/sheinbergon/aac/MediaInfoSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,35 @@

public class MediaInfoSupport {


private final static String VALUE_DELIMITER = "/";
private final static String AAC_MEDIAINFO_FORMAT = "AAC";
private final static int AUDIO_STREAM_INDEX = 0;
private final static int EXPECTED_AUDIO_STREAMS_COUNT = 1;

private final static MediaInfo MEDIA_INFO = new MediaInfo();

// Mediainfo sometimes provide multiple value delimited by '/'. This extract the first cell only.
private static String getParam(String param) {
return MEDIA_INFO.get(MediaInfo.StreamKind.Audio, AUDIO_STREAM_INDEX, param);
return MEDIA_INFO.get(MediaInfo.StreamKind.Audio, AUDIO_STREAM_INDEX, param)
.split(VALUE_DELIMITER)[0]
.trim();
}

// Bitrate is not present as part of AAC's encoding metadata, but is rather deduced from the
// stream itself. There for, it cannot be parsed by the mediainfo shared library
// TODO - This should probably changed once metadata is added
public static void assertAACOutput(File aac, AudioFormat inputFormat, AACEncodingProfile profile) {
boolean opened = false;
try {
if (opened = MEDIA_INFO.open(aac)) {
Assertions.assertEquals(MEDIA_INFO.streamCount(MediaInfo.StreamKind.Audio), EXPECTED_AUDIO_STREAMS_COUNT);
Assertions.assertEquals(Float.valueOf(getParam("SamplingRate" )).floatValue(), inputFormat.getSampleRate());
Assertions.assertEquals(Integer.valueOf(getParam("Channel(s)" )).intValue(), inputFormat.getChannels());
Assertions.assertEquals(getParam("Format" ), AAC_MEDIAINFO_FORMAT);
Assertions.assertEquals(getParam("Format_Profile" ), profile.getProfile());
Assertions.assertEquals(Float.valueOf(getParam("SamplingRate")).floatValue(), inputFormat.getSampleRate());
Assertions.assertEquals(Integer.valueOf(getParam("Channel(s)")).intValue(), inputFormat.getChannels());
Assertions.assertEquals(getParam("Format"), AAC_MEDIAINFO_FORMAT);
Assertions.assertEquals(getParam("Format_Profile"), profile.getProfile());
} else {
throw new IllegalStateException("Could not open AAC file " + aac + " via the mediainfo shared library" );
throw new IllegalStateException("Could not open AAC file " + aac + " via the mediainfo shared library");
}
} finally {
if (opened) {
Expand Down
47 changes: 25 additions & 22 deletions src/test/java/org/sheinbergon/aac/sound/AACFileWriterTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.sheinbergon.aac.sound;

import org.apache.commons.lang3.math.NumberUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
Expand All @@ -15,36 +14,38 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.stream.Stream;

@DisplayName("Java AudioSystem AAC encoding support" )
@DisplayName("Java AudioSystem AAC encoding support")
public class AACFileWriterTest {

private final static AudioFileFormat.Type[] NO_FILE_TYPES = new AudioFileFormat.Type[0];
private final static AudioFileFormat.Type[] AAC_FILE_TYPES = new AudioFileFormat.Type[]{AACFileTypes.AAC_LC};
private final static AudioFileFormat.Type[] AAC_FILE_TYPES = new AudioFileFormat.Type[]{AACFileTypes.AAC_LC, AACFileTypes.AAC_HE, AACFileTypes.AAC_HE_V2};
private final static AACEncodingProfile[] AAC_AUDIO_TYPES = new AACEncodingProfile[]{AACEncodingProfile.AAC_LC, AACEncodingProfile.HE_AAC, AACEncodingProfile.HE_AAC_V2};
private final AACFileWriter writer = new AACFileWriter();

@Test
@DisplayName("Supported audio file types" )
@DisplayName("Supported audio file types")
public void supportedTypes() {
Assertions.assertArrayEquals(AAC_FILE_TYPES, writer.getAudioFileTypes());
}

@Test
@DisplayName("Unsupported audio input" )
@DisplayName("Unsupported audio input")
public void unsupportedAudioInput() throws UnsupportedAudioFileException, IOException {
AudioInputStream input = TestSupport.unsupported24bitWAVAudioInputStream();
Assertions.assertArrayEquals(NO_FILE_TYPES, writer.getAudioFileTypes(input));
}

@Test
@DisplayName("Supported audio input" )
@DisplayName("Supported audio input")
public void supportedAudioInput() throws UnsupportedAudioFileException, IOException {
AudioInputStream input = TestSupport.supportedWAVAudioInputStream();
Assertions.assertArrayEquals(AAC_FILE_TYPES, writer.getAudioFileTypes(input));
}

@Test
@DisplayName("Unsupported audio Encoding" )
@DisplayName("Unsupported audio encoding")
public void unsupportedAudioEncoding() throws UnsupportedAudioFileException, IOException {
File aac = TestSupport.tempAACOutputFile();
AudioInputStream input = TestSupport.unsupported24bitWAVAudioInputStream();
Expand All @@ -59,26 +60,28 @@ public void unsupportedAudioEncoding() throws UnsupportedAudioFileException, IOE
});
}


@Test
@DisplayName("Supported audio output stream encoding" )
public void supportedAudioOutputStreamEncoding() throws UnsupportedAudioFileException, IOException {
File aac = TestSupport.tempAACOutputFile();
FileOutputStream output = new FileOutputStream(aac);
AudioInputStream input = TestSupport.supportedWAVAudioInputStream();
try (input; output) {
writer.write(input, AACFileTypes.AAC_LC, output);
Assertions.assertTrue(aac.length() > 0);
MediaInfoSupport.assertAACOutput(aac, input.getFormat(), AACEncodingProfile.AAC_LC);
} finally {
if (aac.exists()) {
Files.delete(aac.toPath());
@DisplayName("Supported audio types encoding (LC/HE-AAC/HE-AACv2)")
public void supportedAudioOutputStreamEncoding() {
Assertions.assertAll(Stream.of(AAC_FILE_TYPES).map(audioType -> () -> {
File aac = TestSupport.tempAACOutputFile();
FileOutputStream output = new FileOutputStream(aac);
AudioInputStream input = TestSupport.supportedWAVAudioInputStream();
try (input; output) {
writer.write(input, audioType, output);
Assertions.assertTrue(aac.length() > 0);
output.flush();
MediaInfoSupport.assertAACOutput(aac, input.getFormat(), AACFileWriter.profileByType(audioType));
} finally {
if (aac.exists()) {
Files.delete(aac.toPath());
}
}
}
}));
}

@Test
@DisplayName("Supported audio file encoding" )
@DisplayName("Supported audio file encoding")
public void supportedAudioFileEncoding() throws UnsupportedAudioFileException, IOException {
File aac = TestSupport.tempAACOutputFile();
AudioInputStream input = TestSupport.supportedWAVAudioInputStream();
Expand Down

0 comments on commit 86c72ea

Please sign in to comment.