i want to store a H264 video that is recorded from a rtp (live)stream to a file.
i've tried this with a simple java program, but vlc player cannot open the file.
here is my code:
try
{
socket = new DatagramSocket(port);
fos = new FileOutputStream(filePath + outputFileName);
do
{
DatagramPacket in = new DatagramPacket(inData, inData.length);
socket.receive(in);
byte[] bytes = in.getData();
if (curPos < buffer.length)
{
for (int i = 0; i < bytes.length; i++)
{
buffer[curPos] = bytes[i];
curPos++;
if (curPos >= buffer.length)
{
receivePackets = false;
break;
}
}
}
else
{
receivePackets = false;
}
Thread.sleep(SOCKET_TIMEOUT);
}
while (receivePackets);
fos.write(buffer, 0, buffer.length);
if (fos != null)
{
fos.close();
}
if (socket != null)
{
socket.close();
}
}
catch (IOException e)
{
e.printStackTrace();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
the file will be created, but it's not possible to open it.
are there any header informations i've to cut of or add to the byte array to get a correct video file?
UPDATE:
ok, the problem seems to be the nal header information of the h264 rtp packet, because i can open a recorded h263 video with a standard rtp header (but the quality of the video is really bad).
Rtp has headers in their packets that aren't part of the h264 stream, as well as has mechanics for splitting one nal unit (essentially a single h264 "packet") over multiple packets or merging multiple nal units into one packet, both of which add metadata that can't be a part of the final product.
Additionally, the RTP stream may not contain some of the metadata necessary to play the video, especially if its negotiated over rtsp. If that's the case you'll have to pull some NAL units out of the sprop-parameter-sets in the SDP.
You can find the documentation on how all of that works here: https://www.rfc-editor.org/rfc/rfc6184. It's really not as hard as it sounds.
When h264 is written to a file, each nal unit must be preceded by three 0 bytes and then a 1 byte. You can find more details on that here: https://www.itu.int/rec/T-REC-H.264-201906-I/en
VLC has to be talked into playing raw h264 files. You can google that and find easy steps to do so, its not related to the code so I won't copy them here.
If you want the file as a mp4, your best using a library to do this. If you want to do it all yourself, heaven help you, but you can find the spec here: https://web.archive.org/web/20180921052457/http://l.web.umkc.edu:80/lizhu/teaching/2016sp.video-communication/ref/mp4.pdf. The https://github.com/sannies/mp4parser library is java and may do that but, has some issues. Ffmpeg is a wonderful program to do this, and I've had luck running it as a subprocess to Java and feeding its standard in and reading from its standard out.
As a side note, ffprobe is a great program, running it on a broken video file can provide some insight on what's wrong with it.
You have to unpack the video data from RTP packets
Related
I have to write an app that displays videos and if someone plus in usb stick to the device, video is copied from it and played on the device.
Pretty simple. I got it all working with USBBroadcastReceiver to copy te files, it all works as intended.
It's also set as home app if that's important.
Only problem I have is that target devices have no battery, by that I mean, they have to be constantly plugged in (LCD Strips). Not much of a problem until power outage or somenthing like that.
Video I'm using (playing with VideoView) gets corrupted and can't be played anymore so someone would have to do a roud with usb stick and plug it into however many devices they would have.
I've observed the same thing while playing it with just an gallery app.
I've thought I've had a quick fix by simply while copyig the file, I'd also make an backup one and then If the video is corrupted, just copy again from backup and problem solved.
Not so easy after all, backup get's corrupted too. I think this might have to do with this file being cached or something?
It's my first android app and basically first java app so I might not know something obvious.
Here are my copy methods, to me it looks like it shouldn't keep any data about the backup after flushing the stream, but somehow it does.
public void CopyFile() throws IOException {
File vid1=new File(VideoFilePath);
File vid2=new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/videoclip.mp4");
//File vid3=new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/videoclip_backup.mp4");
try {
System.out.println("Copying file");
copy(vid1, vid2);
//copy(vid1, vid3);
System.out.println("Finished copying file");
IsBeingCopied = false;
} catch (Exception e) {
e.printStackTrace();
System.out.println("Failed copying file");
}
}
public void copy(File src, File dst) throws IOException {
InputStream in = new FileInputStream(src);
try {
OutputStream out = new FileOutputStream(dst);
try {
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
} finally {
out.flush();
out.close();
}
} finally {
in.close();
}
}
In Main, in onCompletition listener (which is there to loop the video) I just check if file is corrupted or couldn't be read and copy from the backup one so it's not even used there before after turning the device again.
Is there any way to fix this or sudden system shutdowns will always corrupt the files used in this app?
EDIT.
I fixed it, I knew it was just some dumb mistake. All I had to do is save files of Movies directory, insted of just on Internal Storage. You live and learn.
I have re-created a MIDI file without altering any of its Meta events. I have merely added SysEx (System Exclusive) messages under each note_on and note_off event. I use this library to parse MIDI files and extract tracks in order to get the MIDI messages.
Following is some sample code that I have tried (I have the MIDI file inside the raw folder).
try {
InputStream is = getResources().openRawResource(R.raw.mysamplemidifile);
File tempFile = File.createTempFile("temp", ".mid");
tempFile.deleteOnExit();
FileOutputStream out = new FileOutputStream(tempFile);
IOUtils.copy(is, out);
MidiFile midi = new MidiFile(tempFile);
ArrayList<MidiTrack> tracks = midi.getTracks();
MidiTrack sysExTrack = tracks.get(tracks.size() - 1);
} catch (Exception e) {
e.printStackTrace();
}
Originally the total number of MIDI messages is ~2500. But the library is able to fetch only about 2300 MIDI messages.
The sysExTrack variable contains the incompletely parsed SysEx data.
I am at a loss to understand what is going wrong here. Any help with the same is appreciated.
Edit:
The MIDI file gets parsed correctly in iOS.
I have .gz and .bzip2 files and I need this to be extracted and displayed. I looked at couple of places and it mentions to use zip4j utility. Can I use this to extract and display the files? Please let me know.
I referred the post Uncompress BZIP2 archive and tried to implement as below, but I am not sure what to pass to FileOutputStream and it is giving me an error.Also,is it possible to create a file object after its uncompressed.Please assist. Thank you for all the help again.
if (getExtension(filename).equals("bz2")) {
try {
FileInputStream fin = new FileInputStream("C:\\temp\\test.bz2");
BufferedInputStream in = new BufferedInputStream(fin);
FileOutputStream out = new FileOutputStream("archive.tar");
BZip2CompressorInputStream bzIn = new BZip2CompressorInputStream(in);
int buffersize = 1024;
final byte[] buffer = new byte[buffersize];
int n = 0;
while (-1 != (n = bzIn.read(buffer))) {
out.write(buffer, 0, n);
}
out.close();
bzIn.close();
}
catch (Exception e) {
throw new Error(e.getMessage());
}
Thanks in advance.
~Akshitha
.gz file extension is an indication for using GZIP compression.
You can use GZIPInputStream for opening GZIP in java 7 SDK: http://docs.oracle.com/javase/7/docs/api/java/util/zip/GZIPInputStream.html
BZIP2 compression / decompression is not supported, afaik, by Java SDK libraries and you will have to use a 3rd party library such as Apache Commons Compress
The lib you mentioned seem (from a very brief view) to support only ZIP format, not the GZIP of BZIP2 variants - which are different.
Please note that unlike ZIP compression, GZIP compress all files into one pack - so you cannot extract a single file without decompressing the entire package (or maybe until you reached all the bytes you wanted).
I have a problem about streaming my video to server in real-time from my phone.
that is , let my phone be a IP Camera , and server can watch the live video from my phone
I have googled many many solutions,
but there is no one can solve my problem.
I use MediaRecorder to record .
it can save video file in the SD card correctly.
then , I refered this page and used some method as followings
skt = new Socket(InetAddress.getByName(hostname),port);
pfd =ParcelFileDescriptor.fromSocket(skt);
mediaRecorder.setOutputFile(pfd.getFileDescriptor());
now it seems I can send the video stream while recording
however, I wrote a receiver-side program to receive the video stream from Android ,
but it doesn't work . is there any error?
I can receive file , but I can not open the video file .
I guess the problem may caused by file format ?
there are outline of my code.
in android side
Socket skt = new Socket(hostIP,port);
ParcelFileDescriptor pfd =ParcelFileDescriptor.fromSocket(skt);
....
....
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mediaRecorder.setOutputFile(pfd.getFileDescriptor());
.....
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
.....
mediaRecorder.start();
in receiver side (my ACER notebook)
// anyway , I don't think the file extentions will do any effect
File video = new File (strDate+".3gpp");
FileOutputStream fos;
try {
fos = new FileOutputStream(video);
byte[] data = new byte[1024];
int count =-1;
while( (count = fin.read(data,0,1024) ) !=-1)
{
fos.write(data,0,count);
fos.flush();
}
fos.close();
fin.close();
I confused a long time....
thanks in advance
Poc,
The way MediaRecorder writes files is as follows:
Leave space for empty header
Write file contents while recording
When recording finishes, seek to the beginning of file
Write the header at the beginning of the file
Then (I believe) there is another seek to the end of the file where metadata is written.
Because there is no concept of "seeking" on a socket, you will have to figure out when the header comes, seek to the beginning of your file, and then write the header in the appropriate location.
The best place to start here is to use a hexeditor to determine the format of a valid 3gpp file, then analyze this hex against your receiver program's hex output. Also you will want to look into 3gpp file formats.
I'm analyzing music mp3 files. What I'm doing is extracting the audio data from the file and computing music similarity.
I've been using javazoom in order to handle mp3 files. By using audioFormat I'm extracting the raw data from the mp3 file:
byte[] audioBytes = new byte[numBytes];
in_format_init = AudioSystem.getAudioInputStream(musicFile);
AudioFormat formatInit = in_format_init.getFormat();
AudioFormat formatFinal = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
formatInit.getSampleRate(),
16,
formatInit.getChannels(),
formatInit.getChannels()*2,
formatInit.getSampleRate(),
false);
AudioInputStream streamIn = AudioSystem.getAudioInputStream(formatFinal, in_format_init);
while (((numBytesRead = streamIn.read(audioBytes)) != -1))
{...}
By doing this I store the audio data (without headers or tags) in audioBytes and then the info stored in the array is processed.
My question is: is it posible to extract the audio information from an mp3 audio file and store it as I do it in my example? I've been reading about JMF, but it's confusing for me.
Thanks.
I've just had a quick look at the JMF API so I'm not a 100% sure this will be correct or even work at all, but try something like this:
try {
File f = new File ("/path/to/my/audio.mp3");
DataSource ds = Manager.createDataSource(f.toURI().toURL());
ds.connect();
ds.start();
...
} catch (java.io.IOException e) {
...
} catch (NoDataSourceException e) {
...
}
After this try getting the controls from the DataSource: ds.getControls(), and see if any of the controls allows you to read the raw audio data.
You'll probably have to do all kinds of cleanup as well, e.g. ds.disconnect(), after you're done reading the audio.
Also, don't forget to install the JMF MP3 plugin
-- Lauri