Background: I am consuming a service which returns data with a MIME type of audio/wav. I need to provide a playback mechanism for this audio (currently built as an MVC application). As an example, my endpoint looks something like https://audio.fooservice.com/GetAudio?audioId=123
The audio is 8kHz, 1-channel u-law.
Due to varying format support across browsers when using the HTML5 <audio> tag, I am unable to use the original u-law wav because Internet Explorer will not play it.
My proposed solution is to do a real-time conversion from the source format to mp3.
I've cobbled together a partially working solution from various other questions here and in the NAudio forums, but it throws an exception as noted in the comments below:
private void NAudioTest(string url)
{
Stream outStream = new MemoryStream();
var format = WaveFormat.CreateMuLawFormat(8000, 1);
using (Stream ms = new MemoryStream())
{
var request = (HttpWebRequest)WebRequest.Create(url);
request.KeepAlive = false;
request.ProtocolVersion = HttpVersion.Version10;
using (Stream stream = request.GetResponse().GetResponseStream())
{
using (var reader = new RawSourceWaveStream(stream, format))
{
// reader is not seekable; we need to convert to a byte array to seek
var bytes = reader.ToByteArray();
// create a new stream from the byte aray
var seekableStream = new MemoryStream(bytes);
// instantiating a WaveFileReader as follows will throw an exception:
// "System.FormatException: Not a WAVE file - no RIFF header"
using (var waveReader = new WaveFileReader(seekableStream))
{
using (var pcmStream = WaveFormatConversionStream.CreatePcmStream(waveReader))
{
var pcmBytes = pcmStream.ToByteArray();
var mp3 = pcmBytes.ToMp3();
}
}
}
}
}
}
public static class StreamExtensions
{
public static byte[] ToByteArray(this Stream stream)
{
var ms = new MemoryStream();
var buffer = new byte[1024];
int bytes = 0;
while ((bytes = stream.Read(buffer, 0, buffer.Length)) > 0)
ms.Write(buffer, 0, bytes);
return ms.ToArray();
}
}
public static class ByteExtensions
{
public static byte[] ToMp3(this byte[] bytes)
{
using (var outStream = new MemoryStream())
{
using (var ms = new MemoryStream(bytes))
{
using (var reader = new WaveFileReader(ms))
{
using (var writer = new LameMP3FileWriter(outStream, reader.WaveFormat, 64))
{
reader.CopyTo(writer);
return outStream.ToArray();
}
}
}
}
}
}
I've been poking around at this for most of the day and I feel like I'm introducing unnecessary complexity into something that seems like it should be fairly straightforward.
Any help would be much appreciated.
Note: I cannot change the source format and supporting IE is a requirement.
EDIT: I resolved the RIFF exception and am able to produce a stream of the MP3, but it's nothing but white noise. Hopefully I can resolve that as well. My new code is as follows:
[HttpGet]
public ActionResult GetMp3(string url)
{
if (String.IsNullOrWhiteSpace(url))
return null;
var muLawFormat = WaveFormat.CreateMuLawFormat(8000, 1);
var compressedStream = new MemoryStream();
using (var ms = new MemoryStream())
{
var request = (HttpWebRequest)WebRequest.Create(url);
request.KeepAlive = false;
request.ProtocolVersion = HttpVersion.Version10;
using (Stream webStream = request.GetResponse().GetResponseStream())
{
var buffer = new byte[4096];
int read;
while (webStream != null && (read = webStream.Read(buffer, 0, buffer.Length)) > 0)
ms.Write(buffer, 0, read);
}
ms.Position = 0;
using (WaveStream wav = WaveFormatConversionStream.CreatePcmStream(new RawSourceWaveStream(ms, muLawFormat)))
using (var mp3 = new LameMP3FileWriter(compressedStream, new WaveFormat(), LAMEPreset.MEDIUM_FAST))
wav.CopyTo(mp3);
}
compressedStream.Seek(0, 0);
return new FileStreamResult(compressedStream, "audio/mpeg");
}
This works for me (and I needed to do exactly what you wanted to do). Hope this helps someone else as well. I used NAudio with LAME.
You have to make sure that you copy the libmp3lamexx.dll files to your webserver's BIN location or to some folder in the %PATH% variable, else it won't work.
string sq = /* URL of WAV file (http://foo.com/blah.wav) */
Response.ContentType = "audio/mpeg";
using (WebClient wc = new WebClient())
{
if (!sq.ToLower().EndsWith(".wav"))
{
byte[] rawFile = wc.DownloadData(sq.Trim());
Response.OutputStream.Write(rawFile, 0, rawFile.Length);
}
else
{
using (var wavReader = new WaveFileReader(new MemoryStream(wc.DownloadData(sq.Trim()))))
{
try
{
using (var wavWriter = new LameMP3FileWriter(Response.OutputStream, wavReader.WaveFormat, LAMEPreset.ABR_128))
{
wavReader.CopyTo(wavWriter);
}
}
catch (ArgumentException)
{
var newFormat = new WaveFormat(wavReader.WaveFormat.SampleRate, 16, 2);
using (var pcmStream = new WaveFormatConversionStream(newFormat, wavReader))
{
using (var wavWriter = new LameMP3FileWriter(Response.OutputStream, pcmStream.WaveFormat, LAMEPreset.ABR_128))
{
pcmStream.CopyTo(wavWriter);
}
}
}
}
}
Response.Flush();
Response.End();
}
Related
I use Media Plugin to Pick video from ios and Android devices, but size of stream is very large,
my question is how reduce this stream to send to server.
var file = await CrossMedia.Current.PickVideoAsync();
if (file != null)
{
stream = file.GetStream();
byte[] result;
using (var streamReader = new MemoryStream())
{
stream.CopyTo(streamReader);
result = streamReader.ToArray();
}
}
You could set the CompressionQuality, which is a value from 0 the most compressed all the way to 100 .
var file = await CrossMedia.Current.TakeVideoAsync(new StoreVideoOptions
{
CompressionQuality = 85,
Quality = VideoQuality.Medium
});
Update
using System.IO;
using System.IO.Compression;
public static byte[] Compress(byte[] data)
{
var output = new MemoryStream();
using (var dstream = new DeflateStream(output, CompressionLevel.Optimal))
{
dstream.Write(data, 0, data.Length);
}
return output.ToArray();
}
I have a requirement to post binary file of size 100MB data in the format of either JSON or byte array to Web API 1.1.
My client application is C# winforms application with x32 bit architecture. Where as I want to perform reading binary file from this client application and send this binary file byte array to Web API.
Current implementation in my winforms application is as below
var sFile = #"C"\binary.zip";
var mybytearray = File.ReadAllBytes(sFile);
var webRequest =
(HttpWebRequest)WebRequest.Create("http://localhost/filewriter");
webRequest.ContentType = "text/plain";
webRequest.Method = WebRequestMethods.Http.Post;
webRequest.AllowWriteStreamBuffering = true;
webRequest.Timeout = 100000;
webRequest.Headers.Add("fileName", Path.GetFileName(sFile));
webRequest.ContentLength = mybytearray.Length;
using (var dataStream = new StreamWriter(webRequest.GetRequestStream()))
dataStream.Write(mybytearray);
using (var response = webRequest.GetResponse())
{
if(response.StatusCode = HttpStatusCode.Ok;
return true;
}
below is written at my Web api method
[HttpPost]
public HttpResponseMessage filewriter(byte[] binaryData)
{
using (FileStream binaryFileStream = new FileStream("C:\\myNewFile.zip", FileMode.Create, FileAccess.ReadWrite))
{
binaryFileStream.Write(binaryData, 0, binaryData.Length);
}
}
As you can see, in above code I was not able to send byte array to web api method filewriter. Am I missing something that should work in this case.
Other way as I said I was tried same but instead of byte array with Json one as below
var sFile = #"C"\binary.zip";
var mybytearray = File.ReadAllBytes(sFile);
var mymodel = new model
{
fileName = sFile,
binaryData = mybytearray
};
var jsonResendObjects = JsonConvert.SerializeObject(mymodel);
var webRequest = (HttpWebRequest)WebRequest.Create("http://localhost/filewriter");
webRequest.ContentType = "application/json";
webRequest.Method = WebRequestMethods.Http.Post;
webRequest.AllowWriteStreamBuffering = true;
webRequest.Timeout = 100000;
webRequest.Headers.Add("fileName", Path.GetFileName(sFile));
webRequest.ContentLength = jsonResendObjects.Length;
byte[] responseData = null;
webRequest.AllowWriteStreamBuffering = true;
using (var dataStream = new StreamWriter(webRequest.GetRequestStream()))
dataStream.Write(jsonResendObjects);
On web api side
[HttpPost]
public HttpResponseMessage filewriter([FromBody]model mymodel)
{
using (FileStream binaryFileStream = new FileStream("C:\\myNewFile.zip", FileMode.Create, FileAccess.ReadWrite))
{
binaryFileStream.Write(mymodel.binarydata, 0, binaryDatabinarydat.Length);
}
}
According to me, it would be easy to use base64 encoding for
communication.
If you want to do so
First, convert your file to byte[] and then to base64 string
Like this:
byte[] bytes = File.ReadAllBytes("path");
string file = Convert.ToBase64String(bytes);
// You have base64 Data in "file" variable
On your WebAPI Endpoint accept string
[HttpPost]
public HttpResponseMessage filewriter(string fileData)
{
}
Then convert your base64 string back to byte[] and write it to file or whatever you want to do with that.
Like This:
// put your base64 string in b64str
Byte[] bytes = Convert.FromBase64String(b64Str);
File.WriteAllBytes(path, bytes);
And you can Compress your string Using GZIP Like this
public static void CopyTo(Stream src, Stream dest) {
byte[] bytes = new byte[4096];
int cnt;
while ((cnt = src.Read(bytes, 0, bytes.Length)) != 0) {
dest.Write(bytes, 0, cnt);
}
}
public static byte[] Zip(string str) {
var bytes = Encoding.UTF8.GetBytes(str);
using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream()) {
using (var gs = new GZipStream(mso, CompressionMode.Compress)) {
//msi.CopyTo(gs);
CopyTo(msi, gs);
}
return mso.ToArray();
}
}
public static string Unzip(byte[] bytes) {
using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream()) {
using (var gs = new GZipStream(msi, CompressionMode.Decompress)) {
//gs.CopyTo(mso);
CopyTo(gs, mso);
}
return Encoding.UTF8.GetString(mso.ToArray());
}
}
Reference:-
Convert file to base64 and back
GZip Compression
I am compressing a json using deflate compression technique and saving to sql server database. The json contains values from any culture ie. th-TH, zh-TW. The compressed string is getting saved successfully in database.
Json includes data like {"#id":"2113","description":"อาหารเช้าคอนติเนนทัล"}
Now when i read the same data from db, i convert it to bytes as
Encoding encoding = Encoding.UTF8;
encoding.GetBytes(data ?? string.Empty)
The compression like this
public static string Compress(this string data, CompressionTypeOptions compressionType)
{
var bytes = Compress(Encoding.UTF-8.GetBytes(data ?? string.Empty), compressionType);
return Encoding.UTF-8.GetString(bytes);
}
}
private static byte[] Compress(byte[] data, CompressionTypeOptions compressionType)
{
using (var memoryStream1 = new MemoryStream(data))
{
using (var memoryStream2 = new MemoryStream())
{
using (var compressionStream = CreateCompressionStream(compressionType, (Stream)memoryStream2,
CompressionMode.Compress))
{
CopyTo((Stream)memoryStream1, compressionStream);
compressionStream.Close();
return memoryStream2.ToArray();
}
}
}
}
Then decompressing like this
using (var memoryStream = new MemoryStream(data))
{
using (var compressionStream = CreateCompressionStream(compressionType, (Stream)memoryStream,
CompressionMode.Decompress))
return ReadAllBytesFromStream(compressionStream);
}
Here is ReadAllBytesFromStream definition
private static byte[] ReadAllBytesFromStream(Stream stream)
{
using (var memoryStream = new MemoryStream())
{
var buffer1 = new byte[1];
while (true)
{
int count = stream.Read(buffer1, 0, 1);
if (count != 0)
memoryStream.Write(buffer1, 0, count);
else
break;
}
var length = memoryStream.Length;
var buffer2 = new byte[length];
memoryStream.Position = 0L;
memoryStream.Read(buffer2, 0, (int)length);
return buffer2;
}
}
Getting error at int count = stream.Read(buffer1, 0, 1); as
'System.IO.InvalidDataException'
'Unknown block type. Stream might be corrupted.'
Any help is appreaciated
I am trying to do this using C#(Winforms).
The code I am using is giving me a string as an output, but I need to have a zipped file.
I am using the following code
try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(
"ftp:SITENAME/FILENAME.zip");
request.Method = WebRequestMethods.Ftp.DownloadFile;
// This example assumes the FTP site uses anonymous logon.
request.Credentials = new NetworkCredential("", "");
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
//StreamWriter writer = new StreamWriter(responseStream);
MessageBox.Show(reader.ReadtoEnd);
MessageBox.Show("Download Complete, status {0}" + response.StatusCode);
reader.Close();
response.Close();
}
catch (NotSupportedException ne)
{
MessageBox.Show(ne.Message);
}
I have an idea that I have to use Open source library from SharpZipLib to convert this string to a zipped file. But,I cannot find any sample code to show me how to do that.
I would really appreciate if someone can guide me through the process.
Thanks,
Sidhanshu
This might do just that.
using ICSharpCode.SharpZipLib.BZip2;
public static string Unzip(byte[] compressedbytes)
{
string result;
MemoryStream m_msBZip2 = null;
BZip2InputStream m_isBZip2 = null;
m_msBZip2 = new MemoryStream(compressedbytes);
// read final uncompressed string size stored in first 4 bytes
using (BinaryReader reader = new BinaryReader(m_msBZip2, System.Text.Encoding.ASCII))
{
Int32 size = reader.ReadInt32();
m_isBZip2 = new BZip2InputStream(m_msBZip2);
byte[] bytesUncompressed = new byte[size];
m_isBZip2.Read(bytesUncompressed, 0, bytesUncompressed.Length);
m_isBZip2.Close();
m_msBZip2.Close();
result = Encoding.ASCII.GetString(bytesUncompressed, 0, bytesUncompressed.Length);
reader.Close();
}
return result;
}
public static byte[] Zip(string sBuffer)
{
byte[] result;
using (MemoryStream m_msBZip2 = new MemoryStream())
{
Int32 size = sBuffer.Length;
// Prepend the compressed data with the length of the uncompressed data (firs 4 bytes)
using (BinaryWriter writer = new BinaryWriter(m_msBZip2, System.Text.Encoding.ASCII))
{
writer.Write(size);
using (BZip2OutputStream m_osBZip2 = new BZip2OutputStream(m_msBZip2))
{
m_osBZip2.Write(Encoding.ASCII.GetBytes(sBuffer), 0, sBuffer.Length);
m_osBZip2.Close();
}
writer.Close();
result = m_msBZip2.ToArray();
m_msBZip2.Close();
}
}
return result;
}
I'm trying to use a local c# app to pull some images off a website to files on my local machine. I'm using the code listed below. I've tried both ASCII encoding and UTF8 encoding but the final file is not an correct. Does anyone see what I'm doing wrong? The url is active and correct and show the image just fine when I put the address in my browser.
private void button1_Click(object sender, EventArgs e)
{
HttpWebRequest lxRequest = (HttpWebRequest)WebRequest.Create("http://www.productimageswebsite.com/images/stock_jpgs/34891.jpg");
// returned values are returned as a stream, then read into a string
String lsResponse = string.Empty;
HttpWebResponse lxResponse = (HttpWebResponse)lxRequest.GetResponse();
using (StreamReader lxResponseStream = new StreamReader(lxResponse.GetResponseStream()))
{
lsResponse = lxResponseStream.ReadToEnd();
lxResponseStream.Close();
}
byte[] lnByte = System.Text.UTF8Encoding.UTF8.GetBytes(lsResponse);
System.IO.FileStream lxFS = new FileStream("34891.jpg", FileMode.Create);
lxFS.Write(lnByte, 0, lnByte.Length);
lxFS.Close();
MessageBox.Show("done");
}
nice image :D
try using the following code:
you needed to use a BinaryReader, 'cause an image file is binary data and thus not encoded in UTF or ASCII
edit: using'ified
HttpWebRequest lxRequest = (HttpWebRequest)WebRequest.Create(
"http://www.productimageswebsite.com/images/stock_jpgs/34891.jpg");
// returned values are returned as a stream, then read into a string
String lsResponse = string.Empty;
using (HttpWebResponse lxResponse = (HttpWebResponse)lxRequest.GetResponse()){
using (BinaryReader reader = new BinaryReader(lxResponse.GetResponseStream())) {
Byte[] lnByte = reader.ReadBytes(1 * 1024 * 1024 * 10);
using (FileStream lxFS = new FileStream("34891.jpg", FileMode.Create)) {
lxFS.Write(lnByte, 0, lnByte.Length);
}
}
}
MessageBox.Show("done");
Okay, here's the final answer. It uses a memorystream as a way to buffer the data from the reaponsestream.
private void button1_Click(object sender, EventArgs e)
{
byte[] lnBuffer;
byte[] lnFile;
HttpWebRequest lxRequest = (HttpWebRequest)WebRequest.Create("http://www.productimageswebsite.com/images/stock_jpgs/34891.jpg");
using (HttpWebResponse lxResponse = (HttpWebResponse)lxRequest.GetResponse())
{
using (BinaryReader lxBR = new BinaryReader(lxResponse.GetResponseStream()))
{
using (MemoryStream lxMS = new MemoryStream())
{
lnBuffer = lxBR.ReadBytes(1024);
while (lnBuffer.Length > 0)
{
lxMS.Write(lnBuffer, 0, lnBuffer.Length);
lnBuffer = lxBR.ReadBytes(1024);
}
lnFile = new byte[(int)lxMS.Length];
lxMS.Position = 0;
lxMS.Read(lnFile, 0, lnFile.Length);
}
}
}
using (System.IO.FileStream lxFS = new FileStream("34891.jpg", FileMode.Create))
{
lxFS.Write(lnFile, 0, lnFile.Length);
}
MessageBox.Show("done");
}
A variation of the answer, using async await for async file I/O. See Async File I/O on why this is important.
Download png and write to disk using BinaryReader/Writer
string outFile = System.IO.Path.Combine(outDir, fileName);
// Download file
var request = (HttpWebRequest) WebRequest.Create(imageUrl);
using (var response = await request.GetResponseAsync()){
using (var reader = new BinaryReader(response.GetResponseStream())) {
// Read file
Byte[] bytes = async reader.ReadAllBytes();
// Write to local folder
using (var fs = new FileStream(outFile, FileMode.Create)) {
await fs.WriteAsync(bytes, 0, bytes.Length);
}
}
}
Read all bytes extension method
public static class Extensions {
public static async Task<byte[]> ReadAllBytes(this BinaryReader reader)
{
const int bufferSize = 4096;
using (var ms = new MemoryStream())
{
byte[] buffer = new byte[bufferSize];
int count;
while ((count = reader.Read(buffer, 0, buffer.Length)) != 0) {
await ms.WriteAsync(buffer, 0, count);
}
return ms.ToArray();
}
}
}
You can use the following method to download an image from a web site and save it, using the Image class:
WebRequest req = WebRequest.Create(imageUrl);
WebResponse resp = req.GetResponse();
Image img = Image.FromStream(resp.GetResponseStream());
img.Save(filePath + fileName + ".jpg");