string

string
posts - 27, comments - 177, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理
上面一节是从使用者的角度看Protocol ,这一节从Protocol提供者的角度来看Protocol。
作为提供者,我们要了解三个问题:
1. Protocol是什么?
2. Protocol安装到什么地方。
3. 怎么安装Protocol。
前两个问题上一节已经讲述,那么我们现在看第三个问题。
BootService 提供了InstallProtocolInterface帮我们把Protocol安装到Controller Handle上。
EFI_STATUS
InstallProtocolInterface (
    IN OUT EFI_HANDLE *Handle,                 // Protocol将安装到这儿
    IN EFI_GUID *Protocol,                         // GUID
    IN EFI_INTERFACE_TYPE InterfaceType,  // 通常为EFI_NATIVE_INTERFACE
    IN VOID *Interface                              // Protocol实例
);

我们希望我们的Protocol能常驻内存以提供服务。 我们知道Application 是不能常驻内存的,只有Driver可以常驻内存。那么我们就要用driver的形式来提供服务。但我们的服务与通常的driver不同,driver需要特定硬件支持,而我们的服务不需要。这就使得我们的driver变的简单,driver需要安装到特定的controller上,我们的服务安装到任何controller都可以。
那么还有一个不是问题的问题,何时安装Protocol。作为驱动的Protocol有特定的规范,将在下一节讲述。通常一个driver被load到内存后会执行InstallProtocolInterface 将 Driver Binding Protocol 和Component Name Protocol安装到自身Handle(或者其它handle)上。 相比driver我们的服务要简单许多,我们在Image初始化的时候将Protocol安装到自身Handle即可。

