I have a series of mp4 files saved on the device that need to be merged together to make a single mp4 file.
video_p1.mp4 video_p2.mp4 video_p3.mp4 > video.mp4
The solutions I have researched such as the mp4parser framework use deprecated code.
The best solution I could find is using a MediaMuxer and MediaExtractor.
The code runs but my videos are not merged (only the content in video_p1.mp4 is displayed and it is in landscape orientation, not portrait).
Can anyone help me sort this out?
public static boolean concatenateFiles(File dst, File... sources) {
if ((sources == null) || (sources.length == 0)) {
return false;
}
boolean result;
MediaExtractor extractor = null;
MediaMuxer muxer = null;
try {
// Set up MediaMuxer for the destination.
muxer = new MediaMuxer(dst.getPath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
// Copy the samples from MediaExtractor to MediaMuxer.
boolean sawEOS = false;
//int bufferSize = MAX_SAMPLE_SIZE;
int bufferSize = 1 * 1024 * 1024;
int frameCount = 0;
int offset = 100;
ByteBuffer dstBuf = ByteBuffer.allocate(bufferSize);
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
long timeOffsetUs = 0;
int dstTrackIndex = -1;
for (int fileIndex = 0; fileIndex < sources.length; fileIndex++) {
int numberOfSamplesInSource = getNumberOfSamples(sources[fileIndex]);
// Set up MediaExtractor to read from the source.
extractor = new MediaExtractor();
extractor.setDataSource(sources[fileIndex].getPath());
// Set up the tracks.
SparseIntArray indexMap = new SparseIntArray(extractor.getTrackCount());
for (int i = 0; i < extractor.getTrackCount(); i++) {
extractor.selectTrack(i);
MediaFormat format = extractor.getTrackFormat(i);
if (dstTrackIndex < 0) {
dstTrackIndex = muxer.addTrack(format);
muxer.start();
}
indexMap.put(i, dstTrackIndex);
}
long lastPresentationTimeUs = 0;
int currentSample = 0;
while (!sawEOS) {
bufferInfo.offset = offset;
bufferInfo.size = extractor.readSampleData(dstBuf, offset);
if (bufferInfo.size < 0) {
sawEOS = true;
bufferInfo.size = 0;
timeOffsetUs += (lastPresentationTimeUs + 0);
}
else {
lastPresentationTimeUs = extractor.getSampleTime();
bufferInfo.presentationTimeUs = extractor.getSampleTime() + timeOffsetUs;
bufferInfo.flags = extractor.getSampleFlags();
int trackIndex = extractor.getSampleTrackIndex();
if ((currentSample < numberOfSamplesInSource) || (fileIndex == sources.length - 1)) {
muxer.writeSampleData(indexMap.get(trackIndex), dstBuf, bufferInfo);
}
extractor.advance();
frameCount++;
currentSample++;
Log.d("tag2", "Frame (" + frameCount + ") " +
"PresentationTimeUs:" + bufferInfo.presentationTimeUs +
" Flags:" + bufferInfo.flags +
" TrackIndex:" + trackIndex +
" Size(KB) " + bufferInfo.size / 1024);
}
}
extractor.release();
extractor = null;
}
result = true;
}
catch (IOException e) {
result = false;
}
finally {
if (extractor != null) {
extractor.release();
}
if (muxer != null) {
muxer.stop();
muxer.release();
}
}
return result;
}
public static int getNumberOfSamples(File src) {
MediaExtractor extractor = new MediaExtractor();
int result;
try {
extractor.setDataSource(src.getPath());
extractor.selectTrack(0);
result = 0;
while (extractor.advance()) {
result ++;
}
}
catch(IOException e) {
result = -1;
}
finally {
extractor.release();
}
return result;
}
I'm using this library for muxing videos: ffmpeg-android-java
gradle dependency:
implementation 'com.writingminds:FFmpegAndroid:0.3.2'
Here's how I use it in my project to mux video and audio in kotlin: VideoAudioMuxer
So basically it works like the ffmpeg in terminal but you're inputing your command to a method as an array of strings along with a listener.
fmpeg.execute(arrayOf("-i", videoPath, "-i", audioPath, "$targetPath.mp4"), object : ExecuteBinaryResponseHandler() {
You'll have to search how to merge videos in ffmpeg and convert the commands into array of strings for the argument you need.
You could probably do almost anything, since ffmpeg is a very powerful tool.
Related
I have a csv file, after I overwrite 1 line with the Write method, after re-writing to the file everything is already added to the end of the file, and not to a specific line
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
using System.Text;
using System.IO;
public class LoadQuestion : MonoBehaviour
{
int index;
string path;
FileStream file;
StreamReader reader;
StreamWriter writer;
public Text City;
public string[] allQuestion;
public string[] addedQuestion;
private void Start()
{
index = 0;
path = Application.dataPath + "/Files/Questions.csv";
allQuestion = File.ReadAllLines(path, Encoding.GetEncoding(1251));
file = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
writer = new StreamWriter(file, Encoding.GetEncoding(1251));
reader = new StreamReader(file, Encoding.GetEncoding(1251));
writer.AutoFlush = true;
List<string> _questions = new List<string>();
for (int i = 0; i < allQuestion.Length; i++)
{
char status = allQuestion[i][0];
if (status == '0')
{
_questions.Add(allQuestion[i]);
}
}
addedQuestion = _questions.ToArray();
City.text = ParseToCity(addedQuestion[0]);
}
private string ParseToCity(string current)
{
string _city = "";
string[] data = current.Split(';');
_city = data[2];
return _city;
}
private void OnApplicationQuit()
{
writer.Close();
reader.Close();
file.Close();
}
public void IKnow()
{
string[] quest = addedQuestion[index].Split(';');
int indexFromFile = int.Parse(quest[1]);
string questBeforeAnsver = "";
for (int i = 0; i < quest.Length; i++)
{
if (i == 0)
{
questBeforeAnsver += "1";
}
else
{
questBeforeAnsver += ";" + quest[i];
}
}
Debug.Log("indexFromFile : " + indexFromFile);
for (int i = 0; i < allQuestion.Length; i++)
{
if (i == indexFromFile)
{
writer.Write(questBeforeAnsver);
break;
}
else
{
reader.ReadLine();
}
}
reader.DiscardBufferedData();
reader.BaseStream.Seek(0, SeekOrigin.Begin);
if (index < addedQuestion.Length - 1)
{
index++;
}
City.text = ParseToCity(addedQuestion[index]);
}
}
There are lines in the file by type :
0;0;Africa
0;1;London
0;2;Paris
The bottom line is that this is a game, and only those questions whose status is 0, that is, unanswered, are downloaded from the file. And if during the game the user clicks that he knows the answer, then there is a line in the file and is overwritten, only the status is no longer 0, but 1 and when the game is repeated, this question will not load.
It turns out for me that the first question is overwritten successfully, and all subsequent ones are simply added at the end of the file :
1;0;Africa
0;1;London
0;2;Paris1;1;London1;2;Paris
What's wrong ?
The video shows everything in detail
I am trying to split a text file with multiple threads. The file is of 1 GB. I am reading the file by char. The Execution time is 24 min 54 seconds. Instead of reading a file by char is their any better way where I can reduce the execution time.
I'm having a hard time figuring out an approach that will reduce the execution time. Please do suggest me also, if there is any other better way to split file with multiple threads. I am very new to java.
Any help will be appreciated. :)
public static void main(String[] args) throws Exception {
RandomAccessFile raf = new RandomAccessFile("D:\\sample\\file.txt", "r");
long numSplits = 10;
long sourceSize = raf.length();
System.out.println("file length:" + sourceSize);
long bytesPerSplit = sourceSize / numSplits;
long remainingBytes = sourceSize % numSplits;
int maxReadBufferSize = 9 * 1024;
List<String> filePositionList = new ArrayList<String>();
long startPosition = 0;
long endPosition = bytesPerSplit;
for (int i = 0; i < numSplits; i++) {
raf.seek(endPosition);
String strData = raf.readLine();
if (strData != null) {
endPosition = endPosition + strData.length();
}
String str = startPosition + "|" + endPosition;
if (sourceSize > endPosition) {
startPosition = endPosition;
endPosition = startPosition + bytesPerSplit;
} else {
break;
}
filePositionList.add(str);
}
for (int i = 0; i < filePositionList.size(); i++) {
String str = filePositionList.get(i);
String[] strArr = str.split("\\|");
String strStartPosition = strArr[0];
String strEndPosition = strArr[1];
long startPositionFile = Long.parseLong(strStartPosition);
long endPositionFile = Long.parseLong(strEndPosition);
MultithreadedSplit objMultithreadedSplit = new MultithreadedSplit(startPositionFile, endPositionFile);
objMultithreadedSplit.start();
}
long endTime = System.currentTimeMillis();
System.out.println("It took " + (endTime - startTime) + " milliseconds");
}
}
public class MultithreadedSplit extends Thread {
public static String filePath = "D:\\tenlakh\\file.txt";
private int localCounter = 0;
private long start;
private long end;
public static String outPath;
List<String> result = new ArrayList<String>();
public MultithreadedSplit(long startPos, long endPos) {
start = startPos;
end = endPos;
}
#Override
public void run() {
try {
String threadName = Thread.currentThread().getName();
long currentTime = System.currentTimeMillis();
RandomAccessFile file = new RandomAccessFile("D:\\sample\\file.txt", "r");
String outFile = "out_" + threadName + ".txt";
System.out.println("Thread Reading started for start:" + start + ";End:" + end+";threadname:"+threadName);
FileOutputStream out2 = new FileOutputStream("D:\\sample\\" + outFile);
file.seek(start);
int nRecordCount = 0;
char c = (char) file.read();
StringBuilder objBuilder = new StringBuilder();
int nCounter = 1;
while (c != -1) {
objBuilder.append(c);
// System.out.println("char-->" + c);
if (c == '\n') {
nRecordCount++;
out2.write(objBuilder.toString().getBytes());
objBuilder.delete(0, objBuilder.length());
//System.out.println("--->" + nRecordCount);
// break;
}
c = (char) file.read();
nCounter++;
if (nCounter > end) {
break;
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
The fastest way would be to map the file into memory segment by segment (mapping a large file as a whole may cause undesired side effects). It will skip few relatively expensive copy operations. The operating system will load file into RAM and JRE will expose it to your application as a view into an off-heap memory area in a form of a ByteBuffer. It would usually allow you to squeze last 2x/3x of the performance.
Memory-mapped way requires quite a bit of helper code (see the fragment in the bottom), it's not always the best tactical way. Instead, if your input is line-based and you just need reasonable performance (what you have now is probably not) then just do something like:
import java.nio.Files;
import java.nio.Paths;
...
File.lines(Paths.get("/path/to/the/file"), StandardCharsets.ISO_8859_1)
// .parallel() // parallel processing is still possible
.forEach(line -> { /* your code goes here */ });
For the contrast, a working example of the code for working with the file via memory mapping would look something like below. In case of fixed-size records (when segments can be selected precisely to match record boundaries) subsequent segments can be processed in parallel.
static ByteBuffer mapFileSegment(FileChannel fileChannel, long fileSize, long regionOffset, long segmentSize) throws IOException {
long regionSize = min(segmentSize, fileSize - regionOffset);
// small last region prevention
final long remainingSize = fileSize - (regionOffset + regionSize);
if (remainingSize < segmentSize / 2) {
regionSize += remainingSize;
}
return fileChannel.map(FileChannel.MapMode.READ_ONLY, regionOffset, regionSize);
}
...
final ToIntFunction<ByteBuffer> consumer = ...
try (FileChannel fileChannel = FileChannel.open(Paths.get("/path/to/file", StandardOpenOption.READ)) {
final long fileSize = fileChannel.size();
long regionOffset = 0;
while (regionOffset < fileSize) {
final ByteBuffer regionBuffer = mapFileSegment(fileChannel, fileSize, regionOffset, segmentSize);
while (regionBuffer.hasRemaining()) {
final int usedBytes = consumer.applyAsInt(regionBuffer);
if (usedBytes == 0)
break;
}
regionOffset += regionBuffer.position();
}
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
What i am trying to do is when i click a button, i copy some files from a portable drive like usb and copy those files to my local drive, then i read all csv files which i copied ealier and i put it's values in an arraylist and inject it to database, and then i can delete those files, and i want to show the process in progress bar based on process completion. so this is what i do :
void main()
{
JButton btnTransfer = new JButton("Transfer");
Image transferIMG = ImageIO.read(new File("C:\\Users\\User\\Desktop\\images\\transfer.png"));
btnTransfer.setIcon(new ImageIcon(transferIMG));
btnTransfer.setPreferredSize(new Dimension(110, 90));
btnTransfer.setOpaque(false);
btnTransfer.setContentAreaFilled(false);
btnTransfer.setBorderPainted(false);
btnTransfer.setVerticalTextPosition(SwingConstants.BOTTOM);
btnTransfer.setHorizontalTextPosition(SwingConstants.CENTER);
btnTransfer.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
File csvpath = new File(fileList1.getSelectedValue() + "\\salestablet\\report\\csv");
File htmlpath = new File(fileList1.getSelectedValue() + "\\salestablet\\report\\html");
String removepath = fileList1.getSelectedValue() + "\\salestablet\\report";
if(csvpath.listFiles().length > 0 && htmlpath.listFiles().length > 0)
{
File[] csvarr = csvpath.listFiles();
File[] htmlarr = htmlpath.listFiles();
try
{
copyFileUsingStream(csvarr, htmlarr, txt.getText(), removepath);
}
catch (IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
});
JPanel ButtonCont = new JPanel(new GridLayout(4, 1, 5, 0));
ButtonCont.setBackground(Color.LIGHT_GRAY);
ButtonCont.add(btnTransfer);
gui.add(ButtonCont , BorderLayout.EAST);
frame.setContentPane(gui);
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
frame.setMinimumSize(new Dimension(900, 100));
frame.pack();
frame.setLocationByPlatform(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static void copyFileUsingStream(File[] csvsources, File[] htmlsources, String dest, String removepath) throws IOException
{
int count = 0;
int MaxCount = countprocess(csvsources, htmlsources);
progressBar = new JProgressBar(0, MaxCount);
progressBar.setStringPainted(true);
InputStream is = null;
OutputStream os = null;
String csvfolderpath = dest + "\\csv";
String htmlfolderpath = dest + "\\html";
if(!(new File(csvfolderpath)).exists())
{
(new File(csvfolderpath)).mkdirs(); //create csv folder;
}
if(!(new File(htmlfolderpath)).exists())
{
(new File(htmlfolderpath)).mkdirs(); //create csv folder;
}
for(int i= 0; i < csvsources.length; i++) //copy all csv files to csv folder
{
try
{
is = new FileInputStream(csvsources[i]);
os = new FileOutputStream(csvfolderpath + "\\" + csvsources[i].getName());
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0)
{
os.write(buffer, 0, length);
}
}
finally
{
count += 1;
progressBar.setValue((count / MaxCount) * 100);
//progressBar.repaint();
//progressBar.revalidate();
progressBar.update(progressBar.getGraphics());
is.close();
os.close();
}
}
for(int i= 0; i < htmlsources.length; i++) //copy all html, images and css to html folder
{
if(htmlsources[i].isFile())
{
try
{
is = new FileInputStream(htmlsources[i]);
os = new FileOutputStream(htmlfolderpath + "\\" + htmlsources[i].getName());
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0)
{
os.write(buffer, 0, length);
}
}
finally
{
count += 1;
progressBar.setValue((count / MaxCount) * 100);
//progressBar.repaint();
//progressBar.revalidate();
progressBar.update(progressBar.getGraphics());
is.close();
os.close();
}
}
else if(htmlsources[i].isDirectory()) //for subfolder
{
String path = dest + "\\html\\" + htmlsources[i].getName();
if(!new File(path).exists())
{
(new File(path)).mkdirs(); //create subfolder;
}
File[] arr = (new File(htmlsources[i].getAbsolutePath())).listFiles();
for(int j = 0; j < arr.length; j++)
{
if(arr[j].isFile())
{
try
{
is = new FileInputStream(arr[j]);
os = new FileOutputStream(path + "\\" + arr[j].getName());
byte[] buffer = new byte[1000000];
int length;
while ((length = is.read(buffer)) > 0)
{
os.write(buffer, 0, length);
}
}
finally
{
if(htmlsources[i].getName().contains("images"))
{
count += 1;
progressBar.setValue((count / MaxCount) * 100);
//progressBar.repaint();
//progressBar.revalidate();
progressBar.update(progressBar.getGraphics());
}
is.close();
os.close();
}
}
}
}
}
ArrayList<String > DBValues = new ArrayList<String>(); //read all csv files values
File f1 = new File(csvfolderpath);
for(int i = 0; i < f1.listFiles().length; i++)
{
if(f1.listFiles()[i].isFile())
{
FileReader fl = new FileReader(f1.listFiles()[i]);
BufferedReader bfr = new BufferedReader(fl);
for(int j = 0; j < 2; j++)
{
if(j == 1)
{
DBValues.add(bfr.readLine());
count += 1;
progressBar.setValue((count / MaxCount) * 100);
//progressBar.repaint();
//progressBar.revalidate();
progressBar.update(progressBar.getGraphics());
}
else
{
bfr.readLine();
}
}
bfr.close();
}
}
/*for(int x = 0; x < DBValues.size(); x++)
{
//System.out.println(DBValues.get(x));
}*/
//removing csv in local computer
File f2 = new File(csvfolderpath);
File[] removelist = f2.listFiles();
for(int x = 0; x < removelist.length; x++)
{
if(removelist[x].isFile())
{
removelist[x].delete();
count += 1;
progressBar.setValue((count / MaxCount) * 100);
// progressBar.repaint();
//progressBar.revalidate();
progressBar.update(progressBar.getGraphics());
}
}
//removing csv in device
File f3 = new File(removepath + "\\csv");
if(f3.isDirectory())
{
removelist = f3.listFiles();
for(int y = 0; y < removelist.length; y++)
{
try
{
if(removelist[y].isFile())
{
//System.out.println(removelist[y].getName());
removelist[y].delete();
count += 1;
progressBar.setValue((count / MaxCount) * 100);
//progressBar.repaint();
// progressBar.revalidate();
progressBar.update(progressBar.getGraphics());
}
}
catch(Exception e)
{
System.out.println(e);
}
}
}
//removing html and images in device
File f4 = new File(removepath + "\\html");
if(f4.isDirectory())
{
removelist = f4.listFiles();
for(int z = 0; z < removelist.length; z++)
{
try
{
if(removelist[z].isFile())
{
removelist[z].delete();
count += 1;
progressBar.setValue((count / MaxCount) * 100);
// progressBar.repaint();
// progressBar.revalidate();
progressBar.update(progressBar.getGraphics());
}
else if(removelist[z].isDirectory())
{
if(removelist[z].getName().contains("images"))
{
File[] subfolder = removelist[z].listFiles();
for (int idx = 0; idx < subfolder.length; idx++)
{
if(subfolder[idx].isFile())
{
subfolder[idx].delete();
count += 1;
progressBar.setValue((count / MaxCount) * 100);
// progressBar.repaint();
// progressBar.revalidate();
progressBar.update(progressBar.getGraphics());
}
}
}
}
}
catch(Exception e)
{
System.out.println(e);
}
}
}
/* JProgressBar progressBar = new JProgressBar();
progressBar.setValue(25);
progressBar.setStringPainted(true);*/
Border border = BorderFactory.createTitledBorder("Reading...");
progressBar.setBorder(border);
gui.add(progressBar, BorderLayout.SOUTH);
gui.repaint();
gui.revalidate();
// System.out.println(count);
}
private static int countprocess(File[] csv, File[] html_image)
{
int x = 0;
int y = 0;
int z = 0;
for(int i = 0; i < csv.length; i++)
{
if(csv[i].isFile())
{
x += 1;
}
} //get total count of csv files throught loop
for(int i = 0; i < html_image.length; i++)
{
if(html_image[i].isFile())
{
y += 1;
}
else if(html_image[i].isDirectory())
{
if(html_image[i].getName().contains("images"))
{
File[] flist = html_image[i].listFiles();
for(int j = 0; j < flist.length; j++)
{
z += 1;
}
}
} //get total count of html and images files throught loop
}
return ((4*x) + (2*y) + (2*z));
}
so i tried to refresh my progress bar value by setting it's value like this
progressBar.setValue((count / MaxCount) * 100);
but somehow i can't make it to work, my progress bar does not showing it's progress like 1% 2% 3%.. 10% and so on.. instead it's only show 100% when it's process completed.. what i miss here?
note : i also have tried to set my progress bar value this way progressBar.setValue(count); still no luck.
Reviewing your whole code will take a while. But, inside your btnTransfer.addActionListener's actionPerformed function you are trying to copy stream which might take a while. Any kind of event listener is performed in event dispatch thread. Please refer to this answer for more details.
Now as a quick solution:
put your copyFileUsingStream(csvarr, htmlarr, txt.getText(), removepath); function inside a in-line thread:
new Thread()
{
public void run()
{
copyFileUsingStream(csvarr, htmlarr, txt.getText(), removepath);
}
}.start();
}
Put your progress update of JProgressBar inside SwingUtilities.invokeLater and make the (count/MaxCount) computation by casting one of them to double, as follows:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
count += 1;
progressBar.setValue((int) (((double)count/ MaxCount) * 100));
}
});
as count is local to the copyFileUsingStream function, please try to declare it in your class context to access and change.
But SwingWorker is preferable for this kind of task.
Tutorial Resources:
Worker Threads and SwingWorker
How to use progress bars with swing worker
ProgressBar Demo with SwingWorker
You are setting a value in your progress bar to a percent complete. But the max value of your progress bar is actually the total number of items.
Instead, you need to just set your progressbar value to your current count and get rid of the calculation for the %.
Something like:
progressBar.setValue(count );
Also you should be doing your long running task in a SwingWorker thread so that you don't have to force repainting of the GUI.
i am working to generate thumbnail images from a video. I am able to do it but i need only one thumbnail image from a video , but what i get is more than one images at different times of the video. I have used the following code to generate the thumbnails . Please suggest me what should i modify in the following code to get only one thumbnail from the middle portion of the video . The code i used is as follows ( I have used Xuggler ):
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import com.xuggle.mediatool.IMediaReader;
import com.xuggle.mediatool.MediaListenerAdapter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.mediatool.event.IVideoPictureEvent;
import com.xuggle.xuggler.Global;
public class Main {
public static final double SECONDS_BETWEEN_FRAMES = 10;
private static final String inputFilename = "D:\\k\\Knock On Wood Lesson.flv";
private static final String outputFilePrefix = "D:\\pix\\";
// The video stream index, used to ensure we display frames from one and
// only one video stream from the media container.
private static int mVideoStreamIndex = -1;
// Time of last frame write
private static long mLastPtsWrite = Global.NO_PTS;
public static final long MICRO_SECONDS_BETWEEN_FRAMES =
(long) (Global.DEFAULT_PTS_PER_SECOND * SECONDS_BETWEEN_FRAMES);
public static void main(String[] args) {
IMediaReader mediaReader = ToolFactory.makeReader(inputFilename);
// stipulate that we want BufferedImages created in BGR 24bit color space
mediaReader.setBufferedImageTypeToGenerate(BufferedImage.TYPE_3BYTE_BGR);
mediaReader.addListener(new ImageSnapListener());
// read out the contents of the media file and
// dispatch events to the attached listener
while (mediaReader.readPacket() == null);
}
private static class ImageSnapListener extends MediaListenerAdapter {
public void onVideoPicture(IVideoPictureEvent event) {
if (event.getStreamIndex() != mVideoStreamIndex) {
// if the selected video stream id is not yet set, go ahead an
// select this lucky video stream
if (mVideoStreamIndex == -1) {
mVideoStreamIndex = event.getStreamIndex();
} // no need to show frames from this video stream
else {
return;
}
}
// if uninitialized, back date mLastPtsWrite to get the very first frame
if (mLastPtsWrite == Global.NO_PTS) {
mLastPtsWrite = event.getTimeStamp() - MICRO_SECONDS_BETWEEN_FRAMES;
}
// if it's time to write the next frame
if (event.getTimeStamp() - mLastPtsWrite
>= MICRO_SECONDS_BETWEEN_FRAMES) {
String outputFilename = dumpImageToFile(event.getImage());
// indicate file written
double seconds = ((double) event.getTimeStamp())
/ Global.DEFAULT_PTS_PER_SECOND;
System.out.printf("at elapsed time of %6.3f seconds wrote: %s\n",
seconds, outputFilename);
// update last write time
mLastPtsWrite += MICRO_SECONDS_BETWEEN_FRAMES;
}
}
private String dumpImageToFile(BufferedImage image) {
try {
String outputFilename = outputFilePrefix
+ System.currentTimeMillis() + ".png";
ImageIO.write(image, "png", new File(outputFilename));
return outputFilename;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
}
This is how you can.
public class ThumbsGenerator {
private static void processFrame(IVideoPicture picture, BufferedImage image) {
try {
File file=new File("C:\\snapshot\thimbnailpic.png");//name of pic
ImageIO.write(image, "png", file);
} catch (Exception e) {
e.printStackTrace();
}
}
#SuppressWarnings("deprecation")
public static void main(String[] args) throws NumberFormatException,IOException {
String filename = "your_video.mp4";
if (!IVideoResampler.isSupported(IVideoResampler.Feature.FEATURE_COLORSPACECONVERSION))
throw new RuntimeException("you must install the GPL version of Xuggler (with IVideoResampler support) for this demo to work");
IContainer container = IContainer.make();
if (container.open(filename, IContainer.Type.READ, null) < 0)
throw new IllegalArgumentException("could not open file: "
+ filename);
String seconds=container.getDuration()/(1000000*2)+""; // time of thumbnail
int numStreams = container.getNumStreams();
// and iterate through the streams to find the first video stream
int videoStreamId = -1;
IStreamCoder videoCoder = null;
for (int i = 0; i < numStreams; i++) {
// find the stream object
IStream stream = container.getStream(i);
// get the pre-configured decoder that can decode this stream;
IStreamCoder coder = stream.getStreamCoder();
if (coder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO) {
videoStreamId = i;
videoCoder = coder;
break;
}
}
if (videoStreamId == -1)
throw new RuntimeException(
"could not find video stream in container: " + filename);
if (videoCoder.open() < 0)
throw new RuntimeException(
"could not open video decoder for container: " + filename);
IVideoResampler resampler = null;
if (videoCoder.getPixelType() != IPixelFormat.Type.BGR24) {
resampler = IVideoResampler.make(videoCoder.getWidth(), videoCoder
.getHeight(), IPixelFormat.Type.BGR24, videoCoder
.getWidth(), videoCoder.getHeight(), videoCoder
.getPixelType());
if (resampler == null)
throw new RuntimeException(
"could not create color space resampler for: "
+ filename);
}
IPacket packet = IPacket.make();
IRational timeBase = container.getStream(videoStreamId).getTimeBase();
System.out.println("Timebase " + timeBase.toString());
long timeStampOffset = (timeBase.getDenominator() / timeBase.getNumerator())
* Integer.parseInt(seconds);
System.out.println("TimeStampOffset " + timeStampOffset);
long target = container.getStartTime() + timeStampOffset;
container.seekKeyFrame(videoStreamId, target, 0);
boolean isFinished = false;
while(container.readNextPacket(packet) >= 0 && !isFinished ) {
if (packet.getStreamIndex() == videoStreamId) {
IVideoPicture picture = IVideoPicture.make(videoCoder
.getPixelType(), videoCoder.getWidth(), videoCoder
.getHeight());
int offset = 0;
while (offset < packet.getSize()) {
int bytesDecoded = videoCoder.decodeVideo(picture, packet,
offset);
if (bytesDecoded < 0) {
System.err.println("WARNING!!! got no data decoding " +
"video in one packet");
}
offset += bytesDecoded;
picture from
if (picture.isComplete()) {
IVideoPicture newPic = picture;
if (resampler != null) {
newPic = IVideoPicture.make(resampler
.getOutputPixelFormat(), picture.getWidth(),
picture.getHeight());
if (resampler.resample(newPic, picture) < 0)
throw new RuntimeException(
"could not resample video from: "
+ filename);
}
if (newPic.getPixelType() != IPixelFormat.Type.BGR24)
throw new RuntimeException(
"could not decode video as BGR 24 bit data in: "
+ filename);
BufferedImage javaImage = Utils.videoPictureToImage(newPic);
processFrame(newPic, javaImage);
isFinished = true;
}
}
}
}
if (videoCoder != null) {
videoCoder.close();
videoCoder = null;
}
if (container != null) {
container.close();
container = null;
}
} }
I know this is an old question but I found the same piece of tutorial code while playing with Xuggler today. The reason you are getting multiple thumbnails is due to the following line:
public static final double SECONDS_BETWEEN_FRAMES = 10;
This variable specifies the number of seconds between calls to dumpImageToFile. So a frame thumbnail will be written at 0.00 seconds, at 10.00 seconds, at 20.00 seconds, and so on:
if (event.getTimeStamp() - mLastPtsWrite >= MICRO_SECONDS_BETWEEN_FRAMES)
To get a frame thumbnail from the middle of the video you can calculate the duration of the video using more Xuggler capability which I found in a tutorial at JavaCodeGeeks. Then change your code in the ImageSnapListener to only write a single frame once the IVideoPictureEvent event timestamp exceeds the calculated mid point.
I hope that helps anyone who stumbles across this question.
The following link shows the list of directories where the thumbnails are stored in the respective phones:
http://wiki.forum.nokia.com/index.php/Thumbnail_path_for_3rd_edition_devices
but the phones given on the link are limited. Does it mean that, for other phones (such as N86, Expressmusic etc), I do not have access to thumbnails? I tried using all the directory structures given on the link, but none are working for the mentioned phones. Does anybody know anything about it?
I don't know if it is what you need, but you can fetch embedded thumbnail out of full-size JPEG file. In my j2me application I show the phone gallery this way.
private final static int STOP_AT_BYTE = 8192; //how far to search for thumbnail
private final static int THUMB_MAX_SIZE = 16284;
private Image getThumbnailFromStream(InputStream str, long fileSize)
{
byte[] tempByteArray = new byte[THUMB_MAX_SIZE]; // how big can a thumb get.
byte[] bytefileReader = {0}; // lazy byte reader
byte firstByte,secondByte = 0;
int currentIndex = 0;
int currByte = 0;
try {
str.read(bytefileReader);
firstByte = bytefileReader[0];
str.read(bytefileReader);
secondByte = bytefileReader[0];
currByte += 2;
if ((firstByte & 0xFF) == 0xFF && (secondByte & 0xFF) == 0xD8) { //if this is JPEG
byte rByte = 0;
do {
while (rByte != -1 && currByte < fileSize) {
str.read(bytefileReader);
rByte = bytefileReader[0];
currByte++;
}
str.read(bytefileReader);
rByte = bytefileReader[0];
currByte++;
if (currByte > STOP_AT_BYTE) {
return null;
}
} while ((rByte & 0xFF) != 0xD8 && currByte < fileSize); // thumb starts
if (currByte >= fileSize) {
return null;
}
tempByteArray[currentIndex++] = -1;
tempByteArray[currentIndex++] = rByte;
rByte = 0;
do {
while (rByte != -1){
str.read(bytefileReader);
rByte = bytefileReader[0];
tempByteArray[currentIndex++] = rByte;
}
str.read(bytefileReader);
rByte = bytefileReader[0];
tempByteArray[currentIndex++] = rByte;
} while ((rByte & 0xFF) != 0xD9); // thumb ends
tempByteArray[currentIndex++] = -1;
Image image = Image.createImage(tempByteArray, 0, currentIndex-1);
tempByteArray = null;
return image;
}
} catch (Throwable e) {
//error
}
return null;
}