get duration of audio file - android

I have made a voice recorder app, and I want to show the duration of the recordings in a listview. I save the recordings like this:
MediaRecorder recorder = new MediaRecorder();
recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
folder = new File(Environment.getExternalStorageDirectory()
+ File.separator + "Audio recordings");
String[] files = folder.list();
int number = files.length + 1;
String filename = "AudioSample" + number + ".mp3";
File output = new File(Environment.getExternalStorageDirectory()
+ File.separator + "Audio recordings" + File.separator
+ filename);
FileOutputStream writer = new FileOutputStream(output);
FileDescriptor fd = writer.getFD();
recorder.setOutputFile(fd);
try {
recorder.prepare();
recorder.start();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
Log.e(LOG_TAG, "prepare() failed");
e.printStackTrace();
}
How can I get the duration in seconds of this file?
Thanks in advance
---EDIT
I got it working, I called MediaPlayer.getduration() inside the MediaPlayer.setOnPreparedListener() method so it returned 0.

MediaMetadataRetriever is a lightweight and efficient way to do this. MediaPlayer is too heavy and could arise performance issue in high performance environment like scrolling, paging, listing, etc.
Furthermore, Error (100,0) could happen on MediaPlayer since it's a heavy and sometimes restart needs to be done again and again.
Uri uri = Uri.parse(pathStr);
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(AppContext.getAppContext(),uri);
String durationStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
int millSecond = Integer.parseInt(durationStr);

The quickest way to do is via MediaMetadataRetriever. However, there is a catch
if you use URI and context to set data source you might encounter bug
https://code.google.com/p/android/issues/detail?id=35794
Solution is use absolute path of file to retrieve metadata of media file.
Below is the code snippet to do so
private static String getDuration(File file) {
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(file.getAbsolutePath());
String durationStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
return Utils.formateMilliSeccond(Long.parseLong(durationStr));
}
Now you can convert millisecond to human readable format using either of below formats
/**
* Function to convert milliseconds time to
* Timer Format
* Hours:Minutes:Seconds
*/
public static String formateMilliSeccond(long milliseconds) {
String finalTimerString = "";
String secondsString = "";
// Convert total duration into time
int hours = (int) (milliseconds / (1000 * 60 * 60));
int minutes = (int) (milliseconds % (1000 * 60 * 60)) / (1000 * 60);
int seconds = (int) ((milliseconds % (1000 * 60 * 60)) % (1000 * 60) / 1000);
// Add hours if there
if (hours > 0) {
finalTimerString = hours + ":";
}
// Prepending 0 to seconds if it is one digit
if (seconds < 10) {
secondsString = "0" + seconds;
} else {
secondsString = "" + seconds;
}
finalTimerString = finalTimerString + minutes + ":" + secondsString;
// return String.format("%02d Min, %02d Sec",
// TimeUnit.MILLISECONDS.toMinutes(milliseconds),
// TimeUnit.MILLISECONDS.toSeconds(milliseconds) -
// TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliseconds)));
// return timer string
return finalTimerString;
}

Either try this to get duration in milliseconds:
MediaPlayer mp = MediaPlayer.create(yourActivity, Uri.parse(pathofyourrecording));
int duration = mp.getDuration();
Or measure the time elapsed from recorder.start() till recorder.stop() in nanoseconds:
long startTime = System.nanoTime();
// ... do recording ...
long estimatedTime = System.nanoTime() - startTime;

Try use
long totalDuration = mediaPlayer.getDuration(); // to get total duration in milliseconds
long currentDuration = mediaPlayer.getCurrentPosition(); // to Gets the current playback position in milliseconds
Division on 1000 to convert to seconds.
Hope this helped you.

If the audio is from url, just wait for on prepared:
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
length = mp.getDuration();
}
});

Kotlin Extension Solution
You can add this to reliably and safely get your audio file's duration. If it doesn't exist or there is an error, you'll get back 0.
myAudioFile.getMediaDuration(context)
/**
* If file is a Video or Audio file, return the duration of the content in ms
*/
fun File.getMediaDuration(context: Context): Long {
if (!exists()) return 0
val retriever = MediaMetadataRetriever()
return try {
retriever.setDataSource(context, uri)
val duration = retriever.extractMetadata(METADATA_KEY_DURATION)
retriever.release()
duration.toLongOrNull() ?: 0
} catch (exception: Exception) {
0
}
}
If you are regularly working with String or Uri for files, I'd suggest also adding these useful helpers
fun Uri.asFile(): File = File(toString())
fun String?.asUri(): Uri? {
try {
return Uri.parse(this)
} catch (e: Exception) {
Sentry.captureException(e)
}
return null
}
fun String.asFile() = File(this)

