Make iOS device as source client using Icecast - ios

I want to make iOS device as source client to send the audio to icecast server. I have setup the icecast server on localhost successfully. Right now when I send below request to server it creates the mountpoint but only for first 8 sec of the audio, the total length of audio is 25 sec. I know this is because I'm not sending the data in chunks. Below is my request:
let requestString = "http://localhost:8000/"
let url = URL(string: requestString)
let mountPointName = "myMountPoint"
var request = URLRequest(url: url!)
request.httpMethod = "SOURCE /\(mountPointName) ICE/1.0"
request.addValue("SOURCE /mp3test ICE/1.0", forHTTPHeaderField: "SOURCE")
request.addValue("audio/ogg", forHTTPHeaderField: "Content-Type")
request.setValue("Basic c291cmNlOmhhY2ttZQ==", forHTTPHeaderField: "Authorization")
request.setValue("Server name", forHTTPHeaderField: "ice-name")
request.setValue("https://www.google.com", forHTTPHeaderField: "ice-url")
request.setValue("RockTest", forHTTPHeaderField: "ice-genre")
request.setValue("128", forHTTPHeaderField: "ice-bitrate")
request.setValue("0", forHTTPHeaderField: "ice-private")
request.setValue("1", forHTTPHeaderField: "ice-public")
request.setValue("Server description", forHTTPHeaderField: "ice-description")
request.setValue("samplerate=44100;bitrate=128;channels=2", forHTTPHeaderField: "ice-audio-info")
self.recurseivelySendRequest(request)
My recursive function:
func recurseivelySendRequest(_ _request: URLRequest) {
var request = _request
do {
let fileURL = Bundle.main.url(forResource: "mount", withExtension: "ogg") let data = try Data(contentsOf: fileURL!)
let inputStream = InputStream(data: data)
request.httpBodyStream = inputStream
} catch let error {
print(error)
}
// let dataTask = URLSession.shared.uploadTask(withStreamedRequest: request)
// dataTask.resume()
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
if (error != nil) {
print(error ?? "error = unknown")
return
}
print(response ?? "response = unknown")
do {
let json = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)
print(json)
} catch let error {
print(error)
let str = String.init(data: data!, encoding: String.Encoding.utf8)
print(str ?? "str = nil")
}
self.recurseivelySendRequest(request)
}
dataTask.resume()
}
The problem with above code is it sends only few part of the audio also If I don't send the request recursively the mountpoint no longer exists and seems it is replacing the data not adding. So what I want is to create the mountpoint once and then write the audio data in chunks.
I also followed Icecast 2: protocol description, streaming to it using

I was on wrong path finally I found iOS-Icecast-Client on github. It helps me to make a connection with icecast-server and send the recorder audio to server in chunks. As I have already mentioned that I was able to setup the icecast-server on localhost.
After downloading the above library in AudioProcessor.m file there is a line
ShoutOutputStream_Init("192.x.x.x", 8000, "/stream", "source", "hackme", 0);
Where 192.x.x.x is your wifi ip as you can not use localhost in device to connect it. Also make sure your system and device should be on same wifi connection to reach the network.
8000 is your port defined in icecast.xml.
/stream is mount point name defined in icecast.xml.
And here comes where I spent few hours to get the username and password these are also defined in authentication tag in icecast.xml. I used the default one source and hackme.
0 is whether it is vorbis (0) or a .mp3 (1) file.
Finally I got it working. And Many thanks to gstream79 to provide us such a wonderful library.

Related

Convert CURL command to URLRequest

