org.openmrs.module.patientnarratives.web.controller.WebRtcMediaStreamController.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.module.patientnarratives.web.controller.WebRtcMediaStreamController.java

Source

/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */
package org.openmrs.module.patientnarratives.web.controller;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.*;
import org.openmrs.api.context.Context;
import org.openmrs.obs.ComplexData;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import javax.servlet.http.HttpServletRequest;

import com.xuggle.mediatool.IMediaWriter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.xuggler.IAudioSamples;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.IStream;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IVideoPicture;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Date;
import java.util.List;

/**
 * This is the Controller of the Video recording stuff, this controller will receive the user submitted
 * video record as two multipart http requests. 1. Video blob, 2. Audio blob.
 * (Since WebRTC currently doesn't support generating Video with Audio we had to do this)
 *
 * After that the two blobs are been merged together using the "Xuggler library" which is a (FFMPEG Java wrapper)
 * Then the merged single file will be saved as a OpenMRS complex observation inside Hard disc (~/.OpenMRS/complex_obs folder)
 */
@Controller
public class WebRtcMediaStreamController {

    private String returnUrl;
    public final static String FORM_PATH = "/module/patientnarratives/webRtcMedia.form";
    protected final Log log = LogFactory.getLog(getClass());

    private File tempMergedVideoFile = null;

    @RequestMapping(FORM_PATH)
    public ModelAndView handleRequest(HttpServletRequest request) throws Exception {

        if (request instanceof MultipartHttpServletRequest) {
            MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
            MultipartFile videofile = (MultipartFile) multipartRequest.getFile("video");
            MultipartFile audiofile = (MultipartFile) multipartRequest.getFile("audio");

            /**
             * Xuggler merge process of the two binary streams
             */
            try {
                tempMergedVideoFile = File.createTempFile("mergedVideoFile", ".flv");
                String mergedUrl = tempMergedVideoFile.getCanonicalPath();

                IMediaWriter mWriter = ToolFactory.makeWriter(mergedUrl); //output file

                IContainer containerVideo = IContainer.make();
                IContainer containerAudio = IContainer.make();

                InputStream videoInputStream = videofile.getInputStream();
                InputStream audioInputStream = audiofile.getInputStream();

                if (containerVideo.open(videoInputStream, null) < 0)
                    throw new IllegalArgumentException("Cant find " + videoInputStream);

                if (containerAudio.open(audioInputStream, null) < 0)
                    throw new IllegalArgumentException("Cant find " + audioInputStream);

                int numStreamVideo = containerVideo.getNumStreams();
                int numStreamAudio = containerAudio.getNumStreams();

                System.out.println("Number of video streams: " + numStreamVideo + "\n" + "Number of audio streams: "
                        + numStreamAudio);

                int videostreamt = -1; //this is the video stream id
                int audiostreamt = -1;

                IStreamCoder videocoder = null;

                for (int i = 0; i < numStreamVideo; i++) {
                    IStream stream = containerVideo.getStream(i);
                    IStreamCoder code = stream.getStreamCoder();

                    if (code.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO) {
                        videostreamt = i;
                        videocoder = code;
                        break;
                    }
                }

                for (int i = 0; i < numStreamAudio; i++) {
                    IStream stream = containerAudio.getStream(i);
                    IStreamCoder code = stream.getStreamCoder();

                    if (code.getCodecType() == ICodec.Type.CODEC_TYPE_AUDIO) {
                        audiostreamt = i;
                        break;
                    }
                }

                if (videostreamt == -1)
                    throw new RuntimeException("No video steam found");
                if (audiostreamt == -1)
                    throw new RuntimeException("No audio steam found");

                if (videocoder.open() < 0)
                    throw new RuntimeException("Cant open video coder");
                IPacket packetvideo = IPacket.make();

                IStreamCoder audioCoder = containerAudio.getStream(audiostreamt).getStreamCoder();

                if (audioCoder.open() < 0)
                    throw new RuntimeException("Cant open audio coder");
                mWriter.addAudioStream(1, 1, audioCoder.getChannels(), audioCoder.getSampleRate());

                mWriter.addVideoStream(0, 0, videocoder.getWidth(), videocoder.getHeight());

                IPacket packetaudio = IPacket.make();

                while (containerVideo.readNextPacket(packetvideo) >= 0
                        || containerAudio.readNextPacket(packetaudio) >= 0) {

                    if (packetvideo.getStreamIndex() == videostreamt) {

                        //video packet
                        IVideoPicture picture = IVideoPicture.make(videocoder.getPixelType(), videocoder.getWidth(),
                                videocoder.getHeight());
                        int offset = 0;
                        while (offset < packetvideo.getSize()) {
                            int bytesDecoded = videocoder.decodeVideo(picture, packetvideo, offset);
                            if (bytesDecoded < 0)
                                throw new RuntimeException("bytesDecoded not working");
                            offset += bytesDecoded;

                            if (picture.isComplete()) {
                                System.out.println(picture.getPixelType());
                                mWriter.encodeVideo(0, picture);

                            }
                        }
                    }

                    if (packetaudio.getStreamIndex() == audiostreamt) {
                        //audio packet

                        IAudioSamples samples = IAudioSamples.make(512, audioCoder.getChannels(),
                                IAudioSamples.Format.FMT_S32);
                        int offset = 0;
                        while (offset < packetaudio.getSize()) {
                            int bytesDecodedaudio = audioCoder.decodeAudio(samples, packetaudio, offset);
                            if (bytesDecodedaudio < 0)
                                throw new RuntimeException("could not detect audio");
                            offset += bytesDecodedaudio;

                            if (samples.isComplete()) {
                                mWriter.encodeAudio(1, samples);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                log.error(e);
                e.getStackTrace();
            }
        }

        saveAndTransferVideoComplexObs();

        returnUrl = request.getContextPath() + "/module/patientnarratives/patientNarrativesForm.form";
        return new ModelAndView(new RedirectView(returnUrl));
    }

    /**
     * Saving the file as a Complex observation
     */
    public void saveAndTransferVideoComplexObs() {

        try {
            List<Encounter> encounters = Context.getEncounterService().getEncounters(null, null, null, null, null,
                    null, true);
            Encounter lastEncounter = encounters.get(encounters.size() - 1);

            Person patient = lastEncounter.getPatient();
            ConceptComplex conceptComplex = Context.getConceptService().getConceptComplex(14);
            Location location = Context.getLocationService().getDefaultLocation();
            Obs obs = new Obs(patient, conceptComplex, new Date(), location);

            String mergedUrl = tempMergedVideoFile.getCanonicalPath();
            InputStream out1 = new FileInputStream(new File(mergedUrl));

            ComplexData complexData = new ComplexData("mergedFile1.flv", out1);
            obs.setComplexData(complexData);
            obs.setEncounter(lastEncounter);

            Context.getObsService().saveObs(obs, null);
            tempMergedVideoFile.delete();

        } catch (Exception e) {
            log.error(e);
        }
    }
}