I tried this
val player: MediaPlayer = MediaPlayerFactory("-vvv").mediaPlayers().newMediaPlayer()
val result0: Boolean = player.audio().setVolume(50) // result0: true
player.media().play("/path/to/audio.ogg")
val result1: Boolean = player.audio().setVolume(50) // result1: false
and this
val player: MediaPlayer = MediaPlayerFactory("-vvv").mediaPlayers().newMediaPlayer()
val result0 = player.audio().setVolume(50) // result0: true
player.media().prepare("/path/to/audio.ogg")
val result1: Boolean = player.audio().setVolume(50) // result1: false
player.controls().play()
val result2: Boolean = player.audio().setVolume(50) // result2: false
but the volume remains at 100%.
The only way I found is to make something like this
val player: MediaPlayer = MediaPlayerFactory("-vvv").mediaPlayers().newMediaPlayer()
player.events().addMediaPlayerEventListener(object : MediaPlayerEventAdapter() {
override fun mediaPlayerReady(mediaPlayer: MediaPlayer) {
mediaPlayer.submit {
mediaPlayer.audio().setVolume(50)
}
}
})
player.media().play("/path/to/audio.ogg")
But the solution is a bit far from ideal. Because it starts to play, plays a bit, and then whoosh, the volume has changed.
I tried vlcj 4.4.0 and 4.5.2, VLC 3.0.8 and 3.0.10, jdk8 and 14, but it works in the same way.
This is something that unfortunately does not work in VLC 3.x, but does work in the upcoming VLC 4.x (at the time of writing this answer, VLC 4 is still in development).
The following code works for me using the latest VLC 4 built from source, and the latest vlcj-5 snapshot:
import uk.co.caprica.vlcj.player.component.AudioPlayerComponent;
import uk.co.caprica.vlcj.test.VlcjTest;
public class AudioMediaPlayerComponentTest extends VlcjTest {
public static void main(String[] args) throws Exception {
String mrl = "/home/music/some-cool-synthwave-tune.mp3";
AudioPlayerComponent audioMediaPlayerComponent = new AudioPlayerComponent();
audioMediaPlayerComponent.mediaPlayer().audio().setVolume(5);
audioMediaPlayerComponent.mediaPlayer().media().play(mrl);
Thread.currentThread().join();
}
}
The initial volume for the media player comes from the OS volume settings, and in fact the OS volume setting is linked both ways to the media player. Changing the volume in one place is reflected in the other.
Volume handling through LibVLC generally just seems much better in VLC 4.
If you're stuck on VLC 3, which is reasonable at the present time, then unfortunately you're also stuck with some sort of compromise solution like using the "ready" event that you've already found.
All the ready event does is to wait for the first position-changed event, and that event was created specifically as a compromise for purposes like this.
I tested all the native event callbacks available for the media player, and nothing worked to set the volume before playback had actually started.
This leaves you with the following, as you already found:
import uk.co.caprica.vlcj.player.base.MediaPlayer;
import uk.co.caprica.vlcj.player.component.AudioPlayerComponent;
import uk.co.caprica.vlcj.test.VlcjTest;
public class AudioMediaPlayerComponentTest extends VlcjTest {
public static void main(String[] args) throws Exception {
String mrl = "/home/music/some-cool-synthwave-tune.mp3";
AudioPlayerComponent audioMediaPlayerComponent = new AudioPlayerComponent() {
#Override
public void mediaPlayerReady(MediaPlayer mediaPlayer) {
mediaPlayer.audio().setVolume(30);
}
};
audioMediaPlayerComponent.mediaPlayer().media().play(mrl);
Thread.currentThread().join();
}
}
A completely sideways alternative might be to play the shortest possible silent media as a kind of pre-roll - when that media is finished (there's a finished or stopped event you can listen for) you should then be able to set the volume and play your actual media. I did not try this.
Related
I am developing an android application which processes Camera2 preview frames and displays processed frames on the Texture. At first, I tested with camera1 api, it works fine for real time image processing.
private class CameraPreviewCallback implements Camera.PreviewCallback {
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
processingRunnable.setNextFrame(data, camera);
}
}
Then, I changed my code which utilizes camera2 api. For getting preview frames, I set ImageFormat as YUV_420_888
mImageReaderPreview = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.YUV_420_888, 3);
mImageReaderPreview.setOnImageAvailableListener(mOnPreviewAvailableListener, mBackgroundHandler);
private final ImageReader.OnImageAvailableListener mOnPreviewAvailableListener = new ImageReader.OnImageAvailableListener() {
#Override
public void onImageAvailable(ImageReader reader) {
Image mImage = reader.acquireLatestImage();
if(mImage == null) {
return;
}
processingRunnable.setNextFrame(convertYUV420888ToNV21(mImage));
mImage.close();
}
};
However, it's working slower than camera1. May be it's because of having one extra conversion from YUV_420_888 to NV21. Since Camera1 can directly provides NV21 frame from Camera1.
Conversion could be expensive, depending on how you implement it and what the layout of the YUV_420_888 on a given device is.
Certainly if it's written in pure Java is probably going to be slow.
That said, if the device you're using is at the LEGACY hardware level, camera2 has to run in a legacy mode that can be slow for receiving YUV information. For those devices, staying on API1 may be preferable for your use case.
I have an older implementation using NAudio 1.6 to play a ring tone signalling an incoming call in an application. As soon as the user acceptes the call, I stop the playback.
Basically the follwing is done:
1. As soon as the I get an event that a call must be signalled, a timer is started
2. Inside this timer Play() on the player
3. When the timer starts again, a check is performed if the file is played by checking the CurrentTime property against the TotalTime propery of the WaveStream
4. When the user accepts the call, Stop() is called on the player and also stop the timer
The point is, that we run sometimes in cases where the playback is still repeated although the timer is stopped and the Stop() was called on the player.
In the following link I read that the classes BufferedWaveProvider and WaveChannel32 which are used in the code are always padding the buffer with zero.
http://mark-dot-net.blogspot.com/2011/05/naudio-and-playbackstopped-problem.html
Is it possible that the non-stopping playback is due to usage of the classes BufferedWaveProvider and WaveChannel32?
In NAudio 1.7 the AudioFileReader class is there. Is this class also padding with zeros? I did not find a property like PadWithZeroes in this class. Does it make to use AudioFileReader in this case of looped playback?
Below the code of the current implementation of the TimerElapsed
void TimerElapsed(object sender, ElapsedEventArgs e)
{
try
{
WaveStream stream = _audioStream as WaveStream;
if (stream != null && stream.CurrentTime >= stream.TotalTime )
{
StartPlayback();
}
}
catch (Exception ex)
{
//do some actions here
}
}
The following code creates the input stream:
private WaveStream CreateWavInputStream(string path)
{
WaveStream readerStream = new WaveFileReader(path);
if (readerStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm)
{
readerStream = WaveFormatConversionStream.CreatePcmStream(readerStream);
readerStream = new BlockAlignReductionStream(readerStream);
}
if (readerStream.WaveFormat.BitsPerSample != 16)
{
var format = new WaveFormat(readerStream.WaveFormat.SampleRate, 16, readerStream.WaveFormat.Channels);
readerStream = new WaveFormatConversionStream(format, readerStream);
}
WaveChannel32 inputStream = new WaveChannel32(readerStream);
return inputStream;
}
Has anyone heard of any issues with MessageDialog's not displaying on Windows 8 tablets? Or more specifically Samsung 700t? It uses a regular intel process and not ARM. I built the app on a laptop and the messagedialog shows when debugging from the laptop, shows on the tablet simulator but doesn't show on the actual tablet.
I'm using the Caliburn.Micro IResult interface to display the messagedialog in the view.
Heres snippits of code that I'm using:
public IEnumerable<IResult> NavExecute(String method)
{
Windows.UI.ViewManagement.ApplicationView.TryUnsnap();
var conn = NetworkInformation.GetInternetConnectionProfile();
if (conn.GetNetworkConnectivityLevel() != NetworkConnectivityLevel.InternetAccess)
{
yield return new MessageDialogResult("Internet Connection Not Detected", "Connection Error");
netOn = false;
}
}
the above is in my view model base class, and heres the implementation of the IResult class itself:
public class MessageDialogResult : ResultBase
{
private readonly string _content;
private readonly string _title;
public MessageDialogResult(string content, string title)
{
_content = content;
_title = title;
}
public async override void Execute(ActionExecutionContext context)
{
var dialog = new MessageDialog(_content, _title);
await dialog.ShowAsync();
OnCompleted();
}
}
I doub't it's an issue with the code since I'm debugging in x86 mode on both devices (before anyone asks why I'm not debugging for all devices it's because I'm using SQLite which requires a seperate package for each arhitecture.)
I'm not sure if theres a setting somewhere in Windows 8 that disables in app popups, but I couldn't find one.
Any ideas?
Are you handling the callback of Coroutine.Execute?
The callback on Execute might be calling back with an exception thrown by the coroutine - this would silently fail if you weren't explicitly looking for it in the callback
Coroutine.Execute(YourEnumerator(), new ActionExecutionContext { Blah }, (o, e) => {
if(e.Error != null) // Something went wrong
});
Maybe the async await is throwing or something like that (can't think why!)
Edit:
Ah additionally stuff in your enumerator could also throw:
Windows.UI.ViewManagement.ApplicationView.TryUnsnap();
var conn = NetworkInformation.GetInternetConnectionProfile();
Either one could throw making the outer enumerator swallow an exception if not handled in the callback - or could be a nullref on conn?
The reason why GetInternetConnectionProfile() was returning a null ref was due to the fact that when on a laptop, if you disconnect from a wireless connection the laptop's internet connection profile defaults to ethernet, whereas the tablet (at least the Samsung 700T) doesn't have an ethernet port so it's connection profile doesn't exist if a wireless connection isn't established.
Thanks to Charleh for pointing me in the right direction.
I want to play sound from a mp3 file in windows 8 metro-style app. I tried two approaches to do so:
Method1:
This is using the code provided by https://stackoverflow.com/a/10961201/147530. It works.
Method 2:
Here I just new a MediaElement and set its Source property like so:
var x = new MediaElement { Source = new Uri("ms-appx:/Assets/MyMp3File.mp3") };
When I do x.Play() nothing happens however. There are no exceptions thrown.
Question: How can I make method 2 work?
EDIT:
Wanted to update that none of the MediaFailed, MediaOpened, MediaEnded event handlers get called using Method 2.
sound = new MediaElement { Source = new Uri("ms-appx:/Assets/Clook.mp3") };
sound.MediaFailed += sound_MediaFailed;
sound.MediaOpened += sound_MediaOpened;
sound.MediaEnded += sound_MediaEnded;
static void sound_MediaEnded(object sender, RoutedEventArgs e)
{
Debugger.Break();
}
static void sound_MediaOpened(object sender, RoutedEventArgs e)
{
Debugger.Break();
}
static void sound_MediaFailed(object sender, ExceptionRoutedEventArgs e)
{
Debugger.Break();
}
A couple of things to try. Try the following code
var music = new MediaElement()
{
AudioCategory = AudioCategory.ForegroundOnlyMedia,
Source = new Uri(this.BaseUri, "Assets/MyMp3File.mp3")
};
// This is really the only difference, adding it to the visual tree
// LayoutRoot is the root of the visual tree, in the case, a grid in my XAML
LayoutRoot.Children.Add(music);
music.Play();
Adding it to the visual tree may be the key. Put a break point on that to make sure your MediaElement has data in it.
Second (and actually happened to me so, that's why I mention it), I was developing on a Samsung device from //Build that has a docking station. The audio jack on the device and the speakers are disabled when it is in the docking station. You have to plug a headset into the docking station directly or remove it from the docking station to hear any sound.
You have to put the MediaElement in the visualTree before to make it play any media :)
Use x.autoplay = true. With autoplay it will wait until it's loaded.
I'm currently developing a camera application for Android on which some problems have occurred. I need it to work on all Android devices and since all of these works in different ways specially with the camera hardware, I'm having a hard time finding a solution that works for every device.
My application main goal is to launch the camera on a button click, take a photo and upload it to a server. So I don't really need the functionality of saving the image on the device, but if that's needed for further image use I might as well allow it.
For example I'm testing my application on a Samsung Galaxy SII and a Motorola Pad. I got working code that launches the camera, which is by the way C# code since I'm using Monodroid:
Intent cameraIntent = new Intent(Android.Provider.MediaStore.ActionImageCapture);
StartActivityForResult(cameraIntent, PHOTO_CAPTURE);
And I fetch the result, similar to this guide I followed:
http://kevinpotgieter.wordpress.com/2011/03/30/null-intent-passed-back-on-samsung-galaxy-tab/
Why I followed this guide is because the activity returns null on my galaxy device (Another device oriented problem).
This code works fine on the Galaxy device. It takes a photo and saves the photo in the gallery from which i can upload to a server. By further research this is apparently galaxy standard behaviour, so this doesn't work on my Motorola pad. The camera works fine, but no image is saved to gallery.
So with this background my question is, am I on the right path here? Do I need to save the image to gallery in order for further use in my application? Is there any solution that works for every Android device, cause that's the solution i need.
Thanks for any feedback!
After reading the linked article, the approach taken in that article is geared toward the Galaxy line, since they appear to write to the gallery automatically.
This article discusses some other scenarios in detail:
Android ACTION_IMAGE_CAPTURE Intent
So, I don't necessarily think that following the linked article that you provided is the right path. Not all devices automatically write to the gallery as described in that article, afaik. The article I linked to points to the issues being related to security and suggests writing the image to a /sdcard/tmp folder for storing the original image. Going down a similar path would more than likely lead to code that is going to work reliably across many devices.
Here are some other links for reference:
Google discussion regarding this subject: http://code.google.com/p/android/issues/detail?id=1480
Project with potential a solution to the problem: https://github.com/johnyma22/classdroid
While that discussion/project are in Java/Android SDK, the same concepts should apply to Monodroid. I'd be happy to help you adapt the code to a working Mono for Android solution if you need help.
To long2know:
Yes the same concepts applies to Monodroid. I've already read the stack article you linked among with some other similar. However i don't like the approach in that particular post since it checks for bugs for some devices that are hardcoded into a collection. Meaning it might fail to detect bugs in future devices. Since i won't be doing maintenance on this application, i can't allow this. I found a solution elsewhere and adapted it to my case and i'll post it below if someone would need it. It works on both my devices, guessing it would work for the majority of other devices. Thanks for your post!
Solution that allows you to snap a picture and use, also with the option of using a image from gallery. Solution uses option menu for these purposes, just for testing. (Monodroid code).
Camera code is inspired by:
access to full resolution pictures from camera with MonoDroid
namespace StackOverFlow.UsingCameraWithMonodroid
{
[Activity(Label = "ImageActivity")]
public class ImageActivity
private readonly static int TakePicture = 1;
private readonly static int SelectPicture = 2;
private string imageUriString;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
this.SetContentView(Resource.Layout.ImageActivity);
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
MenuInflater flate = this.MenuInflater;
flate.Inflate(Resource.Menu.ImageMenues, menu);
return base.OnCreateOptionsMenu(menu);
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
switch (item.ItemId)
{
case Resource.Id.UseExisting:
this.SelectImageFromStorage();
return true;
case Resource.Id.AddNew:
this.StartCamera();
return true;
default:
return base.OnOptionsItemSelected(item);
}
}
private Boolean isMounted
{
get
{
return Android.OS.Environment.ExternalStorageState.Equals(Android.OS.Environment.MediaMounted);
}
}
private void StartCamera()
{
var imageUri = ContentResolver.Insert(isMounted ? MediaStore.Images.Media.ExternalContentUri
: MediaStore.Images.Media.InternalContentUri, new ContentValues());
this.imageUriString = imageUri.ToString();
var cameraIntent = new Intent(MediaStore.ActionImageCapture);
cameraIntent.PutExtra(MediaStore.ExtraOutput, imageUri);
this.StartActivityForResult(cameraIntent, TakePicture);
}
private void SelectImageFromStorage()
{
Intent intent = new Intent();
intent.SetType("image/*");
intent.SetAction(Intent.ActionGetContent);
this.StartActivityForResult(Intent.CreateChooser(intent,
"Select Picture"), SelectPicture);
}
// Example code of using the result, in my case i want to upload in another activity
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
// If a picture was taken
if (resultCode == Result.Ok && requestCode == TakePicture)
{
// For some devices data can become null when using the camera activity.
// For this reason we save pass the already saved imageUriString to the upload activity
// in order to adapt to every device. Instead we would want to use the data intent
// like in the SelectPicture option.
var uploadIntent = new Intent(this.BaseContext, typeof(UploadActivity));
uploadIntent.PutExtra("ImageUri", this.imageUriString);
this.StartActivity(uploadIntent);
}
// User has selected a image from storage
else if (requestCode == SelectPicture)
{
var uploadIntent = new Intent(this.BaseContext, typeof(UploadActivity));
uploadIntent.PutExtra("ImageUri", data.DataString);
this.StartActivity(uploadIntent);
}
}
}
}