LVGL笔记-使用《avilib》库播放avi视频(lvgl在windows模拟运行)
Posted hwd00001
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LVGL笔记-使用《avilib》库播放avi视频(lvgl在windows模拟运行)相关的知识,希望对你有一定的参考价值。
文章目录
在github找到一个 avilib库,可以方便的读写avi文件。这里主要用到读取图片帧的功能。avi文件必须是MJPG编码,这样读取出来就是一张张的jpg图片,直接使用lvgl的第三方库《sjpg》解码。
如何生成MJPG编码的avi文件,请参考《 AVI视频封装格式(阅读笔记)》。
在windows仿真的视频:
lvgl播放avi视频
1.函数简介
这里只介绍读取avi用到的部分函数。
1.1 avi_t *AVI_open_input_file(const char *filename, int getIndex)
传入文件名,打开avi文件;返回avi_t指针,后面的操作都要用到这个指针。
1.2 long AVI_video_frames(avi_t *AVI)
获取avi文件的总帧数(有多少张jpg图片)。由视频的时长可以估算,一般是一秒20-30帧,以30帧算,如果视频是10秒长,那么总帧数是300张。
1.3 double AVI_frame_rate(avi_t *AVI)
这个函数直接返回 AVI->fps,每秒的帧数。更新图片的速度由这个参数决定。
1.4 int AVI_set_video_position(avi_t *AVI, long frame)
设置AVI->pos.
传入参数frame是帧索引,范围在0-总帧数之间。
返回值:0表示正常,非0表示出错。
1.5 long AVI_read_frame(avi_t *AVI, char *vidbuf, int *keyframe)
读取AVI->pos对应的图片,把数据存放到vidbuf,如果是关键帧,keyframe = 1,否则keyframe = 0.
返回值:本帧数据大小,单位是字节。
1.6 AVI_close((avi_t *) avi)
关闭avi文件。
2.操作流程
3.源代码
这里有一个疑问,如果一开始设置帧索引为0,会闪退出;设置到10开始就正常。不知道为什么。注意下面的代码第56行。
static lv_img_dsc_t IMG1 =
.header.cf = LV_IMG_CF_RAW,
.header.always_zero = 0,
.header.reserved = 0,
.header.w = 0,
.header.h = 0,
.data_size = 0,
.data = NULL,
;
lv_obj_t * objpg;
//const unsigned char JFIF_INFO[18]=
//0XFF ,0XE0 ,0X00 ,0X10 ,0X4A ,0X46 ,0X49 ,0X46 ,0X00 ,0X01 ,0X01 ,0X01 ,0X00 ,0X60 ,0X00 ,0X60 ,0X00 ,0X00;
lv_obj_t * g_scr;
int avi_file_is_opened=0;
char frameBuffer[20*1024];
int frameSize = 0; //单帧的字节数
int keyFrame = 0; //是否关键帧
int pos; //帧索引
int frames; //帧总数
avi_t * avi;
static void pic_timer(void)
//lv_obj_t* bg = timer->user_data;
if(avi_file_is_opened)
pos++;
if(pos<frames)
// avi->pos 会自动增加,不用下面的函数
//AVI_set_video_position((avi_t *) avi,pos);
frameSize = AVI_read_frame((avi_t *) avi, frameBuffer, &keyFrame);
// 图片的尺寸不能大于分配的空间
if(frameSize > sizeof(frameBuffer)) return;
printf("pos:%d,frameSize: %d",pos, frameSize);
IMG1.data_size = frameSize;
memcpy(frameBuffer+2,JFIF_INFO,sizeof(JFIF_INFO));
IMG1.data = (const uint8_t *)frameBuffer;
lv_img_set_src(g_scr,&IMG1);
else
printf("avi file close.");
avi_file_is_opened = 0;
AVI_close((avi_t *) avi);
void lv_100ask_demo_course_4_1_1(void)
char *ff="./avi/hanfu03.avi";
avi = AVI_open_input_file(ff, 1);
if(avi != NULL)
avi_file_is_opened = 1;
printf("W: %d,H: %d", (int)(avi->width),(int)(avi->height));
//将avi帧byte读取到bitmap中
pos = 10;
frames=AVI_video_frames(avi);
AVI_set_video_position((avi_t *) avi,pos);
frameSize = AVI_read_frame((avi_t *) avi, frameBuffer, &keyFrame);
printf("pos:%d,frameSize: %d",pos, frameSize);
else
printf("open avi fail.");
AVI_close((avi_t *) avi);
return;
IMG1.data_size = frameSize;
//memcpy(frameBuffer+2,JFIF_INFO,sizeof(JFIF_INFO));
IMG1.data = (const uint8_t *)frameBuffer;
g_scr = lv_img_create(lv_scr_act());
lv_img_set_src(g_scr,&IMG1);
lv_timer_t * timer = lv_timer_create(pic_timer, 35, g_scr);
lv_timer_set_repeat_count(timer, frames+1);
4.整个工程源码
【测试代码工程】是在韦东山老师的开源工程上改动的。本例子在工程中的位置:
测试时,在main函数中调用该函数即可:
本文只是记录学习过程,并非教程。
C#AviFile使用(播放AVI文件,兼容性比较差)
最近在做一个视频识别项目,需要用到视频处理,在codeproject上找到了一个关于对Avi的操作库,感觉不错,在这里把一些要点记录下来
http://www.codeproject.com/Articles/7388/A-Simple-C-Wrapper-for-the-AviFile-Library
Avi视频文件的编码有很多,这个库只支持部分Avi文件,有些Avi文件不支持,具体哪些不支持还没搞清楚
AviFile库提供了
1、从视频流中图片的处理
2、视频中音频的处理
3、压缩和解压视频流
1、使用
1、从视频读取图片,还有一些参数可以通过aviStream查看到,可以把当前流信息输出到文件
//Avi文件读取 string filepath = @"D:\test.avi"; AviManager aviManager = new AviManager(filepath, true); VideoStream aviStream = aviManager.GetVideoStream(); //获取和保存音频流到文件 AudioStream audioStream = aviManager.GetWaveStream(); audioStream.ExportStream(@"D:\test.wav"); aviStream.GetFrameOpen(); //获取视频总帧数 int framecount = aviStream.CountFrames; //获取第5帧的图片 Bitmap bmp = aviStream.GetBitmap(5); //视频速度 double rate = aviStream.FrameRate; //直接保存帧图片到文件 //aviStream.ExportBitmap(5, @"D:\frame_05.jpg"); //保存当前流到文件 //aviStream.ExportStream(@"D:\currenttest.avi") aviStream.GetFrameClose(); aviManager.Close();
2、把图片和音频写入视频流(部分参数后面会说到,这里只是简单的演示)
//读取图片文件 string[] files = Directory.GetFiles(@"D:\test\", "*.jpg"); AviManager aviManager = new AviManager(@"D:\newtest.avi", false); //添加音频 String fileName = @"D:\audio.wav"; aviManager.AddAudioStream(fileName, 0); //读取第一张图片,设置每秒3帧 VideoStream aviStream = aviManager.AddVideoStream(true, 3, new Bitmap(files[0])); for (int i = 1; i < files.Length; i++) { aviStream.AddFrame(new Bitmap(files[i])); } aviManager.Close();
3、从视频截取一段保存到AviManager
AviManager aviManager = new AviManager(filepath, true); float startSecond = 0; float stopSecond = 10; //截取0-10s放到newManager AviManager newManager = aviManager.CopyTo(@"D:\newtest_0-10.avi", startSecond, stopSecond);
4、对已压缩的视频进行解压缩,放到另一个VideoStream中
VideoStream newstream; AviManager newManager = aviStream.DecompressToNewFile(@"D:\01.avi", false, out newstream); newstream.GetFrameOpen(); Bitmap bitmap = newstream.GetBitmap(10); newstream.GetFrameClose();
5、向视频流中添加一张图片帧,对于未压缩的视频流,我们可以直接进行添加,但是对于已经压缩过的视频流,添加的图片帧不能重新进行压缩,所以我们需要对其进行解压缩,操作完后在进行压缩
//读取图片文件 string[] files = Directory.GetFiles(@"D:\test\", "*.jpg"); //打开一个已存在的视频 AviManager aviManager = new AviManager(@"D:\test.avi", true); VideoStream avistream = aviManager.GetVideoStream(); VideoStream newstream; //解压视频到newstream中,最后已压缩的形式保存 AviManager newManager = avistream.DecompressToNewFile(@"newtest.avi", true, out newstream); avistream = newManager.GetOpenStream(0); for (int i = 1; i < files.Length; i++) { avistream.AddFrame(new Bitmap(files[i])); } aviManager.Close(); //关闭和保存文件newtest.avi newManager.Close();
6、编辑视频流 EditableVideoStream (可以对视频流的帧,进行cut,delete,paste等早错)
//打开一个已存在的视频 AviManager aviManager = new AviManager(@"D:\test.avi", true); VideoStream avistream = aviManager.GetVideoStream(); EditableVideoStream editableStream = new EditableVideoStream(avistream); int start = 0; int length =10; int position = 10; //Copy IntPtr copiedData = editableStream.Copy(start, length); //Insert editableStream.Paste(copiedData, 0, position, length); //Delete IntPtr deletedData = editableStream.Cut(start, length);
同时也可以把一个 VideoStream 流 Paste 到 EditableVideoStream
editableStream.Paste(stream, 0, position, stream.CountFrames);
7、对视频流进行一些参数设置
Avi.AVISTREAMINFO info = editableStream.StreamInfo; //设置播放速度:每秒 3帧 info.dwRate = 3; editableStream.SetInfo(info);
2、AviFile后台工作
AviManager 管理Avi文件的stream,构造函数传入文件名,当调用Close函数时,关闭所有打开的流和文件,并保存。
可以使用 AddVideoStream 和 AddAudioStream 把视频里和音频流添加到一个新的AviManager中,音频流只支持wav文件
Create a video stream
VideoStream 有两个构造函数
public VideoStream AddVideoStream( bool isCompressed, //display the compression dialog, create a compressed stream int frameRate, //frames per second int frameSize, //size of one frame in bytes int width, int height, PixelFormat format //format of the bitmaps ) { VideoStream stream = new VideoStream(aviFile, isCompressed, frameRate, frameSize, width, height, format); streams.Add(stream); return stream; } public VideoStream AddVideoStream( bool isCompressed, //display the compression dialog, create a compressed stream int frameRate, //frames per second Bitmap firstFrame //get the format from this image and add it to the new stream ) { VideoStream stream = new VideoStream(aviFile, isCompressed, frameRate, firstFrame); streams.Add(stream); return stream; }
VideoStream使用格式化的数据创建新的流,调用 AVIFileCreateStream,如果 isCompressed参数为true,则调用 AVIMakeCompressedStream
public VideoStream(int aviFile, bool writeCompressed, int frameRate, ...) { //store format information //... //create the stream CreateStream(); } private void CreateStream() { //fill stream information Avi.AVISTREAMINFO strhdr = new Avi.AVISTREAMINFO(); strhdr.fccType = Avi.mmioStringToFOURCC("vids", 0); strhdr.fccHandler = Avi.mmioStringToFOURCC("CVID", 0); strhdr.dwScale = 1; strhdr.dwRate = frameRate; strhdr.dwSuggestedBufferSize = frameSize; strhdr.dwQuality = -1; //default strhdr.rcFrame.bottom = (uint)height; strhdr.rcFrame.right = (uint)width; strhdr.szName = new UInt16[64]; //create the stream int result = Avi.AVIFileCreateStream(aviFile, out aviStream, ref strhdr); if(writeCompressed) { //create a compressed stream from CreateCompressedStream(); } } private void CreateCompressedStream() { Avi.AVICOMPRESSOPTIONS_CLASS options = new Avi.AVICOMPRESSOPTIONS_CLASS(); options.fccType = (uint)Avi.streamtypeVIDEO; options.lpParms = IntPtr.Zero; options.lpFormat = IntPtr.Zero; //display the compression options dialog Avi.AVISaveOptions(IntPtr.Zero, Avi.ICMF_CHOOSE_KEYFRAME | Avi.ICMF_CHOOSE_DATARATE, 1, ref aviStream, ref options); //get a compressed stream Avi.AVICOMPRESSOPTIONS structOptions = options.ToStruct(); int result = Avi.AVIMakeCompressedStream(out compressedStream, aviStream, ref structOptions, 0); //format the compressed stream SetFormat(compressedStream); }
其中使用 AVICOMPRESSOPTIONS_CLASS 类代替
AVICOMPRESSOPTIONS 结构体,使用类代替结构体在使用指针的时候更加容易,如果你看不懂,你可能没在.Net使用过
AVISaveOptions
或 AVISaveV
,下面看看 AVISaveOptions 的声明
BOOL AVISaveOptions( HWND hwnd, UINT uiFlags, int nStreams, PAVISTREAM * ppavi, LPAVICOMPRESSOPTIONS * plpOptions);
LPAVICOMPRESSOPTIONS
是一个指向 AVICOMPRESSOPTIONS 结构体指针的指针(指向指针的指针)
在C#中,结构体是值传递的,如果使用ref来传递结构体,则传递的是指向结构体的指针
而类使用的是引用,实际传递的是指针,地址,所以使用ref传递类时,实际传递的是指向类指针的指针(指向指针的指针),
所以这里使用类代替结构体,下面是在C#中声明 AVISaveOptions 和
AVICOMPRESSOPTIONS
[DllImport("avifil32.dll")] public static extern bool AVISaveOptions( IntPtr hwnd, UInt32 uiFlags, Int32 nStreams, ref IntPtr ppavi, ref AVICOMPRESSOPTIONS_CLASS plpOptions ); [StructLayout(LayoutKind.Sequential, Pack=1)] public struct AVICOMPRESSOPTIONS { public UInt32 fccType; public UInt32 fccHandler; public UInt32 dwKeyFrameEvery; public UInt32 dwQuality; public UInt32 dwBytesPerSecond; public UInt32 dwFlags; public IntPtr lpFormat; public UInt32 cbFormat; public IntPtr lpParms; public UInt32 cbParms; public UInt32 dwInterleaveEvery; } [StructLayout(LayoutKind.Sequential, Pack=1)] public class AVICOMPRESSOPTIONS_CLASS { public UInt32 fccType; public UInt32 fccHandler; public UInt32 dwKeyFrameEvery; public UInt32 dwQuality; public UInt32 dwBytesPerSecond; public UInt32 dwFlags; public IntPtr lpFormat; public UInt32 cbFormat; public IntPtr lpParms; public UInt32 cbParms; public UInt32 dwInterleaveEvery; public AVICOMPRESSOPTIONS ToStruct() { AVICOMPRESSOPTIONS returnVar = new AVICOMPRESSOPTIONS(); returnVar.fccType = this.fccType; returnVar.fccHandler = this.fccHandler; returnVar.dwKeyFrameEvery = this.dwKeyFrameEvery; returnVar.dwQuality = this.dwQuality; returnVar.dwBytesPerSecond = this.dwBytesPerSecond; returnVar.dwFlags = this.dwFlags; returnVar.lpFormat = this.lpFormat; returnVar.cbFormat = this.cbFormat; returnVar.lpParms = this.lpParms; returnVar.cbParms = this.cbParms; returnVar.dwInterleaveEvery = this.dwInterleaveEvery; return returnVar; } }
在这个工作区,可以调用 AVISaveOptions ,来设置Avi文件的一些参数
通过AddFrame函数可以用图片填充视频流
public void AddFrame(Bitmap bmp) { bmp.RotateFlip(RotateFlipType.RotateNoneFlipY); if (countFrames == 0) { // the format of the first frame defines the format of the stream CopyPalette(bmp.Palette); SetFormat(writeCompressed ? compressedStream : aviStream, countFrames); } //lock the memory block BitmapData bmpDat = bmp.LockBits( new Rectangle(0,0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat); //add the bitmap to the (un-)compressed stream int result = Avi.AVIStreamWrite( writeCompressed ? compressedStream : aviStream, countFrames, 1, bmpDat.Scan0, (Int32)(bmpDat.Stride * bmpDat.Height), 0, 0, 0); //unlock the memory block bmp.UnlockBits(bmpDat); //count the frames, so that we don‘t have to call AVIStreamLength for every new frame countFrames++; }
Add frames to an existing stream
public VideoStream(int aviFile, IntPtr aviStream) { this.aviFile = aviFile; this.aviStream = aviStream; //read the stream‘s format Avi.BITMAPINFOHEADER bih = new Avi.BITMAPINFOHEADER(); int size = Marshal.SizeOf(bih); Avi.AVIStreamReadFormat(aviStream, 0, ref bih, ref size); Avi.AVISTREAMINFO streamInfo = GetStreamInfo(aviStream); //store the important format values this.frameRate = streamInfo.dwRate / streamInfo.dwScale; this.width = (int)streamInfo.rcFrame.right; this.height = (int)streamInfo.rcFrame.bottom; this.frameSize = bih.biSizeImage; this.countBitsPerPixel = bih.biBitCount; //get the count of frames that are already there int firstFrame = Avi.AVIStreamStart(aviStream.ToInt32()); countFrames = firstFrame + Avi.AVIStreamLength(aviStream.ToInt32()); }
如果视频流是未压缩的,可以直接调用AddFrame,否则,需要对其进行解压缩,并重新压缩到一个新的流
public AviManager DecompressToNewFile(String fileName, bool recompress) { //create a new AVI file AviManager newFile = new AviManager(fileName, false); //create a video stream in the new file this.GetFrameOpen(); Bitmap frame = GetBitmap(0); VideoStream newStream = newFile.AddVideoStream(recompress, frameRate, frame); //decompress each frame and add it to the new stream for(int n=1; n<countFrames; n++) { frame = GetBitmap(n); newStream.AddFrame(frame); } this.GetFrameClose(); return newFile; }
DecompressToNewFile 创建一个可编辑的拷贝到一个新的文件流,可以添加frames到该流
Separate a stream
有时,我们可能只想要视频的声音,或是只要没有声音的视频,我们没有必要重新创建视频,添加每一帧到视频流中,可以通过使用 AviSaveV 把当前流到处到文件,AVISaveV只是所有类型的流,只是压缩参数不一样而已
public override void ExportStream(String fileName) { Avi.AVICOMPRESSOPTIONS_CLASS opts = new Avi.AVICOMPRESSOPTIONS_CLASS(); //for video streams opts.fccType = (UInt32)Avi.mmioStringToFOURCC("vids", 0); opts.fccHandler = (UInt32)Avi.mmioStringToFOURCC("CVID", 0); //for audio streams //opts.fccType = (UInt32)Avi.mmioStringToFOURCC("auds", 0); //opts.fccHandler = (UInt32)Avi.mmioStringToFOURCC("CAUD", 0); //export the stream Avi.AVISaveV(fileName, 0, 0, 1, ref aviStream, ref opts); }
Import sound from a Wave file
现在,我们可以通过Bitmap来生成视频,也可以从视频中导出声音,那么当我们导入wav文件的时候,底层是如何工作的呢,我们还是可以用 AVISaveV 这个方法,来组合视频和音频成一个文件,但这里我们有更简单的方法,打开音频文件作为Avi文件,然后拷贝到另一个流中
public void AddAudioStream(String waveFileName) { //open the wave file AviManager audioManager = new AviManager(waveFileName, true); //get the wave sound as an audio stream... AudioStream newStream = audioManager.GetWaveStream(); //...and add it to the file AddAudioStream(newStream); audioManager.Close(); } public void AddAudioStream(AudioStream newStream) { Avi.AVISTREAMINFO streamInfo = new Avi.AVISTREAMINFO(); Avi.PCMWAVEFORMAT streamFormat = new Avi.PCMWAVEFORMAT(); int streamLength = 0; //read header info, format and length, //and get a pointer to the wave data IntPtr waveData = newStream.GetStreamData( ref streamInfo, ref streamFormat, ref streamLength); //create new stream IntPtr aviStream; Avi.AVIFileCreateStream(aviFile, out aviStream, ref streamInfo); //add format new stream Avi.AVIStreamSetFormat( aviStream, 0, ref streamFormat, Marshal.SizeOf(streamFormat)); //copy the raw wave data into the new stream Avi.AVIStreamWrite( aviStream, 0, streamLength, waveData, streamLength, Avi.AVIIF_KEYFRAME, 0, 0); Avi.AVIStreamRelease(aviStream); }
截取视频流
public AviManager CopyTo(String newFileName, int startAtSecond, int stopAtSecond) { AviManager newFile = new AviManager(newFileName, false); try { //copy video stream VideoStream videoStream = GetVideoStream(); int startFrameIndex = videoStream.FrameRate * startAtSecond; int stopFrameIndex = videoStream.FrameRate * stopAtSecond; videoStream.GetFrameOpen(); Bitmap bmp = videoStream.GetBitmap(startFrameIndex); VideoStream newStream = newFile.AddVideoStream(false, videoStream.FrameRate, bmp); for (int n = startFrameIndex + 1; n <= stopFrameIndex; n++) { bmp = videoStream.GetBitmap(n); newStream.AddFrame(bmp); } videoStream.GetFrameClose(); //copy audio stream AudioStream waveStream = GetWaveStream(); Avi.AVISTREAMINFO streamInfo = new Avi.AVISTREAMINFO(); Avi.PCMWAVEFORMAT streamFormat = new Avi.PCMWAVEFORMAT(); int streamLength = 0; IntPtr ptrRawData = waveStream.GetStreamData(ref streamInfo, ref streamFormat, ref streamLength); int startByteIndex = waveStream.CountSamplesPerSecond * startAtSecond * waveStream.CountBitsPerSample / 8; int stopByteIndex = waveStream.CountSamplesPerSecond * stopAtSecond * waveStream.CountBitsPerSample / 8; ptrRawData = new IntPtr(ptrRawData.ToInt32() + startByteIndex); byte[] rawData = new byte[stopByteIndex - startByteIndex]; Marshal.Copy(ptrRawData, rawData, 0, rawData.Length); streamInfo.dwLength = rawData.Length; streamInfo.dwStart = 0; IntPtr unmanagedRawData = Marshal.AllocHGlobal(rawData.Length); Marshal.Copy(rawData, 0, unmanagedRawData, rawData.Length); newFile.AddAudioStream(unmanagedRawData, streamInfo, streamFormat, rawData.Length); } catch (Exception ex) { newFile.Close(); throw ex; } return newFile; }
以上是关于LVGL笔记-使用《avilib》库播放avi视频(lvgl在windows模拟运行)的主要内容,如果未能解决你的问题,请参考以下文章
.avi 文件视频无法在 MPMoviePlayerController ios 播放器中播放
ARM AVI 视频播放 基于 2440 freambuffer 绘制