I would like to scan for SHOUTcast meta data myself. I realise there cool classes such as IcyStreamMeta etc but I would like to know why I cannot see the data myself.
I am using this URL (have tried others too):
http://www.shoutcastunlimited.com:8512/
My understanding is that I should see meta data within the audio stream data - especially when a radio station changes the current tune.
What I've tried to do is output sequences of printable ASCII character to see if I can see keywords such as "StreamTitle" but all I can see if anything is "LAME".
My code below is less than ideal but is there are reason why I am not seeing "StreamTitle" or other meaningful words?
public void retreiveMetadata()
{
try
{
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url( mStreamUrl ).build();
Response response = client.newCall( request ).execute();
mStream = response.body().byteStream();
// This returns 200 as expected
ContextActivity.LogDebugf( "ICY RESPONSE: %d\n", response.code() );
if( abBuffer == null )
abBuffer = new byte[ nBufferSize ];
for( ;; )
{
// Read data INTO the buffer
int nRead = mStream.read( abBuffer, 0, nBufferSize );
//ContextActivity.LogDebugf( "ICY Data Read: %d\n", nRead );
int nPrintableStart = -1;
int nPrintableCount = 0;
for( int i = 0; i < nRead; i ++ )
{
// Look for printable chars only
if( ( abBuffer[ i ] >= ' ' ) && ( abBuffer[ i ] < '~' ) )
{
if( nPrintableStart < 0 )
{
nPrintableStart = i;
nPrintableCount = 0;
}
nPrintableCount ++;
}
else
{
// End of printable range
if( nPrintableCount >= 11 )
{
String sMeta = new String( abBuffer, nPrintableStart, nPrintableCount, "UTF-8" );
ContextActivity.LogDebugf( "ICY[%s]\n", sMeta );
}
nPrintableStart = -1;
nPrintableCount = 0;
}
}
}
}
catch( Exception e )
{
ContextActivity.LogDebugf( "ICY Exception[%s]\n", e.toString() );
}
}
Here are some "LAME" examples:
02-24 16:58:01.570: I/System.out(26965): ICY[LAME3.98.4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU|]
02-24 16:58:01.580: I/System.out(26965): ICY[LAME3.98.4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU|\]
02-24 16:58:01.590: I/System.out(26965): ICY[LAME3.98.4IUb\]
I don't see any place where you are actually requesting metadata. If you don't request it, the server won't send it. Add this header to your request:
Icy-MetaData: 1
In the response, you'll get a header back (assuming the server supports metadata) that says:
Icy-MetaInt: 8192
Whatever that number is (8192 in this case, which is a typical figure) is the number of bytes in between each metadata block.
The first byte in the metadata block indicates the size of the metadata block. If it's 0x00, then there is no metadata and it's back to audio data for the size of the interval. If it says 0x02 or some other non-zero value, multiply that by 16, and that's the number of bytes (NUL [0x00] padded) of text metadata, in a key="value" sort of format. StreamTitle is the only one that's all that meaningful these days. Some streams have been known to include other data, often for internal tracking.
Related
The point is really this - my Android TLS/TCP socket disconnects sometimes mid-data stream. Usually because of timeouts, etc. I'm trying to determine how much data was actually sent successfully BEFORE the socket cut off so I know where to pick up.
How do I do this?
My thought is to pull the output stream from the socket and write a little bit at a time. If we get a disconnect then I can pick up where we left off and continue sending the next chunk.
I chose NOT to use a BufferedOutputStream because after the disconnect I wouldn't be sure how much was sent and how much wasn't. I'd love to be mistaken.
This is my best attempt:
/**
* Static class that holds the currently writable data
*/
final private class WritingByteArray {
/**
* Easy C-tor
*/
public WritingByteArray( final byte[] bytes ) {
this.bytes = bytes;
this.offset = 0;
}
/**
* Convenience
*/
public int length() {
return( bytes.length );
}
/**
* Convenience
*/
public int remaining( int max ) {
return( Math.min( max, length()-offset ) );
}
/**
* Consume bytes. Really just increment the offset with a safety catch
*/
private void consume( int size ) {
offset = Math.min( bytes.length, offset+size );
}
/**
* Remaining without a max
*/
public int remaining() {
return( length()-offset );
}
/**
* Are we empty?
*/
public boolean empty() {
return( offset>=length() );
}
public byte[] bytes; //!< The byte array we're working with
public int offset; //!< Our reading offset
}
/**
* Our write data function new
*/
private void writeDataNew() {
final int WRITE_BUFFER_SIZE = ( 8*1024 ); //!< 8kb of write buffer size
try {
OutputStream outputStream = sslSocket.getOutputStream();
// Do we have data we are currently writing
if( currentByteArray==null ) {
Log.d(
TAG,
"We don't have anything in the current byte array. Pulling from the output buffer."
);
// Pull from the outputBuffer
synchronized( outputBuffer ) {
final int size = outputBuffer.size();
Log.d( TAG, "OutputBuffer has "+size );
// This was one done outside of synchronization... that's bad!
if( size==0 ) {
Log.d( TAG, "Our output buffer is actually empty." );
return;
}
// Copy the bytes to our private byte array
currentByteArray = new WritingByteArray( outputBuffer.toByteArray() );
// Get the data
// outputStream.write( outputBuffer.toByteArray() );
// Reset the buffer
outputBuffer.reset();
} // Free the mutex so bytearray can be written again
}
// Now write the data
final int size = currentByteArray.remaining( WRITE_BUFFER_SIZE );
// Do we have bytes to send?
if( size>0 ) {
// Alert how much we'll write
Log.d( TAG, "We have "+size+" data to write" );
// Write our deduced size
outputStream.write( currentByteArray.bytes, currentByteArray.offset, size );
// Flush the output so we can block???
// DOES THIS BLOCK?
// If this blocks maybe this will work...
outputStream.flush();
// Just a log tag
Log.d( TAG, "Data wrote "+size+" bytes of data." );
// Now consume
currentByteArray.consume( size );
// Do we have any left?
if( currentByteArray.empty() ) {
Log.d( TAG, "Clearing currentByteArray" );
currentByteArray = null; // Clear it so we know
}
}
else {
Log.d( TAG, "No more data left " );
// Just in case
currentByteArray = null;
}
// Call again
writeDataNew();
}
catch( final Exception e ) {
e.printStackTrace();
reportError( e.getLocalizedMessage() );
// Here we can reconnect and send more data if we failed out
}
}
Information about how much data has been received by the peer is essentially unrecoverable (when discussion is limited to the TCP protocol itself). IOW: You can't do what you're trying to do just by modifying the client.
Yes, TCP does include bidirectional ACKs that include information on how much data has been successfully transferred, but that information is not directly available to the application layer.
Even if you could access the TCP ACK information, there's no way to know that you've received every ACK the peer sent, at the moment a connection fails. (See: Two General's Problem.)
Note that even if you don't use a BufferedOutputStream, buffering still occurs in the TLS layer, and at multiple network layers, including within the kernel.
Bottom line: It is impossible to know the peer's "receive" state with 100% certainty, unless this information is explicitly transmitted, or both peers have some way of agreeing on a common "ground" state that exists at the moment a connection begins.
Most practical systems will have the client query for the peer's state at the beginning of a conversation (HTTP HEAD, as an example), or they'll keep track of explicit acknowledgements sent by the peer. Note that even an orderly connection close can serve as an explicit acknowledgement.
I am encoding raw data on Android using ffmpeg libraries. The native code reads the audio data from an external device and encodes it into AAC format in an mp4 container. I am finding that the audio data is successfully encoded (I can play it with Groove Music, my default Windows audio player). But the metadata, as reported by ffprobe, has an incorrect duration of 0.05 secs - it's actually several seconds long. Also the bitrate is reported wrongly as around 65kbps even though I specified 192kbps.
I've tried recordings of various durations but the result is always similar - the (very small) duration and bitrate. I've tried various other audio players such as Quicktime but they play only the first 0.05 secs or so of the audio.
I've removed error-checking from the following. The actual code checks every call and no problems are reported.
Initialisation:
void AudioWriter::initialise( const char *filePath )
{
AVCodecID avCodecID = AVCodecID::AV_CODEC_ID_AAC;
int bitRate = 192000;
char *containerFormat = "mp4";
int sampleRate = 48000;
int nChannels = 2;
mAvCodec = avcodec_find_encoder(avCodecID);
mAvCodecContext = avcodec_alloc_context3(mAvCodec);
mAvCodecContext->codec_id = avCodecID;
mAvCodecContext->codec_type = AVMEDIA_TYPE_AUDIO;
mAvCodecContext->sample_fmt = AV_SAMPLE_FMT_FLTP;
mAvCodecContext->bit_rate = bitRate;
mAvCodecContext->sample_rate = sampleRate;
mAvCodecContext->channels = nChannels;
mAvCodecContext->channel_layout = AV_CH_LAYOUT_STEREO;
avcodec_open2( mAvCodecContext, mAvCodec, nullptr );
mAvFormatContext = avformat_alloc_context();
avformat_alloc_output_context2(&mAvFormatContext, nullptr, containerFormat, nullptr);
mAvFormatContext->audio_codec = mAvCodec;
mAvFormatContext->audio_codec_id = avCodecID;
mAvOutputStream = avformat_new_stream(mAvFormatContext, mAvCodec);
avcodec_parameters_from_context(mAvOutputStream->codecpar, mAvCodecContext);
if (!(mAvFormatContext->oformat->flags & AVFMT_NOFILE))
{
avio_open(&mAvFormatContext->pb, filePath, AVIO_FLAG_WRITE);
}
if ( mAvFormatContext->oformat->flags & AVFMT_GLOBALHEADER )
{
mAvCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
avformat_write_header(mAvFormatContext, NULL);
mAvAudioFrame = av_frame_alloc();
mAvAudioFrame->nb_samples = mAvCodecContext->frame_size;
mAvAudioFrame->format = mAvCodecContext->sample_fmt;
mAvAudioFrame->channel_layout = mAvCodecContext->channel_layout;
av_samples_get_buffer_size(NULL, mAvCodecContext->channels, mAvCodecContext->frame_size,
mAvCodecContext->sample_fmt, 0);
av_frame_get_buffer(mAvAudioFrame, 0);
av_frame_make_writable(mAvAudioFrame);
mAvPacket = av_packet_alloc();
}
Encoding:
// SoundRecording is a custom class with the raw samples to be encoded
bool AudioWriter::encodeToContainer( SoundRecording *soundRecording )
{
int ret;
int frameCount = mAvCodecContext->frame_size;
int nChannels = mAvCodecContext->channels;
float *buf = new float[frameCount*nChannels];
while ( soundRecording->hasReadableData() )
{
//Populate the frame
int samplesRead = soundRecording->read( buf, frameCount*nChannels );
// Planar data
int nFrames = samplesRead/nChannels;
for ( int i = 0; i < nFrames; ++i )
{
for (int c = 0; c < nChannels; ++c )
{
samples[c][i] = buf[nChannels*i +c];
}
}
// Fill a gap at the end with silence
if ( samplesRead < frameCount*nChannels )
{
for ( int i = samplesRead; i < frameCount*nChannels; ++i )
{
for (int c = 0; c < nChannels; ++c )
{
samples[c][i] = 0.0;
}
}
}
encodeFrame( mAvAudioFrame ) )
}
finish();
}
bool AudioWriter::encodeFrame( AVFrame *frame )
{
//send the frame for encoding
int ret;
if ( frame != nullptr )
{
frame->pts = mAudFrameCounter++;
}
avcodec_send_frame(mAvCodecContext, frame );
while (ret >= 0)
{
ret = avcodec_receive_packet(mAvCodecContext, mAvPacket);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF )
{
break;
}
else
if (ret < 0) {
return false;
}
av_packet_rescale_ts(mAvPacket, mAvCodecContext->time_base, mAvOutputStream->time_base);
mAvPacket->stream_index = mAvOutputStream->index;
av_interleaved_write_frame(mAvFormatContext, mAvPacket);
av_packet_unref(mAvPacket);
}
return true;
}
void AudioWriter::finish()
{
// Flush by sending a null frame
encodeFrame( nullptr );
av_write_trailer(mAvFormatContext);
}
Since the resultant file contains the recorded music, the code to manipulate the audio data seems to be correct (unless I am overwriting other memory somehow).
The inaccurate duration and bitrate suggest that information concerning time is not being properly managed. I set the pts of the frames using a simple increasing integer. I'm unclear what the code that sets the timestamp and stream index achieves - and whether it's even necessary: I copied it from supposedly working code but I've seen other code without it.
Can anyone see what I'm doing wrong?
The timestamp need to be correct. Set the time_base to 1/sample_rate and increment the timestamp by 1024 each frame. Note: 1024 is aac specific. If you change codecs, you need to change the frame size.
I am making an android app + arduino that will receive ir code from arduino and sending the results.value (ir decode) to android through bluetooth. on the android side I have receive the code as a String that results into (for example) 92c0 then made a test button that will send it back to arduino and trigger it to send ir code to a device by irsend.sendNEC(0x92c0, 32) problem is when receiving the codes back from the android app is I have to receive it by char data, how do I use data which is a char and use it as a substitute for 0x92c0 in irsend.sendN My sketch down below:
#include <IRremote.h>
#include <SoftwareSerial.h>
SoftwareSerial bluetoothPort(4,5);
const int RECV_PIN = 12;
char data = "0";
const int SEND_PIN = 13;
IRsend irsend;
IRrecv irrecv(RECV_PIN);
decode_results results;
int BTval;
int IRval;
void setup()
{
bluetoothPort.begin(9600);
Serial.begin(9600);
irrecv.enableIRIn();
}
void loop()
{
if(bluetoothPort.available() > 0)
{
data = bluetoothPort.read();
Serial.print(data);
irsend.sendNEC(operator[data],32);
irrecv.resume();
}
if(irrecv.decode(&results))
{
Serial.println(results.value);
int set = results.value;
bluetoothPort.println(results.value, HEX);
irrecv.resume();
}
}
The problem here is that you CAN'T transfer 0x92c0 as a char (or byte). Just because it is not a byte, but two.
Now, I'm not much into android, so I need to see the android code to make a real solution You can handle this in three ways:
Binary transfer the data (2 bytes)
Transfer the data as a string (5 bytes)
Index the possible replies in an array and transfer the index (only applicable for a small quantity of codes).
I'm showing you the last 2, because the 1st is the most effective but I don't think you have the proper knowledge to send and receive data in binary format from android (and surely I don't have it).
So, if the data is transferred in string format (the same way you are uploading it) you will receive more bytes:
1 to 4 hexadecimal digits
1 carriage return
The code just stores the bytes you receive in a variable and then sends it when you receive a CR or LF:
// Outside the loop function
uint16_t receivedData;
// Inside the loop function
if(bluetoothPort.available() > 0)
{
data = bluetoothPort.read();
if ((data >= '0') && (data <= '9'))
{ // If it is a digit between 0 and 9 (in ascii)
receivedData = (receivedData << 4) | (data - '0');
}
else if ((data >= 'A') && (data <= 'F'))
{ // If it is a digit between A and F (in ascii)
receivedData = (receivedData << 4) | (data - 'A' + 10);
}
else if ((data >= 'a') && (data <= 'f'))
{ // Lowercase case
receivedData = (receivedData << 4) | (data - 'a' + 10);
}
else if (((data == '\r') || (data == '\n')) && (receivedData > 0))
{ // I tend to consider both CR and LF, because windows always screws this
Serial.print(receivedData, HEX);
irsend.sendNEC(receivedData,32); // Not sure about the 32 here...
irrecv.resume();
receivedData = 0;
}
else
receivedData = 0; // Something went wrong, just reset the variable
}
If you just have to send a few codes, you can store them and then transfer only the proper index. For instance:
// Outside the loop function
uint16_t possibleCodes[] = { 0x92c0, 0x8238, 0x5555 };
// Inside the loop function
if(bluetoothPort.available() > 0)
{
data = bluetoothPort.read();
// If you are using string transmission, use the following
// line to get the correct value
// data = bluetoothPort.read() - '0';
if (data < sizeof(possibleCodes) / sizeof(possibleCodes[0]))
{
Serial.print(possibleCodes[data], HEX);
irsend.sendNEC(possibleCodes[data],32); // Not sure about the 32 here...
irrecv.resume();
}
}
For instance in this case to send 0x92c0 you will have to send from android the value 0.
I'm currently working on communication between devices using HID over a usb cable. I am sending a string that is UTF-8 encoded from an Android device, and would like to receive and read it on my Arduino Leonardo.
My problem is that I am unable to get the received message into any other type. I need to do a human readable string comparison as I'm sending a variety of commands to the Arduino. The IDE either has a type mismatch problem regardless of how I try to convert the received message. I've tried many different things but I will post one as an example. I'm sure there is something I missing that's keeping me from getting this!
int n;
n = RawHID.recv(buffer, 0); // 0 timeout = do not wait
if (msUntilNextSend > 2000) {
msUntilNextSend = msUntilNextSend - 2000;
// String mystr = "";
// byte charbuff[10];
//
// for (int i = 0; i < 64; i++)
// {
// mystr.concat((char) buffer[i]);
// }
//
// mystr.toCharArray(charbuff, 10);
char readin[64] = { ' ' };
readin = (char *)buffer;
String myString = String((char *)buffer);
if (strcmp(readin, "test") == 0)
{
String resp = "response";
resp.getBytes(buffer, 64);
n = RawHID.send(buffer, 100);
}
I've included some comments with bits of a different approach but as I mentioned, I have been unsuccessful in my attempts. Any insight is appreciated!
This is with reference to sipdroid data encrypt failed
I tried using XOR operation instead of reverse byte code for send packets and receive packets in SipdroidSocket.class.
I experienced same issue(too much noise)
Please guide me in encrypting and decrypting packets in SipdroidSocket.class
Sorry for late reply.I am posting the snippets of the code I tried. Please refer the original RtpSocket.java and SipdroidSocket.java classes for complete view. I am just putting the snippets here.
In RtpSocket.java , I took a static value and collected the packet's header length. Then used this header length in SipdroidSocket.java so as to remove the header part prior tweaking with the payload:
In SipdroidSocket.java, following editing were done in Send and Receive functions:
public void receive(DatagramPacket pack) throws IOException {
if (loaded) {
impl.receive(pack);
byte[] b = pack.getData(); // fetch data from receiver
int len = RtpSocket.header;
pack.setData(do_something(b, len)); // do the XORing to retrieve
// original data
} else {
super.receive(pack);
byte[] b = pack.getData();
int len = RtpSocket.header;
pack.setData(do_something(b, len));
}
}
public void send(DatagramPacket pack) throws IOException {
byte[] b = pack.getData(); // fetch original data
int len = RtpSocket.header;
pack.setData(do_something(b, len)); // replace with tweaked data
if (loaded)
impl.send(pack);
else
super.send(pack);
}
private byte[] do_something(byte[] b, int len) {
// TODO Auto-generated method stub
int new_buff_len = b.length - len;
byte[] new_buff = new byte[new_buff_len];
int i = 0;
for (i = len; i < b.length; i++) // separating header values
{
new_buff[i] = (byte) (b[i] ^ 0x43); // XORing original packet
// payload before sending and
// after receiving to get
// original data on both sides
}
return new_buff;
}
Kindly , try it and suggest me please.
Finally it worked ! Had to meddle with the other parts of the code . XOR operation now works fine and have attained the objective.