I'm trying to convert a curl command in order to use it within swift for an ios app i'm making. I'm using a playground project to try out this feature.
The curl command is as follows:
curl -L -X POST 'https://myurl.com/mydata' \-H 'x-api-key: xxx' \-H 'Content-Type: application/json' \-d '{"uniqueNumber": “F4”}’
When I enter this into terminal, my data is displayed.
I have browsed through stackoverflow and managed to find articles like this and this
I'm still fairly new to swift and from what I understand, curl does not work in swift and so you have to convert it to a URLRequest.
I have attempted this with my code below but keep getting a message that says "Missing Authentication Token". What am I doing wrong?
import PlaygroundSupport
import Foundation
let key = "xxx"
let url = URL(string: "https://myurl.com/mydata")
let contentType = "application/json"
let uniqueNumber = "F4"
var request = URLRequest(url: url!)
request.addValue("x-api-key: \(key)", forHTTPHeaderField: "-H")
request.addValue("Content-Type: \(contentType)", forHTTPHeaderField: "-H")
request.addValue("uniqueNumber: \(uniqueNumber)", forHTTPHeaderField: "-d")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard error == nil else {
print(error!)
return
}
guard let data = data else {
print("Data is empty")
return
}
let json = try! JSONSerialization.jsonObject(with: data, options: [])
print(json)
}
task.resume()
PlaygroundPage.current.needsIndefiniteExecution = true
update -
Found the issue. I had to include the requestHTTP method, as well as the httpBody. After doing this it was fully working. See below for the working code:
import PlaygroundSupport
import Foundation
let key = "xxx"
let url = URL(string: "https://myurl.com/mydata")
let contentType = "application/json"
//setting and converting the uniqueNumber (input) to a data item so it can be recognized by the API
var uniqueNumber: Data? = "{\"uniqueNumber\": \"F09\"}".data(using: .utf8) // non-nil
var request = URLRequest(url: url!)
request.httpMethod = "POST"
request.addValue(key, forHTTPHeaderField: "x-api-key")
request.addValue(contentType, forHTTPHeaderField: "Content-Type")
request.httpBody = uniqueNumber
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard error == nil else { print(error!.localizedDescription); return }
guard let data = data else { print("Empty data"); return }
if let str = String(data: data, encoding: .utf8) {
print(str)
}
}.resume()
PlaygroundPage.current.needsIndefiniteExecution = true
You need to set header like this...
request.addValue(key, forHTTPHeaderField: "x-api-key")
request.addValue(contentType, forHTTPHeaderField: "Content-Type")
request.addValue(uniqueNumber, forHTTPHeaderField: "uniqueNumber")
You were setting invalid headers for a request.
EDIT
You also need to add Authentication Token in header like below.
let authToken = "THIS IS AUTHENTICATION TOKEN TO BE PASSED ON SERVER"
request.addValue(authToken, forHTTPHeaderField: "authenticationToken")
//Please make sure to pass the authentication token the key "authenticationToken".
//Please change as per actual key to be passed

My url is not responding

Hi there I'm trying to make a post request so I made a class, a simple class to test the url but is not responding, I mean I can use other url different to the url that I suppose to use and It's responding so the request it's ok what is not working is the url. The weird thing is that in postman the url is working the server response ok. I also enable the app transport security allow arbitrary load to yes and still not working could you have any idea why is this? Thanks in advance.
Here is my code
#IBAction func buton(_ sender: Any) {
let parameters: [String : Any] = ["acceptPrivacyNotice": true,
"name": "xxxx xxxx",
"email":"xxx#mail.com",
"password": "qwerty2012",
"passwordConfirm": "qwerty2012",
"deviceID": "",
"isProvider": false,
"idTypeProvider":1 ]
guard let url = URL(string: "https://www.apps-sellcom-dev.com/Engie/api/account/register") else {return}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("YOURAPIKEY==", forHTTPHeaderField: "Authorization")
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) else {
return
}
request.httpBody = httpBody
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print("Response",response)
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
}
The certificate of the domain is about to expire, so I guess thats why the URL wasn't found.
I enabled the app transport security and set the URL in the info.plist using LSApplicationQueriesSchemes, and now I'm getting a response from the server.

Swift 3 URLSession sending empty request

I can't get the URLSession to send anything in the body of a POST request.
Here is my code:
// Set up the request
var request = URLRequest(url: URL(string: baseURL + url)!)
request.httpMethod = "POST"
let jsonData = try JSONSerialization.data(withJSONObject: values,
options: .prettyPrinted)
request.httpBody = jsonData
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let config = URLSessionConfiguration.ephemeral
let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) { data, response, err in
if let err = err {
print(err)
}
else {
let jsondata = data!
do {
let sessionData =
try JSONSerialization.jsonObject(with: jsondata, options: .mutableLeaves)
as? Dictionary<String, String>
callback.taskComplete(response: sessionData, task: task)
}
catch {
print(error)
}
}
}
task.resume()
The server receives the request, but the body is empty and the content type header is null. I can't use the default session because I'm developing with self-signed certificates, so I need my class to be a URLSession delegate.
I'm using Swift 3, XCode 8, and iOS 10. Any help would be appreciated.
Problem solved. It was a combination of errors. If the URL doesn't have the trailing "/", Jetty sends a 302 redirect to the same URL with the slash appended. The iOS client does the redirect with a "GET," so there is nothing in the body. When I add the trailing "/", the request works fine.
I hope this answer will help someone doing iOS development. I searched Google for hours before I posted this question. I finally found the answer in a post about the Apache Java HttpClient. It has the same behavior.
Thank you for all the responses.