According to Vijay's answer, The function gives us the duration of the audio/video file but unfortunately, there is an issue of a run time exception so I sorted out and below function work properly and return the exact duration of the audio or video file.
public String getAudioFileLength(String path, boolean stringFormat) {
StringBuilder stringBuilder = new StringBuilder();
try {
Uri uri = Uri.parse(path);
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(HomeActivity.this, uri);
String duration = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
int millSecond = Integer.parseInt(duration);
if (millSecond < 0) return String.valueOf(0); // if some error then we say duration is zero
if (!stringFormat) return String.valueOf(millSecond);
int hours, minutes, seconds = millSecond / 1000;
hours = (seconds / 3600);
minutes = (seconds / 60) % 60;
seconds = seconds % 60;
if (hours > 0 && hours < 10) stringBuilder.append("0").append(hours).append(":");
else if (hours > 0) stringBuilder.append(hours).append(":");
if (minutes < 10) stringBuilder.append("0").append(minutes).append(":");
else stringBuilder.append(minutes).append(":");
if (seconds < 10) stringBuilder.append("0").append(seconds);
else stringBuilder.append(seconds);
}catch (Exception e){
e.printStackTrace();
}
return stringBuilder.toString();
}
:)

You can use this readyMade method, hope this helps someone.
Example 1 : getAudioFileLength(address, true); // if you want in stringFormat
Example 2 : getAudioFileLength(address, false); // if you want in milliseconds
public String getAudioFileLength(String path, boolean stringFormat) {
Uri uri = Uri.parse(path);
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(Filter_Journals.this, uri);
String duration = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
int millSecond = Integer.parseInt(duration);
if (millSecond < 0) return String.valueOf(0); // if some error then we say duration is zero
if (!stringFormat) return String.valueOf(millSecond);
int hours, minutes, seconds = millSecond / 1000;
hours = (seconds / 3600);
minutes = (seconds / 60) % 60;
seconds = seconds % 60;
StringBuilder stringBuilder = new StringBuilder();
if (hours > 0 && hours < 10) stringBuilder.append("0").append(hours).append(":");
else if (hours > 0) stringBuilder.append(hours).append(":");
if (minutes < 10) stringBuilder.append("0").append(minutes).append(":");
else stringBuilder.append(minutes).append(":");
if (seconds < 10) stringBuilder.append("0").append(seconds);
else stringBuilder.append(seconds);
return stringBuilder.toString();
}

For me, the AudioGraph class came to the rescue:
public static async Task<double> AudioFileDuration(StorageFile file)
{
var result = await AudioGraph.CreateAsync(new AudioGraphSettings(Windows.Media.Render.AudioRenderCategory.Speech));
if (result.Status == AudioGraphCreationStatus.Success)
{
AudioGraph audioGraph = result.Graph;
var fileInputNodeResult = await audioGraph.CreateFileInputNodeAsync(file);
return fileInputNodeResult.FileInputNode.Duration.TotalSeconds;
}
return -1;
}

Kotlin shortest way to do it (if it is an audiofile):
private fun getDuration(absolutePath: String): String {
val retriever = MediaMetadataRetriever()
retriever.setDataSource(absolutePath)
//For format in string MM:SS
val rawDuration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
val duration = rawDuration.milliseconds
return format("%02d:%02d", duration.inWholeMinutes, duration.inWholeSeconds % 60)
}
private fun getDurationInSeconds(absolutePath: String): Long {
val retriever = MediaMetadataRetriever()
retriever.setDataSource(absolutePath)
//Return only value in seconds
val rawDuration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
return rawDuration.milliseconds.inWholeSeconds
}

After you write the file, open it up in a MediaPlayer, and call getDuration on it.

Have you looked at Ringdroid?. It's pretty light weight and the integration is straight forward. It works well with VBR media files as well.
For your problem with getting the duration, you might want to do something like below using Ringdroid.
public class AudioUtils
{
public static long getDuration(CheapSoundFile cheapSoundFile)
{
if( cheapSoundFile == null)
return -1;
int sampleRate = cheapSoundFile.getSampleRate();
int samplesPerFrame = cheapSoundFile.getSamplesPerFrame();
int frames = cheapSoundFile.getNumFrames();
cheapSoundFile = null;
return 1000 * ( frames * samplesPerFrame) / sampleRate;
}
public static long getDuration(String mediaPath)
{
if( mediaPath != null && mediaPath.length() > 0)
try
{
return getDuration(CheapSoundFile.create(mediaPath, null));
}catch (FileNotFoundException e){}
catch (IOException e){}
return -1;
}
}
Hope that helps

It's simply. use RandomAccessFile Below is the code snippet to do so
public static int getAudioInfo(File file) {
try {
byte header[] = new byte[12];
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
randomAccessFile.readFully(header, 0, 8);
randomAccessFile.close();
return (int) file.length() /1000;
} catch (Exception e) {
return 0;
}
}
You can, of course, be more complete depending on your needs

Related

Video trimming on Android using MediaCodec, Media Extractor and Media Muxer

