Stream data (Artist / Title) not displaying in AVPlayerItemTrack array in xcode - ios

I have the following code which, when used on most (Shoutcast) streaming servers, returns the artist/song metadata, but on a particular server it is not working and is simply displaying the letter 't'!
As NSLog isn't currently working for me (despite all kinds of changes, I can't get it to work (Xcode 9.2)), I'm slightly in the dark.
- (void)FetchMeta
{
for (AVPlayerItemTrack *item in player.currentItem.tracks)
{
if ([item.assetTrack.mediaType isEqual:AVMediaTypeAudio])
{
NSArray *meta = [playerItem timedMetadata];
for (AVMetadataItem *metaItem in meta)
{
NSString *source = metaItem.stringValue;
if(![source isEqualToString:self.songInfo.text])
{
self.songInfo.text = [source stringByAppendingString:#" "];
//self.songInfo.text = #"Test";
[self LoadArtistImage];
}
NSLog(#"meta %#\n%#",source,metaItem.extraAttributes);
}
}
}
}
I know I'm in the right part of the code, because if I uncomment the //self.songInfo.text = #"Test"; line, I see it on the display where the song metadata should appear, but have been on this for 3 hours solid and can't get it to display the metadata and not having NSLog really isn't helping.
UPDATE : I have been able to interrogate the servers and can see the difference, but I still no not know how to tell the code to look in correct field... My understanding is that the data is being put into an array (I think) but it is taking the wrong field. Maybe it is always just taking the last field? TO be clear, I'm looking for it to take the 'StreamTitle' field.
Working server response:
Input #0, mp3, from 'http://server:port':
Metadata:
icy-notice1 : <BR>This stream requires Winamp<BR>
icy-notice2 : SHOUTcast DNAS/posix(linux x86) v2.5.1.724<BR>
icy-name : Station Name
icy-genre : 80s, Pop
icy-br : 128
icy-sr : 44100
icy-url : http://website.com
icy-pub : 1
StreamTitle : Rod Stewart - Lost In You
Non working server response:
Input #0, mp3, from 'http://server:port/':
Metadata:
icy-notice1 : <BR>This stream requires Winamp<BR>
icy-notice2 : SHOUTcast Distributed Network Audio Server/Linux v1.9.8<BR>
icy-name : Station Name
icy-genre : Various
icy-url : www.website.com
icy-pub : 1
icy-br : 128
StreamTitle : Theory of a Deadman - Rx
StreamUrl : t
To further clarify. The 'working server' output works correctly displaying the 'StreamTitle' field using the code displayed, the non working server displays the last field 'StreamUrl' which is not the desired field.
Any pointers?
Many thanks.

As #Larme says your loop will essentially set your text field to the value of each item in turn, so the value of the last item "wins".
You have to only set your text field if the current item in your loop is the stream title, something like that:
for (AVMetadataItem *metaItem in meta)
{
if([metaItem.key isEqualTo: AVMetadataIcyMetadataKeyStreamTitle])
{
self.songInfo.text = metaItem.stringValue;
}
}

Related

Trouble Comparing Bluetooth-Sent ASCII char in iOS

I have an iOS application that talks to a RedBearLab Arduino device. My code that I use to send an int via bluetooth from Arduino to iOS is as follows:
void sendMyInt(int myInt) {
char b[4];
String str;
str=String(myInt);
str.toCharArray(b,4);
for (int i; i < 3; i++) {
char toPrint = b[i];
ble_write(toPrint);
}
}
Here is my code on the receiving end:
-(void) bleDidReceiveData:(unsigned char *)data length:(int)length
{
NSData *d = [NSData dataWithBytes:data length:length];
NSLog([NSString stringWithFormat:#"%#",d]);
NSString *s = [[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding];
const char* clearChar = "!";
if ([self.label.text isEqualToString:#"Label"]) {
self.label.text = #"";
}
else if ([s isEqualToString:[NSString stringWithUTF8String:clearChar]]) {
self.label.text = #"";
}
else {
NSString *store = self.label.text;
NSString *full = [NSString stringWithFormat:#"%#%#",store,s];
self.label.text = full;
}
}
The final else statement fires somewhat as expected, and a value of 233! is printed out to the label over and over again, getting longer each time eventually forming things like 233!233!233! etc. As you can see, I am using a character (!) sent over a function to clear the label, but it never clears. The integer is the ASCII code for the exclamation point:
void clearLabel() {
int clearString = 33;
char excalamtion = clearString;
ble_write(excalamtion);
}
Why would this not clear the label? I assume it has something to do with the clashing formats, but I'm not really too good at that even after reading some documentation. For the else if statement I also tried this
if ([s isEqualToString:#"!"])
but that didn't work out either... Any help would be appreciated.
EDIT:
I forgot to put in my loop code so you can see function calls. Here it is:
void loop()
{
if ( ble_connected() ) {
int a = 223;
sendMyInt(a);
delay(1000);
clearLabel();
delay(1000);
}
ble_do_events();
}
EDIT 2:
Based on a suggestion by #Duncan C , I have isolated the problem to the fact that the data is being sent as one packet to the iPhone. Upon printing out my generated string when the data is received, the string 233! is received all at once rather than individual chars of 2 3 3, and one second later the signal to clear, !. The data takes two seconds to appear on my phone, indicating that both delays are being used. I need a way to separate the 2 3 3 packet from the ! packet.
First off, this line:
NSLog([NSString stringWithFormat:#"%#",d]);
Is sort of pointless. The stringWithFormat serves no real purpose, since NSLog takes a format string anyway.
Use this instead:
NSLog(#"%#", d);
You should probably also log the contents of "s" once you convert your NSData to an NSString. That will help you figure out what's going on.
What is likely going on is that your string is coming in as "233!", all together, 4 bytes at a time (assuming that your integer is == 233).
Your string is unlikely to ever contain just "!". Instead, it will likely contain "233!" (4 characters.) I say likely because it depends on how the data is packetized into BLE. Something that short should be sent all in 1 BLE packet, so you should get the entire string together.
You could use the NSString method rangeOfString: to search for your "!" string, and if it contains an "!", clear your label, but that won't really do any good either. If you're sending "233!", then the iOS code will see the exclamation point in the string it receives and simply clear the label.
Or does your arduino project first send "233", then after some other event, send the "!". You didn't make that clear.
Another problem: What does the Arduino String class do if the integer is less than 1000, or less than 100, and doesn't require 3 or 4 characters to convert to a char array? What is stored in the unused bytes? You're always sending 4 characters, which is probably wrong.
Adding in another ble_do_events(); after calling the sendMyInt(); function causes the data to be transmit in two separate packets.

if statements and stringWithFormat

I need to know if there is a way to use if statements to display certain nsstrings, depending on whether or not that NSString contains any data.
I have an nsstringcalled visitorInfo.
The string uses data from other strings (i.e. which operating system the user is running) and displays that info. Here is an example of what I'm talking about:
NSString *visitorInfo = [NSString stringWithFormat:#"INFO\n\nVisitor Location\n%#\n\nVisitor Blood Type\n\%#", _visitor.location, _visitor.bloodType];
And it would display like this:
INFO
Location
Miami, FL
Blood Type
O positive
However, I have several pieces of data that only load if the user chooses to do so. i.e their email address.
This section of code below would do what I want, but my visitorInfo string contains tons of different strings, and if I use this code below, then it won't load any of them if the user chooses not to submit his blood type.
if ([self.visitor.bloodType length] > 0) {
NSString *visitorInfo = [NSString stringWithFormat:#"INFO\n\nVisitor Location\n%#\n\nVisitor Blood Type\n\%#", _visitor.location, _visitor.bloodType];
}
So basically if their is data stored in bloodType then i went that code to run, but if there isn't any data I only want it to skip over bloodType, and finish displaying the rest of the data.
Let me know if you have any more questions
Additional details. I'm using an NSString for a specific reason, which is why I'm not using a dictionary.
Just build up the string as needed using NSMutableString:
NSMutableString *visitorInfo = [NSMutableString stringWithFormat:#"INFO\n\nVisitor Location\n%#, _visitor.location];
if ([self.visitor.bloodType length] > 0) {
[visitorInfo appendFormat:#"\n\nVisitor Blood Type\n\%#", _visitor.bloodType];
}
You can check if a string has any data in it by using the following
if([_visitor.location length]<1){
//This means there's no data and is a better way of checking, rather than isEqualToString:#"".
}else{
//there is some date here
}
** EDIT - (just re-reading your question, sorry this answer is dependant on _visitor.location being a string in the first place)*
I hope this helps
Try this -
NSString *str = #"INFO";
if (_visitor.location) {
str = [NSString stringWithFormat:#"\n\nVisitor Location\n%#",_visitor.location];
}
if (_visitor.bloodType) {
str = [NSString stringWithFormat:#"\n\nVisitor Blood Type\n\%#",_visitor.bloodType];
}

Trouble with string encoding and emoji

I've got some trouble to retrieve some text message from my server, especially with the encoding. Messages can be from many languages (so they can have accents, be in japanese,... ) and can include emoji.
I'm retrieving my message with a JSON with some info. Here is some logs example :
(lldb) po dataMessages
<__NSCFArray 0x14ecc7f0>(
{
author = "User 1";
text = "Hier, c'\U00c3\U00a9tait incroyable";
},
{
...
}
)
(lldb) po [[dataMessages objectAtIndex:0] objectForKey:#"text"]
Hier, c'était incroyable
I'm able to get the correct text with :
const char *c = [[[dataMessages objectAtIndex:indexPath.row] objectForKey:#"text"] cStringUsingEncoding:NSWindowsCP1252StringEncoding];
NSString *myMessage = [NSString stringWithCString:c encoding:NSUTF8StringEncoding];
However, if the message contains emoji, cStringUsingEncoding: return a NULL value.
I don't have control on my server, so I can't change their encoding before messages are sent to me.
The problem is determining the encoding correctly. Emoji are not part of NSWindowsCP1252StringEncoding so the conversion just fails.
Moreover, you are passing through an unnecessary stage. Do not make an intermediate C string! Just call NSString's initWithData:encoding:.
In your case, calling NSWindowsCP1252StringEncoding was always a mistake; I'm surprised that this worked for any string. C3A9 is Unicode (UTF8). So just call initWithData:encoding: with the UTF8 encoding (NSUTF8StringEncoding) from the get-go and all will be well.

accessing json content from dictionary

I have a dictionary containing Json data. I am not able to handle the data inside it. I tried enumerating but keeps failing.
This is the data format I am getting back using NSLog:
2013-03-21 12:48:36.973 SaleItems[21009:c07] {
article = "Surface's other shoe is about to drop: the full Windows 8, Intel Core-driven Surface is headed to stores in just a few weeks.";
id = 2;
title = "Microsoft Surface Pro hits U.S. and Canada ";
}
2013-03-21 12:48:36.974 SaleItems[21009:c07] {
article = "Hit by growing demand for Samsung devices, the iPhone will lose smartphone market share for the current quarter, says a Citi analyst";
id = 3;
title = "iPhone to shed market share";
}
2013-03-21 12:48:36.974 SaleItems[21009:c07] {
article = "The carrier says it plans to buy wireless spectrum and network assets from Atlantic Tele-Network, aka Alltel, in an effort to boost its spectrum coffers.";
id = 4;
title = "AT&T spends $780 million ";
}
I know from the looks of it that I am getting back a dictionary of arrays. How can I access the arrays and elements inside them?
It looks like you have key as article, id and title, simply access the object in loop as:
*I am assuming that you have a Class called SalesItem and the above log are from each object of that
for(SalesItem *object in SalesItem){
NSLog(#"Article : %#",object[#"article"]);
NSLog(#"Id : %#",object[#"id"]);
NSLog(#"Title : %#",object[#"title"]);
}

Integer from .plist

I'm new to plists, and I really need to use one. What I have is a plist where different numbers are stored, under two dictionaries. I need to get that number which is stored, and make it an integer. This process will be run from a method called 'readPlist.
The plist is called 'properties.plist'. The first dictionary is called 'Enemies'. It contains various other dictionaries, which will have the name stored in the NSMutableString called 'SpriteType'. The name of the number will have the format 'L - %d', with the %d being an integer called 'LevelNumber'.
If possible, can someone give me the code on how to get that integer using the information, and the names of dictionaries above.
I have looked around at how to access plists, but the code that people have shown doesn't work.
Thanks in advance.
EDIT: Too make it more understandable, this is my plist. What i want in an integer, called 'SpriteNumber' to be equal to the value of 'L - %d'
If you read the contents of your plist into a dictionary (I won't tell you how to do it, but this is the tutorial I refer to often), then it's a matter of getting the string out of the key for the level with [[myDictionary objectForKey:#"key"]stringValue];. Then, using of NSString's extremely helpful -stringByReplacingOccurencesOfString:withString: to get rid of the "L -" part and only get a numerical value. Finally, get an integer from the string with [myString intValue].
well, the easiest way would be something like :
-(int) getMosquitoCountForLevel:(int) level {
int mosquitoCount=0;
NSString *gsFile = #"whateverFullyQualifiedFileNameYourPlistIs";
NSDictionary* definitions = [NSDictionary dictionaryWithContentsOfFile:gsFile];
NSDictionary* mosquitos = [definitions objectForKey:#"Mosquito"];
if(mosquitos) {
NSString *levelKey = [NSString stringWithFormat:#"L - %d",level];
NSNumber *mosquitoCountAsNumber = [mosquitos objectForKey:levelKey];
if(mosquitoCountAsNumber) {
mosquitoCount=[mosquitoCountAsNumber intValue];
} else {
CCLOGERROR(#"%# - Mosquito definitions in %# does not contain a en entry for level %#.",self.class,gsFile,levelKey);
}
} else {
CCLOGERROR(#"%# - file %# does not contain a Mosquito dictionary.",self.class,gsFile);
}
return mosquitoCount;
}
this compiles but not tested with actual data.

Resources