using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Saar.FFmpeg.CSharp;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.Threading;
using Coolest.Windows;
using Coolest;
using System.IO;
namespace SaarFFmpeg.VideoTest
{
public class Program
{
public class BufferedWaveStream : IWaveStream
{
private MemoryStream mStream = new MemoryStream();
private long mReader;
private long mWriter;
private object mStreamLocker = new object();
public void AddSample(byte[] buffer, int start, int length)
{
lock (mStreamLocker)
{
long pos = mStream.Position;
mStream.Position = mWriter;
mStream.Write(buffer, start, length);
mWriter = mStream.Position;
mStream.Position = pos;
}
}
unsafe public void AddSample(byte* buffer, int start, int length)
{
UnmanagedMemoryStream memoryStream = new UnmanagedMemoryStream(buffer + start, length);
lock (mStreamLocker)
{
long pos = mStream.Position;
mStream.Position = mWriter;
memoryStream.CopyTo(mStream);
mWriter = mStream.Position;
mStream.Position = pos;
}
memoryStream.Dispose();
}
public WaveFormat Format
{
get;
private set;
}
public int Read(byte[] buffer, int start, int length)
{
lock (mStreamLocker)
{
int ret = 0;
long pos = mStream.Position;
mStream.Position = mReader;
ret = mStream.Read(buffer, start, length);
mReader = mStream.Position;
mStream.Position = pos;
return ret;
}
}
public BufferedWaveStream(WaveFormat waveFormat)
{
this.Format = waveFormat;
}
}
public class VideoFormPanel : Panel
{
object locker = new object();
List<Bitmap> mFrames = new List<Bitmap>();
Bitmap mBmp;
public VideoFormPanel()
{
this.DoubleBuffered = true;
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
}
public void UpdateBitmap(Bitmap bmp)
{
try
{
if (!this.IsHandleCreated || this.IsDisposed) return;
if (this.InvokeRequired)
{
this.Invoke(new Action(() =>
{
UpdateBitmap(bmp);
}));
return;
}
BMP = bmp;
this.Invalidate();
}
catch (Exception ee)
{
Console.WriteLine(ee.ToString());
}
}
public Bitmap BMP
{
get
{
lock (locker)
{
return mBmp;
}
}
set
{
lock (locker)
{
mBmp = value;
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
Bitmap bmp = BMP;
if (bmp != null)
{
e.Graphics.DrawImage(bmp, 0, 0, this.Width, this.Height);
}
base.OnPaint(e);
}
}
public static AudioFormat AudioFormatFromWaveFormat(Coolest.WaveFormat waveFormat, bool isPlanar = false)
{
return new AudioFormat(waveFormat.SampleRate, waveFormat.Channels, waveFormat.BitsPerSample, isPlanar);
}
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
OpenFileDialog openFile = new OpenFileDialog();
openFile.Filter = "MP4 File(*.mp4)|*.mp4";
openFile.FilterIndex = 1;
if (openFile.ShowDialog() != DialogResult.OK)
{
return;
}
Form form = new Form();
BufferedWaveStream waveStream = null;
VideoFormPanel panel = new VideoFormPanel();
panel.Dock = DockStyle.Fill;
form.Controls.Add(panel);
var media = new MediaReader(openFile.FileName);
var decoder = media.Decoders.OfType<VideoDecoder>().First();
var audioDecoder = media.Decoders.OfType<AudioDecoder>().First();
decoder.OutFormat = new VideoFormat(decoder.InFormat.Width, decoder.InFormat.Height, AVPixelFormat.Bgr24, 4);
VideoFrame frame = new VideoFrame();
AudioFrame audio = new AudioFrame();
AudioFrame convertedAudio = new AudioFrame();
waveStream = new BufferedWaveStream(new Coolest.WaveFormat(audioDecoder.InFormat.SampleRate, audioDecoder.InFormat.BitsPerSample, audioDecoder.InFormat.Channels));
Coolest.Windows.WasapiOut waveOut = new Coolest.Windows.WasapiOut(ShareMode.Shared, true, Role.Multimedia, 640);
waveOut.Initialize(waveStream);
AudioResampler resampler = new AudioResampler(AudioFormatFromWaveFormat(waveStream.Format, audioDecoder.InFormat.IsPlanarFormat), AudioFormatFromWaveFormat(waveOut.OutFormat));
waveOut.Resample += (o, format) => {
audioDecoder.OutFormat = new AudioFormat(format.OutFormat.SampleRate, format.OutFormat.Channels, format.OutFormat.ValidBitsPerSample);
};
Thread thread = new Thread(new ThreadStart(() => {
while (true)
{
bool hasVideo = false;
bool hasAudio = false;
//利用ReadPacket取得Packet
Packet packet = media.ReadPacket();
if (packet == null) break;
if (packet.StreamIndex == decoder.StreamIndex)
{
//影像串流
hasVideo = decoder.Decode(packet, frame);
if (hasVideo)
{
Bitmap image = new Bitmap(frame.Format.Width, frame.Format.Height, frame.Format.Strides[0], PixelFormat.Format24bppRgb, frame.Scan0);
DateTime dt = DateTime.Now;
panel.UpdateBitmap(image);
double elapsed = DateTime.Now.Subtract(dt).TotalMilliseconds;
double sleepTime = (1000.0 / media.FramesPerSecond) - elapsed;
if (sleepTime > 0)
Thread.Sleep((int)sleepTime);
}
}
else if (packet.StreamIndex == audioDecoder.StreamIndex)
{
//聲音串流
// TODO push to another thread
hasAudio = audioDecoder.Decode(packet, audio);
if (hasAudio)
{
//利用resampler轉換成waveOut期待收到的格式
resampler.Convert(audio, convertedAudio);
int bytes = convertedAudio.LineDataBytes;
unsafe
{
for (int i = 0; i < convertedAudio.Data.Length; ++i)
{
if (convertedAudio.Data[i].Equals(IntPtr.Zero)) break;
byte* ptr = (byte*)convertedAudio.Data[i].ToPointer();
int length = convertedAudio.LineDataBytes;
waveStream.AddSample(ptr, 0, length);
}
}
}
}
if (form.IsDisposed)
break;
}
}));
form.Disposed += (o, e) => { thread.Abort(); };
thread.IsBackground = true;
waveOut.Play();
thread.Start();
Application.Run(form);
}
}
}
SaarFFMPEG: VideoTest as a player
Be the first to comment
You can use [html][/html], [css][/css], [php][/php] and more to embed the code. Urls are automatically hyperlinked. Line breaks and paragraphs are automatically generated.