In my Android App I need a solution when the user can trim a video from internal storage. I am trying to achieve this without using any third-party library. I was referencing this Google's Gallery App source code here. But I am getting the following error :
timestampUs 66733 < lastTimestampUs 133467 for Video track
There are few SO questions which talk about this timestamp issue, but I am really not being to figure out as I am new to using MediaCodecs, MediaMuxers etc. Would be great if any can hep
Here's my code :
// Set up MediaExtractor to read from the source.
MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(srcPath);
int trackCount = extractor.getTrackCount();
System.out.println("tracl" + trackCount);
// Set up MediaMuxer for the destination.
MediaMuxer muxer;
muxer = new MediaMuxer(dstPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
// Set up the tracks and retrieve the max buffer size for selected
// tracks.
HashMap<Integer, Integer> indexMap = new HashMap<>(trackCount);
int bufferSize = -1;
for (int i = 0; i < trackCount; i++) {
MediaFormat format = extractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
boolean selectCurrentTrack = false;
if (mime.startsWith("audio/") && useAudio) {
selectCurrentTrack = true;
} else if (mime.startsWith("video/") && useVideo) {
selectCurrentTrack = true;
}
if (selectCurrentTrack) {
extractor.selectTrack(i);
int dstIndex = muxer.addTrack(format);
System.out.println(format);
System.out.println("dstIndex" + dstIndex);
indexMap.put(i, dstIndex);
if (format.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
int newSize = format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
bufferSize = newSize > bufferSize ? newSize : bufferSize;
}
}
}
System.out.println(indexMap);
if (bufferSize < 0) {
bufferSize = DEFAULT_BUFFER_SIZE;
}
// Set up the orientation and starting time for extractor.
MediaMetadataRetriever retrieverSrc = new MediaMetadataRetriever();
retrieverSrc.setDataSource(srcPath);
String degreesString = retrieverSrc.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
if (degreesString != null) {
int degrees = Integer.parseInt(degreesString);
if (degrees >= 0) {
muxer.setOrientationHint(degrees);
}
}
if (startMs > 0) {
extractor.seekTo(startMs * 1000, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
}
// System.out.println(extractor.);
// Copy the samples from MediaExtractor to MediaMuxer. We will loop
// for copying each sample and stop when we get to the end of the source
// file or exceed the end time of the trimming.
int offset = 0;
int trackIndex = -1;
ByteBuffer dstBuf = ByteBuffer.allocate(bufferSize);
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
try {
muxer.start();
while (true) {
System.out.println("copying");
bufferInfo.offset = offset;
bufferInfo.size = extractor.readSampleData(dstBuf, offset);
if (bufferInfo.size < 0 ) {
// InstabugSDKLogger.d(TAG, "Saw input EOS.");
System.out.println("Saw input EOS.");
bufferInfo.size = 0;
break;
} else {
/**
* The presentation timestamp in microseconds for the buffer.
* This is derived from the presentation timestamp passed in
* with the corresponding input buffer. This should be ignored for
* a 0-sized buffer.
*/
/**
* Returns the current sample's presentation time in microseconds.
* or -1 if no more samples are available.
*/
bufferInfo.presentationTimeUs = extractor.getSampleTime();
//1 sec = 1000000 micco sec
if (endMs > 0 && bufferInfo.presentationTimeUs > (endMs * 1000)) {
break;
} else {
bufferInfo.flags = extractor.getSampleFlags();
trackIndex = extractor.getSampleTrackIndex();
muxer.writeSampleData(indexMap.get(trackIndex), dstBuf, bufferInfo);
//System.out.println(muxer);
extractor.advance();
}
}
}
muxer.stop();
File file = new File(srcPath);
file.delete();
} catch (IllegalStateException e) {
System.out.println("The source video file is malformed");
} finally {
muxer.release();
}

retrofit call execute using rxjava

Scenario :
Create request
**Interface**
#GET("someurl.mp4")
#Streaming
Call<ResponseBody> downloadFile(); // retrofit2.Call
**call**
RetrofitInterface retrofitInterface = retrofit.create(RetrofitInterface.class);
//okhttp3.ResponseBody
Call<ResponseBody> request = retrofitInterface.downloadFile();
try {
downloadFile(request.execute().body());
} catch (IOException e) {
e.printStackTrace();
}
Download bytes by bytes
private void downloadFile(ResponseBody body) throws IOException {
int count;
byte[] data;
data = new byte[1024 * 4];
long fileSize = body.contentLength();
Log.i("Download", "downloadFile: " + fileSize);
InputStream bis = new BufferedInputStream(body.byteStream(), 1024 * 8);
File outputFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), System.currentTimeMillis() + ".mp4");
try (OutputStream output = new FileOutputStream(outputFile)) {
long total = 0;
long startTime = System.currentTimeMillis();
Log.i("Download", "downloadFile size: " + fileSize);
int timeCount = 1;
while ((count = bis.read(data)) != -1) {
total += count;
totalFileSize = (int) (fileSize / (Math.pow(1024, 2)));
double current = Math.round(total / (Math.pow(1024, 2)));
int progress = (int) ((total * 100) / fileSize);
long currentTime = System.currentTimeMillis() - startTime;
Download download = new Download();
download.setTotalFileSize(totalFileSize);
if (currentTime > 1000 * timeCount) {
download.setCurrentFileSize((int) current);
download.setProgress(progress);
sendNotification(download);
timeCount++;
}
if (download.getProgress() != 0)
Log.i("Download", "progress: " + download.getProgress());
output.write(data, 0, count);
}
onDownloadComplete();
output.flush();
//output.close();
}
bis.close();
}
Problem
I am not able to port above code in RxJava using Observable/Single interface.
All i want is to download file bytes by bytes for some purpose.
I tried to call downloadFile(request.execute().body()); inside ongoing async operation(RxJava) but didn't work as expected.
There is no good reason to declare method as returning Call<ResponseBody> if you are using RxJava anyway. Declare it as Single<ResponseBody> and use its result directly.

getDuration() gives 0 output for one file in Android App

