Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stream audio with spring boot

I want to enable the user to play a sound. My implementation works fine with firefox. On Safari the sound is not played. I verified, that the audio control works in safari with other websites. So, I assume, that I will have to change something in my controller?

Controller:

@RequestMapping(value = "/sound/character/get/{characterId}", method = RequestMethod.GET, produces = {
            MediaType.APPLICATION_OCTET_STREAM_VALUE })
        public ResponseEntity playAudio(HttpServletRequest request,HttpServletResponse response, @PathVariable("characterId") int characterId) throws FileNotFoundException{

        logger.debug("[downloadRecipientFile]");

        de.tki.chinese.entity.Character character = characterRepository.findById(characterId);
        String file = UPLOADED_FOLDER + character.getSoundFile();

        long length = new File(file).length();


        InputStreamResource inputStreamResource = new InputStreamResource( new FileInputStream(file));
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentLength(length);
        httpHeaders.setCacheControl(CacheControl.noCache().getHeaderValue());
        return new ResponseEntity(inputStreamResource, httpHeaders, HttpStatus.OK);
    }

View

        <audio id="voice" controls="">
            <source src="/sound/character/get/2">
        </audio>

Firefox (works fine): Firefox (works fine)

Safari (not working): Safari (not working!)

like image 237
tobi Avatar asked Feb 07 '19 08:02

tobi


1 Answers

Most players are going to require a controller that supports partial content requests (or byte ranges).

This can be a little tricky to implement so I would suggest using something like the Spring Community Project Spring Content then you don't need to worry about how to implement the controller at all. The concepts and programming model are very similar to Spring Data's that, by looks of it, you are already using.

Assuming you are using Spring Boot (let me know if you are not) then it would look something like this:

pom.xml

<!-- Java API -->
<dependency>
    <groupId>com.github.paulcwarren</groupId>
    <artifactId>content-fs-spring-boot-starter</artifactId>
    <version>0.6.0</version>
</dependency>
<!-- REST API -->
<dependency>
    <groupId>com.github.paulcwarren</groupId>
    <artifactId>spring-content-rest-boot-starter</artifactId>
    <version>0.6.0</version>
</dependency>

SpringBootApplication.java

@SpringBootApplication
public class YourSpringBootApplication {

  public static void main(String[] args) {
    SpringApplication.run(YourSpringBootApplication.class, args);
  }

  @Configuration
  @EnableFilesystemStores
  public static class StorageConfig {
    File filesystemRoot() {
        return new File("/path/to/your/sounds");
    }

    @Bean
    public FileSystemResourceLoader fsResourceLoader() throws Exception {
      return new FileSystemResourceLoader(filesystemRoot().getAbsolutePath());
    }
  }

  @StoreRestResource(path="characterSounds")
  public interface SoundsContentStore extends ContentStore<UUUID,String> {
    //
  }
}

Charater.java

public class Character {

    @Id
    @GeneratedValue
    private Long id;

    ...other existing fields...

    @ContentId
    private UUID contentId;

    @ContentLength
    private Long contnetLength;

    @MimeType
    private String mimeType;
} 

This is all you need to create a REST-based audio service at /characterSounds supporting streaming. It actually supports full CRUD functionality as well; Create == POST, Read == GET (include the byte-range support that you need), Update == PUT, Delete == DELETE in case that is useful to you. Uploaded sounds will be stored in "/path/to/your/sounds".

So...

GET /characterSounds/{characterId}

will return a partial content response and this should stream properly in most, if not all, players (including seeking forwards and backwards).

HTH

like image 155
Paul Warren Avatar answered Sep 28 '22 01:09

Paul Warren