Making HTTP Request with header in Swift

I am trying to make an HTTP request to the Imgur API. I am trying to retrieve all images associated with the tag "cats." The url, according to the Imgur API is: https://api.imgur.com/3/gallery/t/cats
the Imgur API states the following about the authorization needed to make get requests:
For public read-only and anonymous resources, such as getting image
info, looking up user comments, etc. all you need to do is send an
authorization header with your client_id in your requests. This also
works if you'd like to upload images anonymously (without the image
being tied to an account), or if you'd like to create an anonymous
album. This lets us know which application is accessing the API.
Authorization: Client-ID YOUR_CLIENT_ID
I've looked at the following questions and tried things suggested there, but none of them have helped.
JSON NSURLRequest with credentials
Swift GET request with parameters
How to make a Http get and set httpHeader in Swift?
My current code is this:
let string = "https://api.imgur.com/3/gallery/t/cats"
let url = NSURL(string: string)
let request = NSMutableURLRequest(URL: url!)
request.setValue("clientIDhere", forHTTPHeaderField: "Authorization")
//request.addValue("clientIDhere", forHTTPHeaderField: "Authorization")
request.HTTPMethod = "GET"
let session = NSURLSession.sharedSession()
let tache = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if let antwort = response as? NSHTTPURLResponse {
let code = antwort.statusCode
print(code)
}
}
tache.resume()
But I continually get a status code of 403, meaning authorization is required. What am I doing wrong?
I think you need to prepend Client-ID string to your actual client ID as for the header value:
request.setValue("Client-ID <your_client_id>", forHTTPHeaderField: "Authorization")
Updated for swift 4 :
func fetchPhotoRequest(YOUR_CLIENT_ID: String) {
let string = "https://photoslibrary.googleapis.com/v1/albums"
let url = NSURL(string: string)
let request = NSMutableURLRequest(url: url! as URL)
request.setValue(YOUR_CLIENT_ID, forHTTPHeaderField: "Authorization") //**
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
let mData = session.dataTask(with: request as URLRequest) { (data, response, error) -> Void in
if let res = response as? HTTPURLResponse {
print("res: \(String(describing: res))")
print("Response: \(String(describing: response))")
}else{
print("Error: \(String(describing: error))")
}
}
mData.resume()
}

Swift to php on local server doesn't work on my device?

I am having an issue with the following code, it works on the iphone 5s simulator. But when i attach my iphone5s device it doesn't work. With the simulator i get this (as expected) back from swiftupload.php
Button pressed <- swift
responseString = Optional({"message":"some variable"}Success) <- from php
Email has ben sent <- swift
And with my device attached i get
Button pressed
responseString = Optional()
The php file looks like:
$postdata = json_decode(file_get_contents("php://input"), TRUE);
$message = $postdata["data"];
// Store values in an array
$returnValue = array("message" => $message);
// Send back request in JSON format
echo json_encode($returnValue);
And this is the function in swift
func postToServerFunction(){
// json php
let request = NSMutableURLRequest(URL: NSURL(string: "http://localhost/mydomain.com/swiftupload.php")!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
let params = ["data":"some variable"] as Dictionary<String, String>
request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
//Response print
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
if let HTTPResponse = response as? NSHTTPURLResponse {
let statusCode = HTTPResponse.statusCode
if statusCode == 200{
print("Email has ben sent")
}
}
})
task.resume()
print("Button pressed")
}
On the iPhone the localhost is IP of the iPhone.
Replace "localhost" with IP of your MAC/PC and check App Transport Security exceptions https://stackoverflow.com/a/30732693/4755417

Resources