My app loads two audio files from database and stores them in an array. User can play any of them by selecting one from radio button group. both are mp3. One is playing fine and it's elapsed and total duration is displaying correctly. But the same functions display 00:00 total duration for other. Seek bar also updates its progress to 100% in this case but the elapsed time is correctly displaying and audio is playing fine. Someone please tell what is the problem? Why this is happening.. and how can I resolve it??
audio_urdu's time is fine.. error is with audio_eng.
private void updateView(int i) throws JSONException
{
idx=0;
_imgBtnPlay.setClickable(false);
_imgBtnStop.setClickable(false);
JSONObject jObject=null;
jObject=Jarray.getJSONObject(i);
audioUrl_eng=jObject.getString("audio_eng");
audioUrl_urdu=jObject.getString("audio_urdu");
lbl_tDuration.setText("00:00");
lbl_cDuration.setText("00:00");
lbl_loading.setText("Loading audio files...");
loadAudio(audioUrl_eng);
}
// Loading audio files from URL
private void loadAudio(String url)
{
// TODO Auto-generated method stub
mMediaPlayer=new MediaPlayer();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try
{
mMediaPlayer.setDataSource(url);
mMediaPlayer.prepareAsync();
}
catch(IOException e)
{
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalStateException e)
{
e.printStackTrace();
}
mMediaPlayer.setOnPreparedListener(this);
}
// Notify when audio is ready to be played
#Override
public void onPrepared(MediaPlayer mp)
{
// TODO Auto-generated method stub
audioPlayerList[idx]=mp;
idx++;
if (idx == 1)
{
audioPlayer = mp;
lbl_tDuration.setText(mSecToTimer(mp.getDuration()));
lbl_cDuration.setText(mSecToTimer(mp.getCurrentPosition()));
updateSeekBar();
loadAudio(audioUrl_eng);
}
if (idx == 2)
{
// Enabling the media control buttons
_imgBtnPlay.setClickable(true);
_imgBtnStop.setClickable(true);
rdGrp.setClickable(true);
lbl_loading.setText("");
idx = 0;
}
}
public void onClick(View v)
{
switch(v.getId())
{
// calling search function
case R.id.imgBtnSearch:
onSearchRequested();
break;
// when play/pause button is tapped
case R.id.imgBtnPlay:
if (audioPlayer.isPlaying())
{
audioPlayer.pause();
_imgBtnPlay.setImageResource(R.drawable.ic_action_play);
}
else if (audioPlayer!=null)
{
audioPlayer.start();
_imgBtnPlay.setImageResource(R.drawable.ic_action_pause);
durationHandler.postDelayed(updateSeekBarTime, 100);
}
break;
// when stop button is tapped
case R.id.imgBtnStop:
audioPlayer.pause();
_imgBtnPlay.setImageResource(R.drawable.ic_action_play);
resetProgress();
break;
default:
break;
}
// Updating the seek bar's time after every 100 milliseconds
public void updateSeekBar()
{
durationHandler.postDelayed(updateSeekBarTime, 100);
}
// Updating the progress of seek bar
private Runnable updateSeekBarTime = new Runnable()
{
public void run()
{
long tDuration = audioPlayer.getDuration();
long cDuration = audioPlayer.getCurrentPosition();
lbl_tDuration.setText(mSecToTimer(tDuration));
lbl_cDuration.setText(mSecToTimer(cDuration));
int progress = (int) getProgressPercentage(cDuration, tDuration);
_seekbar.setProgress(progress);
durationHandler.postDelayed(this, 100);
}
};
// Converting milliseconds into min:sec format
public String mSecToTimer(long ms)
{
String finalTimerString = "";
String secString = "";
String minString = "";
// Convert total duration into minutes and seconds
int min = (int)(ms % (1000*60*60)) / (1000*60);
int sec = (int) ((ms % (1000*60*60)) % (1000*60) / 1000);
// Prepending 0 to seconds if it is one digit
if(sec < 10)
secString = "0" + sec;
else
secString = "" + sec;
// Prepending 0 to minutes if it is one digit
if(min < 10)
minString = "0" + min;
else
minString = "" + min;
finalTimerString = minString + ":" + secString;
return finalTimerString;
}
// calculating the percentage progress of seek bar
public int getProgressPercentage(long cDuration, long tDuration)
{
Double percentage = (double) 0;
long cSeconds = (int) (cDuration / 1000);
long tSeconds = (int) (tDuration / 1000);
percentage =(((double)cSeconds)/tSeconds)*100;
return percentage.intValue();
}
// Converting progress of seek bar into time duration in milliseconds
public int progressToTimer(int progress, int tDuration)
{
int cDuration = 0;
tDuration = (int) (tDuration / 1000);
cDuration = (int) ((((double)progress) / 100) * tDuration);
return cDuration * 1000;
}
// Reseting the progress of seek bar when stop button is tapped
public void resetProgress()
{
audioPlayer.seekTo(0);
lbl_cDuration.setText(mSecToTimer(0));
_seekbar.setProgress(0);
}
I have one audio in English and one in Urdu language. both are in the array audioPlayerList. User can select different languages using radio buttons. and idx is variable which tells which audio file is to be played. audio_eng is on index 0 (idx = 0) and audio_urdu is on index 1 (idx = 1). Audio is selected as audioPlayer = audioPlayerList[idx]
code for Radio button selection is this:
rdGrp.setOnCheckedChangeListener(new OnCheckedChangeListener()
{
#Override
public void onCheckedChanged(RadioGroup group, int checkedId)
{
// Find which radio button is selected
if (audioPlayer!=null)
{
if(audioPlayer.isPlaying())
audioPlayer.pause();
_imgBtnPlay.setImageResource(R.drawable.ic_action_play);
resetProgress();
if (checkedId == R.id.rdEng)
audioPlayer = audioPlayerList[0];
else if (checkedId == R.id.rdUrdu)
audioPlayer = audioPlayerList[1];
}
}
});
Duration of your file 'audio_eng' might be less than 1 second. When calculating percentage you convert milliseconds to seconds that results in 0 total length. That's why you get progress bar set to 100% from the beginning (actually, an exception might be thrown in this case - did you check that?).
When calculatng percentage try not to convert xDurationinto xSeconds but divide durations themselves in method getProgressPercentage.
I couldn't find other reasons why you get such result

Trimming video with the help of mp4Parser

I am trying to trim or cut a video for specific time, Example:- Video-1 which is of 30 Sec trim to Video-2 10 sec (0.10 sec to 0.20 sec). I am able to do this and able to play this video but in the end of video it gives error saying- Sorry! cant play this video.
public static void main(String args) throws IOException {
Movie movie = new MovieCreator()
.build(new RandomAccessFileIsoBufferWrapperImpl(
new File(
"/sdcard/Videos11/"+args+".mp4")));
List<Track> tracks = movie.getTracks();
movie.setTracks(new LinkedList<Track>());
// remove all tracks we will create new tracks from the old
double startTime = 3.000;
double endTime = 9.000;
boolean timeCorrected = false;
// Here we try to find a track that has sync samples. Since we can only
// start decoding
// at such a sample we SHOULD make sure that the start of the new
// fragment is exactly
// such a frame
for (Track track : tracks) {
if (track.getSyncSamples() != null
&& track.getSyncSamples().length > 0) {
if (timeCorrected) {
// This exception here could be a false positive in case we
// have multiple tracks
// with sync samples at exactly the same positions. E.g. a
// single movie containing
// multiple qualities of the same video (Microsoft Smooth
// Streaming file)
throw new RuntimeException(
"The startTime has already been corrected by another track with SyncSample. Not Supported.");
}
}
for (Track track : tracks) {
long currentSample = 0;
double currentTime = 0;
long startSample = -1;
long endSample = -1;
for (int i = 0; i < track.getDecodingTimeEntries().size(); i++) {
TimeToSampleBox.Entry entry = track.getDecodingTimeEntries().get(i);
for (int j = 0; j < entry.getCount(); j++) {
// entry.getDelta() is the amount of time the current sample
// covers.
if (currentTime <= startTime) {
// current sample is still before the new starttime
startSample = currentSample;
}
if (currentTime <= endTime) {
// current sample is after the new start time and still
// before the new endtime
endSample = currentSample;
} else {
// current sample is after the end of the cropped video
break;
}
currentTime += (double) entry.getDelta()
/ (double) track.getTrackMetaData().getTimescale();
currentSample++;
}
}
movie.addTrack(new CroppedTrack(track, startSample, endSample));
}
IsoFile out = new DefaultMp4Builder().build(movie);
String filePath = "sdcard/test"+i+".mp4";
i++;
File f = new File(filePath);
FileOutputStream fos = new FileOutputStream(f);
BufferedOutputStream bos = new BufferedOutputStream(fos, 65535);
out.getBox(new IsoOutputStream(bos));
bos.close();
fos.close();
}
P.S:-I am not much familiar with this code but some how am able to trim video but it shows error in last.
You should "normalize" your start and stop values them to correspond to mp4 sync samples, otherwise you'll get some glitches on an output video or even broken file.
Please have a look at correctTimeToSyncSample method:
Android Gallery source (uses mp4parser 0.9.x, I guess)
My sample project (uses mp4parser 1.1.18).
private static double correctTimeToSyncSample(Track track, double cutHere, boolean next) {
double[] timeOfSyncSamples = new double[track.getSyncSamples().length];
long currentSample = 0;
double currentTime = 0;
for (int i = 0; i = 0) {
timeOfSyncSamples[Arrays.binarySearch(track.getSyncSamples(), currentSample + 1)] = currentTime;
}
currentTime += (double) delta / (double) track.getTrackMetaData().getTimescale();
currentSample++;
}
double previous = 0;
for (double timeOfSyncSample : timeOfSyncSamples) {
if (timeOfSyncSample > cutHere) {
if (next) {
return timeOfSyncSample;
} else {
return previous;
}
}
previous = timeOfSyncSample;
}
return timeOfSyncSamples[timeOfSyncSamples.length - 1];
}
Usage:
for (Track track : tracks) {
if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {
if (timeCorrected) {
throw new RuntimeException("The startTime has already been corrected by another track with SyncSample. Not Supported.");
}
startTime = correctTimeToSyncSample(track, startTime, false);
endTime = correctTimeToSyncSample(track, endTime, true);
timeCorrected = true;
}
}
Maybe the position which you want to cut is at the middle of an Iframe. I'm facing a similar problem. I can not cut exactly where I want, because at the begin of video I get bad frames. Did you get bad frames at the begin of trimmed video?

How can I get the current UTC time online in Android?

I am using the UTC timezone and the time in my application to get data. In the application, wherever the user can get the UTC time and used for getting the data.
I used this method to get the UTC time.
String format = "yyyy-MM-dd HH:mm:ss";
final SimpleDateFormat sdf = new SimpleDateFormat(format);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String utcTime = sdf.format(new Date());
The application is working fine. But it is converting the system time to UTC time. The problem is that sometimes the user can change the time to the wrong time. So the data is not coming.
Case for the above issue is:
For example the current date time in India is Thursday, 26 July 2012, 14:27:56, time zone Kolkata. Then the time, Pacific time zone, should be Thursday, 26 July 2012, 01:59:30 PDT.
But the user changed his device time from 14:27:56 to 13:27:56, so the converted UTC time will be Thursday, 26 July 2012, 00:59:30 PDT. At this point my app is not able to get the date because of the one-hour difference.
I don't want to use date, Calendar classes for Java and I don't want to use the device time. How can I get the UTC time directly, without involving the device's time, date. Is there any open-source API for that?
Thanks in advance.
UTC Time over SntpClient above don't work if the the Sytem time changed e.g. manually for 8 hours. Because it using System.currentTimeMillis wich returns the false value!!!
// get current time and write it to the request packet
long requestTime = System.currentTimeMillis();
long requestTicks = SystemClock.elapsedRealtime();
Better use this Class to get the right UTC Time from NTP Server:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
class NTP_UTC_Time
{
private static final String TAG = "SntpClient";
private static final int RECEIVE_TIME_OFFSET = 32;
private static final int TRANSMIT_TIME_OFFSET = 40;
private static final int NTP_PACKET_SIZE = 48;
private static final int NTP_PORT = 123;
private static final int NTP_MODE_CLIENT = 3;
private static final int NTP_VERSION = 3;
// Number of seconds between Jan 1, 1900 and Jan 1, 1970
// 70 years plus 17 leap days
private static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L;
private long mNtpTime;
public boolean requestTime(String host, int timeout) {
try {
DatagramSocket socket = new DatagramSocket();
socket.setSoTimeout(timeout);
InetAddress address = InetAddress.getByName(host);
byte[] buffer = new byte[NTP_PACKET_SIZE];
DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, NTP_PORT);
buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);
writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
socket.send(request);
// read the response
DatagramPacket response = new DatagramPacket(buffer, buffer.length);
socket.receive(response);
socket.close();
mNtpTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
} catch (Exception e) {
// if (Config.LOGD) Log.d(TAG, "request time failed: " + e);
return false;
}
return true;
}
public long getNtpTime() {
return mNtpTime;
}
/**
* Reads an unsigned 32 bit big endian number from the given offset in the buffer.
*/
private long read32(byte[] buffer, int offset) {
byte b0 = buffer[offset];
byte b1 = buffer[offset+1];
byte b2 = buffer[offset+2];
byte b3 = buffer[offset+3];
// convert signed bytes to unsigned values
int i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0);
int i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1);
int i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2);
int i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3);
return ((long)i0 << 24) + ((long)i1 << 16) + ((long)i2 << 8) + (long)i3;
}
/**
* Reads the NTP time stamp at the given offset in the buffer and returns
* it as a system time (milliseconds since January 1, 1970).
*/
private long readTimeStamp(byte[] buffer, int offset) {
long seconds = read32(buffer, offset);
long fraction = read32(buffer, offset + 4);
return ((seconds - OFFSET_1900_TO_1970) * 1000) + ((fraction * 1000L) / 0x100000000L);
}
/**
* Writes 0 as NTP starttime stamp in the buffer. --> Then NTP returns Time OFFSET since 1900
*/
private void writeTimeStamp(byte[] buffer, int offset) {
int ofs = offset++;
for (int i=ofs;i<(ofs+8);i++)
buffer[i] = (byte)(0);
}
}
And use it with:
long now = 0;
NTP_UTC_Time client = new NTP_UTC_Time();
if (client.requestTime("pool.ntp.org", 2000)) {
now = client.getNtpTime();
}
If you need UTC Time "now" as DateTimeString use function:
private String get_UTC_Datetime_from_timestamp(long timeStamp){
try{
Calendar cal = Calendar.getInstance();
TimeZone tz = cal.getTimeZone();
int tzt = tz.getOffset(System.currentTimeMillis());
timeStamp -= tzt;
// DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.getDefault());
DateFormat sdf = new SimpleDateFormat();
Date netDate = (new Date(timeStamp));
return sdf.format(netDate);
}
catch(Exception ex){
return "";
}
}
and use it with:
String UTC_DateTime = get_UTC_Datetime_from_timestamp(now);
long dateInMillis = System.currentTimeMillis();
This will return you the number of milliseconds since January 1, 1970 00:00:00 UTC.
Then just parse it to the most suitable format you want:
String format = "yyyy-MM-dd HH:mm:ss";
final SimpleDateFormat sdf = new SimpleDateFormat(format);
String dateString = sdf.format(new Date(dateInMillis)));
I think you must implement a Sntp Client and access NTP to get network time.
My code:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Date;
public class GetTime {
public static void main(String[] args) {
SntpClient client = new SntpClient();
if (client.requestTime("pool.ntp.org", 30000)) {
long now = client.getNtpTime() + System.nanoTime() / 1000
- client.getNtpTimeReference();
Date current = new Date(now);
System.out.println(current.toString());
}
}
}
class SntpClient {
private static final int ORIGINATE_TIME_OFFSET = 24;
private static final int RECEIVE_TIME_OFFSET = 32;
private static final int TRANSMIT_TIME_OFFSET = 40;
private static final int NTP_PACKET_SIZE = 48;
private static final int NTP_PORT = 123;
private static final int NTP_MODE_CLIENT = 3;
private static final int NTP_VERSION = 3;
// Number of seconds between Jan 1, 1900 and Jan 1, 1970
// 70 years plus 17 leap days
private static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L;
// system time computed from NTP server response
private long mNtpTime;
// value of SystemClock.elapsedRealtime() corresponding to mNtpTime
private long mNtpTimeReference;
// round trip time in milliseconds
private long mRoundTripTime;
/**
* Sends an SNTP request to the given host and processes the response.
*
* #param host
* host name of the server.
* #param timeout
* network timeout in milliseconds.
* #return true if the transaction was successful.
*/
public boolean requestTime(String host, int timeout) {
try {
DatagramSocket socket = new DatagramSocket();
socket.setSoTimeout(timeout);
InetAddress address = InetAddress.getByName(host);
byte[] buffer = new byte[NTP_PACKET_SIZE];
DatagramPacket request = new DatagramPacket(buffer, buffer.length,
address, NTP_PORT);
// set mode = 3 (client) and version = 3
// mode is in low 3 bits of first byte
// version is in bits 3-5 of first byte
buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);
// get current time and write it to the request packet
long requestTime = System.currentTimeMillis();
long requestTicks = System.nanoTime() / 1000;
writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);
socket.send(request);
// read the response
DatagramPacket response = new DatagramPacket(buffer, buffer.length);
socket.receive(response);
long responseTicks = System.nanoTime() / 1000;
long responseTime = requestTime + (responseTicks - requestTicks);
socket.close();
// extract the results
long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET);
long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
long roundTripTime = responseTicks - requestTicks
- (transmitTime - receiveTime);
// receiveTime = originateTime + transit + skew
// responseTime = transmitTime + transit - skew
// clockOffset = ((receiveTime - originateTime) + (transmitTime -
// responseTime))/2
// = ((originateTime + transit + skew - originateTime) +
// (transmitTime - (transmitTime + transit - skew)))/2
// = ((transit + skew) + (transmitTime - transmitTime - transit +
// skew))/2
// = (transit + skew - transit + skew)/2
// = (2 * skew)/2 = skew
long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime)) / 2;
// if (Config.LOGD) Log.d(TAG, "round trip: " + roundTripTime +
// " ms");
// if (Config.LOGD) Log.d(TAG, "clock offset: " + clockOffset +
// " ms");
// save our results - use the times on this side of the network
// latency
// (response rather than request time)
mNtpTime = responseTime + clockOffset;
mNtpTimeReference = responseTicks;
mRoundTripTime = roundTripTime;
} catch (Exception e) {
return false;
}
return true;
}
/**
* Returns the time computed from the NTP transaction.
*
* #return time value computed from NTP server response.
*/
public long getNtpTime() {
return mNtpTime;
}
/**
* Returns the reference clock value (value of
* SystemClock.elapsedRealtime()) corresponding to the NTP time.
*
* #return reference clock corresponding to the NTP time.
*/
public long getNtpTimeReference() {
return mNtpTimeReference;
}
/**
* Returns the round trip time of the NTP transaction
*
* #return round trip time in milliseconds.
*/
public long getRoundTripTime() {
return mRoundTripTime;
}
/**
* Reads an unsigned 32 bit big endian number from the given offset in the
* buffer.
*/
private long read32(byte[] buffer, int offset) {
byte b0 = buffer[offset];
byte b1 = buffer[offset + 1];
byte b2 = buffer[offset + 2];
byte b3 = buffer[offset + 3];
// convert signed bytes to unsigned values
int i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0);
int i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1);
int i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2);
int i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3);
return ((long) i0 << 24) + ((long) i1 << 16) + ((long) i2 << 8)
+ (long) i3;
}
/**
* Reads the NTP time stamp at the given offset in the buffer and returns it
* as a system time (milliseconds since January 1, 1970).
*/
private long readTimeStamp(byte[] buffer, int offset) {
long seconds = read32(buffer, offset);
long fraction = read32(buffer, offset + 4);
return ((seconds - OFFSET_1900_TO_1970) * 1000)
+ ((fraction * 1000L) / 0x100000000L);
}
/**
* Writes system time (milliseconds since January 1, 1970) as an NTP time
* stamp at the given offset in the buffer.
*/
private void writeTimeStamp(byte[] buffer, int offset, long time) {
long seconds = time / 1000L;
long milliseconds = time - seconds * 1000L;
seconds += OFFSET_1900_TO_1970;
// write seconds in big endian format
buffer[offset++] = (byte) (seconds >> 24);
buffer[offset++] = (byte) (seconds >> 16);
buffer[offset++] = (byte) (seconds >> 8);
buffer[offset++] = (byte) (seconds >> 0);
long fraction = milliseconds * 0x100000000L / 1000L;
// write fraction in big endian format
buffer[offset++] = (byte) (fraction >> 24);
buffer[offset++] = (byte) (fraction >> 16);
buffer[offset++] = (byte) (fraction >> 8);
// low order bits should be random data
buffer[offset++] = (byte) (Math.random() * 255.0);
}
}
Hope that can help you.
Using the free API at http://www.worldweatheronline.com/, you can perform a HTTP GET via java to get the current time in UTC (http://www.worldweatheronline.com/time-zone-api.aspx)
For android, if you tried to connect to the Internet from the main thread the app will crash, so I edited to code to work correctly on android
import android.os.AsyncTask;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class NTPUTCTime {
private static final String TAG = "SntpClient";
private static final int RECEIVE_TIME_OFFSET = 32;
private static final int TRANSMIT_TIME_OFFSET = 40;
private static final int NTP_PACKET_SIZE = 48;
private static final int NTP_PORT = 123;
private static final int NTP_MODE_CLIENT = 3;
private static final int NTP_VERSION = 3;
// Number of seconds between Jan 1, 1900 and Jan 1, 1970
// 70 years plus 17 leap days
private static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L;
private long mNtpTime;
public boolean requestTime() {
try {
new AsyncTask<Void, Void, Boolean>() {
#Override
protected Boolean doInBackground(Void... voids) {
try {
DatagramSocket socket = new DatagramSocket();
socket.setSoTimeout(1000);
InetAddress address = InetAddress.getByName("pool.ntp.org");
byte[] buffer = new byte[NTP_PACKET_SIZE];
DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, NTP_PORT);
buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);
writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
socket.send(request);
// read the response
DatagramPacket response = new DatagramPacket(buffer, buffer.length);
socket.receive(response);
socket.close();
mNtpTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
} catch (Exception e) {
// if (Config.LOGD) Log.d(TAG, "request time failed: " + e);
e.printStackTrace();
return false;
}
return true;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR).get();
} catch (Exception e) {
// if (Config.LOGD) Log.d(TAG, "request time failed: " + e);
e.printStackTrace();
return false;
}
return true;
}
public long getNtpTime() {
return mNtpTime;
}
/**
* Reads an unsigned 32 bit big endian number from the given offset in the buffer.
*/
private long read32(byte[] buffer, int offset) {
byte b0 = buffer[offset];
byte b1 = buffer[offset + 1];
byte b2 = buffer[offset + 2];
byte b3 = buffer[offset + 3];
// convert signed bytes to unsigned values
int i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0);
int i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1);
int i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2);
int i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3);
return ((long) i0 << 24) + ((long) i1 << 16) + ((long) i2 << 8) + (long) i3;
}
/**
* Reads the NTP time stamp at the given offset in the buffer and returns
* it as a system time (milliseconds since January 1, 1970).
*/
private long readTimeStamp(byte[] buffer, int offset) {
long seconds = read32(buffer, offset);
long fraction = read32(buffer, offset + 4);
return ((seconds - OFFSET_1900_TO_1970) * 1000) + ((fraction * 1000L) / 0x100000000L);
}
/**
* Writes 0 as NTP starttime stamp in the buffer. --> Then NTP returns Time OFFSET since 1900
*/
private void writeTimeStamp(byte[] buffer, int offset) {
int ofs = offset++;
for (int i = ofs; i < (ofs + 8); i++)
buffer[i] = (byte) (0);
}
}
You can use Joda library to manipulate date/time data
class GetUTCTime : AppCompatActivity() {
private val OUTPUT_DATE_FORMATE = "dd-MM-yyyy - hh:mm a"
internal lateinit var textView : TextView
val utcTime: String
get() {
LongOperation().execute("")
return ""
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_get_utctime)
// Log.d("UTC", "onCreate: "+ getUTCTime());
textView = findViewById(R.id.dateTime)
findViewById<View>(R.id.fetchButton).setOnClickListener {
utcTime
textView.text = "Fetching..."
}
}
private inner class LongOperation : AsyncTask<String, Void, Long>() {
override fun doInBackground(vararg params: String): Long? {
var nowAsPerDeviceTimeZone: Long = 0
try {
val sntpClient = SntpClient()
if (sntpClient.requestTime("ntp.ubuntu.com", 30000)) {
nowAsPerDeviceTimeZone = sntpClient.ntpTime
}
Thread.sleep(1000)
} catch (e: InterruptedException) {
Thread.interrupted()
}
return nowAsPerDeviceTimeZone
}
override fun onPostExecute(nowAsPerDeviceTimeZone: Long?) {
// might want to change "executed" for the returned string passed
// into onPostExecute() but that is upto you
Log.d("GMT", "getUTCTime:0 " + Date(nowAsPerDeviceTimeZone!!))
textView.text = Date(nowAsPerDeviceTimeZone).toString()
// Calendar cal = Calendar.getInstance();
// TimeZone timeZoneInDevice = cal.getTimeZone();
// int differentialOfTimeZones = timeZoneInDevice.getOffset(System.currentTimeMillis());
// nowAsPerDeviceTimeZone -= differentialOfTimeZones;
//
//
//
//
//
// Log.d("GMT", "getUTCTime:1 "+ new Date(nowAsPerDeviceTimeZone).toString());
}
override fun onPreExecute() {}
override fun onProgressUpdate(vararg values: Void) {}
}
}
Find the SntpClient here

Categories

Resources