通过上面的分析,我们决定使用driver来提供服务,Image初始化的时候安装Protocol。具体到我们将要提供的视频解码服务,我们还要分析一下我们需要提供哪些函数。我们需要OpenVideo来打开视频,QueryFrame取得一帧,CloseFrame关闭视频,还需要一个函数来获取视频信息。下面让我们一步步来产生这个Protocol吧。
头文件ffdecoder.h
首先我们要定义我们Protocol的GUID
#define EFI_FFDECODER_PROTOCOL_GUID \
{ \
    0xce345171, 0xabcd, 0x11d2, {0x8e, 0x4f, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \
}

//
///// Protocol GUID name defined in EFI1.1.
// 
#define FFDECODER_PROTOCOL  EFI_FFDECODER_PROTOCOL_GUID
然后定义Protocol里服务的函数原型
/**
  Open the video 
  @param  This       Indicates a pointer to the calling context.
  @param  FileName   File name of the video under current dir. 
  @retval EFI_SUCCESS           The video is opened successfully. 
  @retval EFI_NOT_FOUND         There is no such file.
**/
typedef 
EFI_STATUS
(EFIAPI* EFI_OPEN_VIDEO)(
        IN EFI_FFDECODER_PROTOCOL* This,
        IN CHAR16* FileName
        );

/**
  Close the video 
  @param  This       Indicates a pointer to the calling context.
  @retval EFI_SUCCESS           The video is closed successfully. 
**/
typedef 
EFI_STATUS
(EFIAPI* EFI_CLOSE_VIDEO)(
        IN EFI_FFDECODER_PROTOCOL* This
        );

/**
  Query a frame from the video. 
  @param  This       Indicates a pointer to the calling context.
  @param  pFrame     Points to the current Frame.  
  @retval EFI_SUCCESS          The video is opened successfully. 
  @retval EFI_NO_MEDIA         There is no opened video. 
  @retval EFI_END_OF_MEDIA     There is no more Frame. 
**/
typedef 
EFI_STATUS
(EFIAPI* EFI_QUARY_FRAME)(
        IN  EFI_FFDECODER_PROTOCOL  *This,
        OUT AVFrame                 **pFrame
        );

/**
  Query the Width and height of a frame. 
  @param  This       Indicates a pointer to the calling context.
  @param  Width      Width(in pixels) of a frame.
  @param  Height     Height(in pixels) of a frame.
  @retval EFI_SUCCESS          Return the Size successfully. 
  @retval EFI_NO_MEDIA         There is no opened video. 
**/
typedef 
EFI_STATUS
(EFIAPI* EFI_QUARY_FRAME_SIZE)(
        IN  EFI_FFDECODER_PROTOCOL  *This,
        OUT UINT32                  *Width,
        OUT UINT32                  *Height 
        );
下面就要定义Protocol本身了, 按照EDK2的规则,我们的Protocol取名为EFI_FFDECODER_PROTOCOL . 
struct _EFI_FFDECODER_PROTOCOL{
 UINT64          Revision;
 EFI_OPEN_VIDEO  OpenVideo;
 EFI_CLOSE_VIDEO CloseVideo;
 EFI_QUARY_FRAME QueryFrame;
 EFI_QUARY_FRAME_SIZE QueryFrameSize;
};
typedef struct _EFI_FFDECODER_PROTOCOL EFI_FFDECODER_PROTOCOL;
typedef EFI_FFDECODER_PROTOCOL  EFI_FFDECODER;

头文件ffdecoder.h最后要提供给用户使用。下面进入EFI_FFDECODER_PROTOCOL 的实现部分ffdecoder.c
ffdecoder.c中我们要提供EFI_FFDECODER_PROTOCOL 的四个成员函数,以及一个Image初始化函数, 一个Private数据结构,用于存放EFI_FFDECODER_PROTOCOL 的上下文。
首先看Private数据结构,如果你对ffmpeg比较熟悉,那么你很快就会明白我们需要在Private中存放什么
#define FFDECODER_PRIVATE_DATA_SIGNATURE  SIGNATURE_32 ('V', 'I', 'D', 'O')
/**
  @member     Signature         The signature of the Protocol Context
  @member     FFDecoder        The EFI_FFDECODER_PROTOCOL   
  @member     pFormatCtx       Video Format Context
  @member    videoStream       The index of Video Stream in all the streams.
  @member     pCodecCtx        Codec context
  @member     pFrame             The yuv Frame
  @member     pFrameRGBA      The RGBA Frame
  @member     buffer               internal used
  @member     img_convert_ctx  The Context of converting from yuv to rgba.
 **/
typedef struct {
    UINTN                 Signature;
    EFI_FFDECODER_PROTOCOL  FFDecoder; 
    AVFormatContext    *pFormatCtx;
    int                        videoStream;
    AVCodecContext     *pCodecCtx;
    AVFrame               *pFrame; 
    AVFrame               *pFrameRGBA;
    uint8_t                  *buffer;
    struct SwsContext  *img_convert_ctx ;

} FFDECODER_PRIVATE_DATA;

static FFDECODER_PRIVATE_DATA gFFDecoderPrivate;

我们还定义了一个变量gFFDecoderPrivate, 同时我们就得到了 EFI_FFDECODER_PROTOCOL 的一个实例。下面我们要定义4个函数,对应EFI_FFDECODER_PROTOCOL 的四个成员函数。
EFI_STATUS
OpenVideo(
        IN EFI_FFDECODER_PROTOCOL* This,
        IN CHAR16* FileName
        )
{
    FFDECODER_PRIVATE_DATA* Private;
    Private = FFDECODER_PRIVATE_DATA_FROM_THIS(This);
    ...
}

EFI_STATUS
CloseVideo(
        IN EFI_FFDECODER_PROTOCOL* This
        )
{
    FFDECODER_PRIVATE_DATA* Private;
    Private = FFDECODER_PRIVATE_DATA_FROM_THIS(This);
    ...
}

EFI_STATUS
QueryFrame(
        IN  EFI_FFDECODER_PROTOCOL  *This,
        OUT AVFrame                 **ppFrame
        )
{
    FFDECODER_PRIVATE_DATA* Private;
    Private = FFDECODER_PRIVATE_DATA_FROM_THIS(This);
    ...
}

EFI_STATUS
QueryFrameSize(
        IN  EFI_FFDECODER_PROTOCOL  *This,
        OUT UINT32                  *Width,
        OUT UINT32                  *Height
        )
{
    FFDECODER_PRIVATE_DATA* Private;
    Private = FFDECODER_PRIVATE_DATA_FROM_THIS(This);
    ...
}
宏FFDECODER_PRIVATE_DATA_FROM_THIS(This) 用于根据Protocol指针取得Protocol的上下文, 定义如下:
#define FFDECODER_PRIVATE_DATA_FROM_THIS(a) CR (a, FFDECODER_PRIVATE_DATA, FFDecoder, FFDECODER_PRIVATE_DATA_SIGNATURE)
本篇重点讲述Protocol,所以这个四个函数细节不再详述,感兴趣的话可以看附件中的源码。

下面我们看最重要的部分,Image的初始化函数,回忆一下,我们会记得,在初始化函数中我们将要安装Protocol。
EFI_STATUS
EFIAPI
InitFFdecoder (
        IN EFI_HANDLE        ImageHandle,
        IN EFI_SYSTEM_TABLE  *SystemTable
        )
{
    EFI_STATUS Status;
    FFDECODER_PRIVATE_DATA* Private = &gFFDecoderPrivate;
    //初始化ShellProtocol
    ShellLibConstructorWorker2(NULL,NULL,NULL,NULL);
    //初始化StdLib
    (void) DriverInitMain(0, NULL);

    //设置Protocol上下文的Signature.
    //初始化Protocol,设置Protocol成员函数。
    Private-> Signature= FFDECODER_PRIVATE_DATA_SIGNATURE  ; 
    Private->FFDecoder.OpenVideo = OpenVideo; 
    Private->FFDecoder.CloseVideo = CloseVideo; 
    Private->FFDecoder.QueryFrame= QueryFrame;  
    Private->FFDecoder.QueryFrameSize= QueryFrameSize; 

    //将
EFI_FFDECODER_PROTOCOL的实例 
&(Private->FFDecoder )安装到自身Handle中。
    Status = gBS->InstallProtocolInterface (
            &ImageHandle,
            &gEfiFFDecoderProtocolGUID ,
            EFI_NATIVE_INTERFACE,
            &Private->FFDecoder
            );

}

在.inf文件中我们把InitFFdecoder 设为Entrypoint,当Image被load到内存后InitFFdecoder 会自动执行。
[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = ffdecoder
  FILE_GUID                        = 33a97c46-7491-4dfd-b442-74798713ce5f
  #ENTRY_POINT                 = ShellCEntryLib
  #MODULE_TYPE                = UEFI_APPLICATION 
  VERSION_STRING              = 0.1
  MODULE_TYPE                  = UEFI_DRIVER 
  ENTRY_POINT                   = InitFFdecoder 

#
#  VALID_ARCHITECTURES           = IA32 X64 IPF
#

[Sources]
  ffdecoder.c 
  math.c
  InitShell.c
[Packages]
  StdLib/StdLib.dec
  MdePkg/MdePkg.dec
  MdeModulePkg/MdeModulePkg.dec
  ShellPkg/ShellPkg.dec
  ffmpeg/ffmpeg.dec
  StdLibPrivateInternalFiles/DoNotUse.dec

[LibraryClasses]
  UefiDriverEntryPoint
  LibC
  LibStdio
  LibMath
  LibString
  BsdSocketLib
  EfiSocketLib
  UseSocketDxe
  DevShell
  zlib
  libavcodec
  libavutil
  libswscale
  libavformat

  LibUefi
  LibNetUtil
啊哈,编译得到ffdecoder.efi. 使用命令 load ffdecoder.efi 加载之后就可以像使用其它Protocol一样使用EFI_FFDECODER_PROTOCOL了。
下面是一个简单的例子 fplayer.c 用于播放视频
#ifdef __cplusplus
extern "C"{
#endif
#include 
<Uefi.h> 
#include 
<Base.h> 
#include 
<Library/DebugLib.h>
#include 
<Library/PrintLib.h>
#include 
<Protocol/GraphicsOutput.h>
EFI_GRAPHICS_OUTPUT_PROTOCOL         
*GraphicsOutput;
#ifdef __cplusplus
}
#endif
#include 
"ffdecoder.h"

EFI_STATUS LocateGraphicsOutput()
{
    EFI_STATUS Status 
= gBS->LocateProtocol(
            
&gEfiGraphicsOutputProtocolGuid,
            NULL,
            (VOID 
**)&GraphicsOutput);
    
if (EFI_ERROR(Status)) {
        Print(L
"LocateProtocol %r\n", Status);
    }
    
return Status;
}
void ShowFrame(AVFrame *pFrame, int width, int height, int iFrame)
{
    
if(GraphicsOutput)
        GraphicsOutput
->Blt(
                GraphicsOutput,
                (EFI_GRAPHICS_OUTPUT_BLT_PIXEL 
*)pFrame->data[0],
                EfiBltBufferToVideo,
                
0,0,
                
0,0,
                width, height,
                
0
                );
}

int main(int argc, char *argv[])
{
    AVFrame         
*pFrame; 
    EFI_GUID gEfiFFDecoderProtocolGUID 
= EFI_FFDECODER_PROTOCOL_GUID ;
    EFI_FFDECODER_PROTOCOL 
*FFDecoder; 
    UINT32 Width, Height;
    CHAR16
* FileName = 0;

    
// Locate the Protocol
    EFI_STATUS Status = gBS->LocateProtocol(
            
&gEfiFFDecoderProtocolGUID ,
            NULL,
            (VOID 
**)&FFDecoder );
    
if (EFI_ERROR(Status)) {
        Print(L
"LocateProtocol %r\n", Status);
        
return Status;
    }
    LocateGraphicsOutput();

    
// Open Video
    Status =  gBS->AllocatePool(EfiLoaderData, AsciiStrLen(argv[1]) *2 + 2, (VOID**)&FileName );
    AsciiStrToUnicodeStr(argv[
1], FileName);
    Status 
= FFDecoder -> OpenVideo( FFDecoder, FileName);
    (
void) gBS->FreePool ( FileName);
    
if (EFI_ERROR(Status)) {
        Print(L
"Open %r\n", Status);
        
return Status;
    }
    
// Query Frame Size(Width height)
    Status = FFDecoder-> QueryFrameSize(FFDecoder, &Width, &Height);
    
// Query Frame 
    while!EFI_ERROR( FFDecoder-> QueryFrame(FFDecoder, &pFrame)))
    {
        ShowFrame(pFrame, Width, Height, 
0);
    }
    
// Close Video
    Status = FFDecoder -> CloseVideo(FFDecoder );
    
return 0;
}
在shell里面执行
f0:/>ffplayer test.avi                                     
就可以播放视频了
 下载EFI_FFDECODER_PROTOCOL 附近包含了32-bit的ffdecoder.efi 以及ffdecoder.c fplayer.c

Feedback

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2012-03-27 03:58 by djx_zh
下载EFI_FFDECODER_PROTOCOL
http://codelibrarydzh.googlecode.com/files/EFI_FFDECODER_PROTOCOL_1.0.zip

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2012-07-14 22:56 by djx_zh
@djx_zh
https://codelibrarydzh.googlecode.com/files/EFI_FFDECODER_PROTOCOL_1.1.zip
修正一个bug: 显示的时候应采用BGRA格式。
增加了timer用于控制播放。按'q'退出播放

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2012-08-23 15:44 by Amin
ffdecoder.efi 的driver存放在U盤中,要在shell下運行Load,老是無法成功
指令 fs0:\ Load ffdecoder.efi
都只會顯示Load : fs0:\ffdecoder.efi is not a image

請問是否哪邊有錯誤?

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2012-08-23 17:02 by Amin
請問ffdecoder.efi 與ffplayer.efi是否有X64版本可以提供?

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2012-08-23 17:50 by Amin
我無法找出以下的LibraryClasses
zlib
libavcodec
libavutil
libswscale
libavformat
請問可從哪里獲取?

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务[未登录]  回复  更多评论   

2012-08-24 08:54 by djx_zh
@Amin
X64的仍有些技术问题没有解决。解码速度已经足够了,但是GraphOutputProtocol的显示速度太慢。 你可以留个email给我,我发给你。

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2012-08-24 09:28 by Amin
我的mail : white_5168@hotmail.com
請問我在build x64時有很多的LIB加得很辛苦,請問有什麼方法可以include一個文檔就可以build成功
我的環境只有用X64的,所以32位元的ffdecoder與ffplayer都無法執行

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2012-08-24 10:08 by djx_zh
@Amin
你是说编译ffdecoder很辛苦吗? ffdecoder的编译比较麻烦,需要修改EDK2的源码和ffmpeg源码。除非你很有兴趣去学习EDK2, 否则没必要去编译ffdecoder.
ffplayer编译非常简单。
X64的.efi文件已经发给你了。

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2012-09-19 10:38 by Amin
不是编译ffdecoder很辛苦,而是你所附上的附檔,里面真的缺了很多文档,光是要找出来与引用就需要很费时间,不知是否能提供的完整,如libavcodec.inf,libavutil.inf,libswscale.inf,libavformat.inf,ffmpeg/ffmpeg.dec

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务[未登录]  回复  更多评论   

2013-05-06 10:52 by 张扬
博主,你好,请问有什么方法可以直接调用shellpkg这个包里面的函数呢,比如读写文件,可以用simple_file_protocol,但是shellpkg里面的方法如何调用?
还有关于视屏播放的源码能否分享?(如果不涉及您的版权的前提下)
多谢!

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务[未登录]  回复  更多评论   

2013-05-06 10:53 by 张扬
我的Email:1102878561@qq.com

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2013-12-19 09:18 by lingming
有X64的吗?麻烦给我一份,saillimited@hotmail.com,谢谢。

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2014-07-17 17:41 by 李波
您好,非常希望能继续拜读您的大作,请问一下哪里能买到你的书???
另外,能不能把这份源码发给我,我是新手,有些地方不是很清楚。我的邮箱 lumia361@hotmail.com ,谢谢。

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2014-11-12 21:41 by Xexerse
你好,能把代码发给我一份吗,314018436@qq.com,那个链接不能用了。还有你的书出版了吗?叫什么名字啊,现在在学UEFI,想买来参考下

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2015-05-25 15:43 by 周彬彬
您好,能给我发一份完整的整个代码吗?1146625664@qq.com,谢谢

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务[未登录]  回复  更多评论   

2015-06-02 22:23 by djx_zh
ffmpeg的源代码可到此处下载
https://github.com/zhenghuadai/uefi-programming/releases/download/1.0/ffmpeg-0.10.2-for-UEFI.tgz

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2015-12-29 11:06 by Zafir
楼主,可否给我也发一份64位的。511599737@qq.com万分感谢

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2016-02-17 17:55 by pingl
楼主,可否发一份64位的给我,谢谢。568900705@qq.com

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2016-02-17 17:57 by pingl
@Amin

楼主,可否发一份64位的给我,谢谢。568900705@qq.com

# re: UEFI 实战(4) protocol 之利用Protocol提供视频解码服务  回复  更多评论   

2016-02-25 03:11 by djxzh
@pingl

http://pan.baidu.com/s/1pKwBzy7

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理