I have a file movie.mp4 that I wish to create a preview of and play in a VideoView. For the purpose of this question let's say movie.mp4 is 30 megabytes.
I want to extract the first 10 megabytes of video from movie.mp4 and save it in a temporary file. I will feed this file into the VideoView and play it.
This is what I've done so far:
// Get movie file.
File f = getMovieFile("movie.mp4");
// Create temporary file.
File t = File.createTempFile("preview", "mp4");
t.deleteOnExit();
// Create input stream.
InputStream is = new FileInputStream(f);
BufferedInputStream bis = new BufferedInputStream(is, 2048);
// Create output stream to temp file.
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(t));
byte[] buffer = new byte[16384];
int totalRead = 0;
int bytesRead;
int maxBytes = (int) Math.pow(2, 20) * 10; // 10 megabytes
// Write data from input to output.
while ((bytesRead = bis.read(buffer)) != -1 && totalRead < maxBytes) {
bos.write(buffer, 0, bytesRead);
bos.flush();
totalRead += bytesRead;
}
bos.close();
// Play the preview.
mVideoView.setVideoPath(t.getAbsolutePath());
mVideoView.start();
When I run this bit of code Android complains and tells me that the file is unplayable. If I remove the 10 megabyte restriction and let the loop run its course, the VideoView will play the file just fine.
I'm guessing that when the preview is created and only data from one part of the file is copied, some important meta-data from the original mp4 file is missing in the new one. This is just a guess though.
The problem could be in your video,
a partial mp4 must have metadata at the beginning of the file.
While there are tons of articles about encoding MPEG 4 videos with FFmpeg, most of them fail to warn you about something: most video players will not start the playback until the metadata is available (meaning if that data is placed at the back-end of file, as done by some encoders, the whole video must be downloaded first to reach the metadata & playback begins).
This example use ffmpeg for create a mp4 faststart video:
ffmpeg.exe -i INPUT_FILE_NAME_HERE -c:v libx264 -profile:v baseline
-preset slow -b:v 800k -c:a libvo_aacenc -movflags faststart OUTPUT_FILE_NAME.mp4
Related
I'm developing a sound recognition app on android and am using the MediaRecorder class as well as a tensorflow model. I create the audio file where I will be saving the recorder microphone audio in the onCreate method for the class
audioFile = getExternalCacheDir().getAbsolutePath();
audioFile += "/Recording.3gp";
And I set the output file of the mediaRecorder to this file in the startRecording class
mediaRecorder.setOutputFile(audioFile);
The issue im having is that I need to convert the recording into a series of MFCC values for the model to work, and the MFFC.java class im using requires that the recording be converted to a double array. I'm doing that like this
ByteArrayOutputStream out = new ByteArrayOutputStream();
BufferedInputStream in = new BufferedInputStream(new FileInputStream(audioFile));
int read;
byte[] buff = new byte[1024];
while ((read = in.read(buff)) > 0)
{
out.write(buff, 0, read);
}
out.flush();
byte[] bytes = out.toByteArray();
int times = Double.SIZE / Byte.SIZE;
double[] doubleArray = new double[bytes.length / times];
for(int i=0;i<doubleArray.length;i++){
doubleArray[i] = ByteBuffer.wrap(bytes, i*times, times).getDouble();
}
Which is how they said to do it on another stack overflow post. The issue with this is that the audio file im sending the recording(s) to just keeps adding the new recordings to previous ones. This is because I am recording the audio then passing it to my classifier method in a loop like so
while(true){
try {
soundRecognition task = new soundRecognition();
task.execute();
sleep(1500);
}
Solutions I have tried
I have tried to move the creation of the audio to the sound recognition class but I cant do that as it produces errors, specifically mediaRecorder start called in invalid state: 4.
I have tried to overwrite the file using a FileWriter and PrintWriter class, but this didnt work, im assuming because the file is and audio file.
Any help would be appreciated
Is there any other way to convert a file (PDF) into byte array other than using FileInputStream or toByteArray(InputStream input)..
Is there any method to convert it directly. I found Files.readAllBytes in java.nio.file.* package but it gives ClassNotFoundException in my RAD. I am having Java 8 JDK in my system.
Is java.nio.file.* package not available in Java 8?
My requirement is that i should not to stream the file using InputStram.
Seems like you're loading the whole content of your file into a byte[] directly in memory, then writing this in the OutputStream. The problem with this approach is that if you load files of 1 or 2 GBs entirely in memory then you will encounter with OutOfMemoryError quickly. To avoid this, you should read the data from InputStream in small chunks and write these chunks in the output stream. Here's an example for file downloading:
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(new File("/path/to/folder", "file.pdf")));
ServletOutputStream outStream = response.getOutputStream();
//to make it easier to change to 8 or 16 KBs
//make some tests to determine the best performance for your case
int FILE_CHUNK_SIZE = 1024 * 4;
byte[] chunk = new byte[FILE_CHUNK_SIZE];
int bytesRead = 0;
while ((bytesRead = bis.read(chunk)) != -1) {
outStream.write(chunk, 0, bytesRead);
}
bis.close();
outStream.flush();
outStream.close();
In my program I had to remove certain frames from video and audio so that advertisments are removed. The video is stored in .rgb file and audio is stored in .wav file.
The video is working fine. But when I try and write only a few frames from one wav file to the output wav file it is giving an error while playing.
Though the program is writing it properly and I can play it in Real Player, the audio gives an error called mark/reset not supported and unable to get position error when I run it on the code same code to play wav file that professor has given.
I cant find the mistake in the writing audio function. Here is the function
public void do_audioWrite() throws IOException
{
long offset=0;
long offset2=0;
int audioInfoSize = 2; //audio info is in bytes
long FRAMESIZE= 352*288*(24/8);
double MAX_POSSIBLE_FRAMES=AdvRemove.file_len/FRAMESIZE;
double AUDIO_PER_FRAME = 2000;
long videoFrames=0;
byte[] bytes = new byte[4000];
byte[] head = new byte[44];
RandomAccessFile raf = new RandomAccessFile(AdvRemove.afilename,"r");
OutputStream outputStream = new FileOutputStream(AdvRemove.write_audio);
raf.seek(0);
raf.read(head);
outputStream.write(head);
for (offset=44; ((offset < raf.length()) && (videoFrames < MAX_POSSIBLE_FRAMES)); offset+=2*AUDIO_PER_FRAME)
{
if(Group1.size()>Group2.size())
{
if(Group1.contains((int)videoFrames))
{
raf.seek(offset);
raf.read(bytes);
outputStream.write(bytes);
}
}
else if(Group1.size()<Group2.size())
{
if(Group2.contains((int)videoFrames))
{
raf.seek(offset);
raf.read(bytes);
outputStream.write(bytes);
}
}
videoFrames++;
}
}
ABOVE IS MY FUNCTION TO WRITE THE AUDIO.
I only write an audio frame if the corresponding video frame is found in my arrayList called Group1 and Group2
First I took the header of input wav file and write it to the output wav file.
Then I check if corresponding video frame exists. If yes, I write the audio frames corresponding to that video frame.
You need to update the output file header to be correct for the new file you are creating.
outputStream.write(head);
Here you are simply copy the original files header, but it looks like in your loop you are not always copying a sample from the original file to your stripped file (i.e your output file is going to be shorter than the input file). The wav file format has information in the header regarding the size of the file (see https://ccrma.stanford.edu/courses/422-winter-2014/projects/WaveFormat/ for a break down of the header), you need to update the header to have the correct data length for your new file.
I am using GZIPInputStream to download PDF file. I want to show the download progress of the file on a UI button. But, I am not getting the actual size of the file, what I am getting is compressed size due to which I am unable to show the correct download progress. This download progress is exceeding 100 as the actual file size is greater than the compressed size of file.
Header content of file from server: Following info I receive from server, from which I am using content-length which is giving compressed file size.
1.Connection
2.Content-Encoding
3.Content-length
4.Content-Type
5.Keep-Alive
6.Server
7.Date
Here is my code. Is there any way to get original size of file?
long fileLength = httpResponse.getEntity().getContentLength();//
GZIPInputStream input = new GZIPInputStream(new BufferedInputStream(
httpResponse.getEntity().getContent()));
FileOutputStream output = new FileOutputStream(destinationFilePath);
byte data[] = new byte[1024];
long total = 0;
float percentage = 0;
int count;
currentDownloadingPercentage=0;
while ((count = input.read(data)) != -1) {
total += count;
output.write(data, 0, count);
// publishing the progress....
percentage = (float)total/(float)fileLength;
percentage *= 100;
if ((int)percentage > (int)currentDownloadingPercentage) {
currentDownloadingPercentage = percentage;
Bundle resultData = new Bundle();
resultData.putBoolean(DOWNLOAD_FAILED, false);
resultData.putInt(DOWNLOAD_PROGRESS ,(int)percentage);
receiver.send(processID, resultData);
resultData = null;
}
}
You're looking at it the wrong way. You should be counting the compressed bytes that you read and calculating the progress based on those. Instead, you're counting the decompressed bytes and comparing it with the compressed file size. In answer to your question, there's no (reliable) way to determine the size of a gzipped file without decompressing it.
Update: Here's one way you could count the uncompressed bytes coming in. Wrap the raw input stream with a TeeInputStream before wrapping it with the GZIPInputStream. Make the TeeInputStream branch to a CountingOutputStream. Then you'll always have a current count of the compressed bytes that have been downloaded via getByteCount()
This issue discuss result seems not way to avoid HttpURLConnection.getInputStream() automatically returned GZIPInputStream, once you let HttpURLConnection accept gzip compression, you won't calculate download progress accurately, the only one we can do just disable gzip as acceptable encoding :
HttpURLConnection.setRequestProperty("Accept-Encoding", "identity");
Another choice is use AndroidHttpClient, I had tested about this, even we present accept gzip encoding like this :
HttpUriRequest.addHeader("Accept-Encoding", "gzip");
the InputStream instance that return by HttpResponse.getEntity().getContent() will be EofSensorInputStream, an original InputStream is what we wanted, isn't GZIPInputStream, that make us possible to wrap it to GZIPInputStream by myself, we can use TeeInputStream and CountingOutputStream to finish calculating download progress.
HttpResponse response = ...;
HttpEntity entity = response.getEntity();
long fileSize = entity.getContentLength();
InputStream ins = entity.getContent(); // instance of EofSensorInputStream
CountingOutputStream coStrem = new CountingOutputStream(new ByteArrayOutputStream(100));
GZIPInputStream inStrem = new GZIPInputStream(new TeeInputStream(ins, coStrem, true));
byte[] buffer = new byte[6 * 1024]; // 6K buffer
int offset;
while ((offset = inStrem.read(buffer)) != -1) {
tmpFileRaf.write(buffer, 0, offset);
postDownloadProgress(fileSize, coStrem.getByteCount());
}
I think that's all we can do with this problem, I tried pick up android libcore source with my project so we can customize HttpURLConnectionImpl then suppress it return GZIPInputStream, but many errors makes trouble, I discard this effort.
In this post, Jesse Wilson suggested we the best choice client of Android is HttpURLConnection, so I'm looking for how to solve this problem always, I hope I can get a way soon.
I am transferring file from client to server. I dont know the amount of time it will take to transfer. But my UI will simple remain the same without any intimation to user. I need to keep a progress bar in such a way it should be progress till file is uploaded. How can i acheive this.
I am abit aware of this scenario in .net. but how can we do it in java?
trashgod's answer is correct for actions that are truly 'indeterminate'. Why do you think that your file transfer fits into this category? Haven't you ever downloaded a file on the internet with some sort of progress bar associated with it? Can you imagine not having that?
See the example below that was provided among the answers to How do I use JProgressBar to display file copy progress?
public OutputStream loadFile(URL remoteFile, JProgressBar progress) throws IOException
{
URLConnection connection = remoteFile.openConnection(); //connect to remote file
InputStream inputStream = connection.getInputStream(); //get stream to read file
int length = connection.getContentLength(); //find out how long the file is, any good webserver should provide this info
int current = 0;
progress.setMaximum(length); //we're going to get this many bytes
progress.setValue(0); //we've gotten 0 bytes so far
ByteArrayOutputStream out = new ByteArrayOutputStream(); //create our output steam to build the file here
byte[] buffer = new byte[1024];
int bytesRead = 0;
while((bytesRead = inputStream.read(buffer)) != -1) //keep filling the buffer until we get to the end of the file
{
out.write(buffer, current, bytesRead); //write the buffer to the file offset = current, length = bytesRead
current += bytesRead; //we've progressed a little so update current
progress.setValue(current); //tell progress how far we are
}
inputStream.close(); //close our stream
return out;
}
As shown in How to Use Progress Bars, you can specify indeterminate mode until you either have enough data to gauge progress or the download concludes. The exact implementation depends on how the transfer takes place. Ideally, the sender provides the length first, but it may also be possible to calculate the rate dynamically as data accumulates.