您的位置:首页 > 编程语言

0基础学习音视频编程技术(四) 使用FFMPEG解码视频之保存成图片

2018-09-20 16:46 633 查看

原文地址:http://blog.yundiantech.com/?log=blog&id=8

前面我们已经知道怎么使用FFMPEG了。

现在,开始着手使用FFMPEG吧:

 

首先来个简单的例子,使用FFMPEG打开视频文件,并解码保存成一张张的图片。

具体的步骤如下所示:

 

1.首先需要先初始化一下,使用如下函数:

1

av_register_all(); 
//初始化FFMPEG  调用了这个才能正常适用编码器和解码器

 

 

使用这个函数完成编码器和解码器的初始化,只有初始化了编码器和解码器才能正常使用,否则会在打开编解码器的时候失败。

 

2.接着需要分配一个AVFormatContext,FFMPEG所有的操作都要通过这个AVFormatContext来进行

1

AVFormatContext *pFormatCtx = avformat_alloc_context();

 

3.接着调用打开视频文件

这里文件名先不要使用中文,否则会打开失败,后期再讲解如何处理中文。

1

2

char
 
*file_path = 
"E:in.mp4"
;

avformat_open_input(&pFormatCtx, file_path, NULL, NULL);

 

4.文件打开成功后就是查找文件中的视频流了:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

    
///循环查找视频中包含的流信息,直到找到视频类型的流    

    
///便将其记录下来 保存到videoStream变量中

    
///这里我们现在只处理视频流  音频流先不管他

    
for
 
(i = 0; i < pFormatCtx->nb_streams; i++) {

        
if
 
(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {

            
videoStream = i;

        
}

    
}

 

    
///如果videoStream为-1 说明没有找到视频流

    
if
 
(videoStream == -1) {

        
printf
("Didn't find a video stream.

");

        
return
 
-1;

    
}

 

5.现在根据视频流  打开一个解码器来解码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

    
///查找解码器    

    
pCodecCtx = pFormatCtx->streams[videoStream]->codec;

    
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

 

    
if
 
(pCodec == NULL) {

        
printf
("Codec not found.

");

        
return
 
-1;

    
}

 

    
///打开解码器

    
if
 
(avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {

        
printf
("Could not open codec.

");

        
return
 
-1;

    
}

 

可以看出  我们可以直接根据查找到的视频流信息获取到解码器。

而且我们并不知道他实际用的是什么编码器。

这就是为什么一开始我们使用FFMPEG来操作,因为很多东西我们可以不关系。

 

 

6.现在开始读取视频了:

1

2

3

4

5

6

7

8

    
int
 
y_size = pCodecCtx->width * pCodecCtx->height;

    
AVPacket *packet = (AVPacket *) 
malloc
(
sizeof
(AVPacket)); 
//分配一个packet

    
av_new_packet(packet, y_size); 
//分配packet的数据

 

    
if
 
(av_read_frame(pFormatCtx, packet) < 0)

    
{

        
break
//这里认为视频读取完了

    
}

可以看出 av_read_frame读取的是一帧视频,并存入一个AVPacket的结构中。

 

 

7.前面我们说过 视频里面的数据是经过编码压缩的,因此这里我们需要将其解码:

1

2

3

4

5

6

7

8

9

10

    
if
 
(packet->stream_index == videoStream) 

    
{        

        
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);

 

        
if
 
(ret < 0) {

            
printf
("decode error.

");

            
return
 
-1;

        
}

    
}

 

8.基本上所有解码器解码之后得到的图像数据都是YUV420的格式,而这里我们需要将其保存成图片文件,因此需要将得到的YUV420数据转换成RGB格式,转换格式也是直接使用FFMPEG来完成:

1

2

3

4

5

6

    
if
 
(got_picture) {        

        
sws_scale(img_convert_ctx,

                
(uint8_t 
const
 
const
 
*) pFrame->data,

                
pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,

                
pFrameRGB->linesize);

    
}

 

至于YUV420和RGB图像格式的具体内容,这里不用去了解。这里只需要知道有这么个东西就行了,对我们使用FFMPEG转换没有影响。

 

 

9.得到RGB数据之后就是直接写入文件了:

1

    
SaveFrame(pFrameRGB,     pCodecCtx->width,pCodecCtx->height,index++); 
//保存图片     if (index > 50) return 0; //这里我们就保存50张图片

 

 

至此读取视频解码保存成图片就写好了:

完整的代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

extern
 
"C"
{

    
#include 
"libavcodec/avcodec.h"

    
#include 
"libavformat/avformat.h"

    
#include 
"libavutil/pixfmt.h"

    
#include 
"libswscale/swscale.h"

}

 

#include <stdio.h>

 

///现在我们需要做的是让SaveFrame函数能把RGB信息定稿到一个PPM格式的文件中。

///我们将生成一个简单的PPM格式文件,请相信,它是可以工作的。

void
 
SaveFrame(AVFrame *pFrame, 
int
 
width, 
int
 
height,
int
 
index)

{

 

  
FILE
 
*pFile;

  
char
 
szFilename[32];

  
int
  
y;

 

  
// Open file

  
sprintf
(szFilename, 
"frame%d.ppm"
, index);

  
pFile=
fopen
(szFilename, 
"wb"
);

 

  
if
(pFile==NULL)

    
return
;

 

  
// Write header

  
fprintf
(pFile, "P6

%d %d

255

", width, height);

 

  
// Write pixel data

  
for
(y=0; y<height; y++)

  
{

    
fwrite
(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);

  
}

 

  
// Close file

  
fclose
(pFile);

 

}

 

 

int
 
main(
int
 
argc, 
char
 
*argv[])

{

    
char
 
*file_path = 
"E:in.mp4"
;

 

    
AVFormatContext *pFormatCtx;

    
AVCodecContext *pCodecCtx;

    
AVCodec *pCodec;

    
AVFrame *pFrame, *pFrameRGB;

    
AVPacket *packet;

    
uint8_t *out_buffer;

 

    
static
 
struct
 
SwsContext *img_convert_ctx;

 

    
int
 
videoStream, i, numBytes;

    
int
 
ret, got_picture;

 

    
av_register_all(); 
//初始化FFMPEG  调用了这个才能正常适用编码器和解码器

 

    
//Allocate an AVFormatContext.

    
pFormatCtx = avformat_alloc_context();

 

    
if
 
(avformat_open_input(&pFormatCtx, file_path, NULL, NULL) != 0) {

        
printf
("can't open the file. 

");

        
return
 
-1;

    
}

 

    
if
 
(avformat_find_stream_info(pFormatCtx, NULL) < 0) {

        
printf
("Could't find stream infomation.

");

        
return
 
-1;

    
}

 

    
videoStream = -1;

 

    
///循环查找视频中包含的流信息,直到找到视频类型的流

    
///便将其记录下来 保存到videoStream变量中

    
///这里我们现在只处理视频流  音频流先不管他

    
for
 
(i = 0; i < pFormatCtx->nb_streams; i++) {

        
if
 
(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {

            
videoStream = i;

        
}

    
}

 

    
///如果videoStream为-1 说明没有找到视频流

    
if
 
(videoStream == -1) {

        
printf
("Didn't find a video stream.

");

        
return
 
-1;

    
}

 

    
///查找解码器

    
pCodecCtx = pFormatCtx->streams[videoStream]->codec;

    
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

 

    
if
 
(pCodec == NULL) {

        
printf
("Codec not found.

");

        
return
 
-1;

    
}

 

    
///打开解码器

    
if
 
(avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {

        
printf
("Could not open codec.

");

        
return
 
-1;

    
}

 

    
pFrame = av_frame_alloc();

    
pFrameRGB = av_frame_alloc();

 

    
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,

            
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,

            
PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);

 

    
numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height);

 

    
out_buffer = (uint8_t *) av_malloc(numBytes * 
sizeof
(uint8_t));

    
avpicture_fill((AVPicture *) pFrameRGB, out_buffer, PIX_FMT_RGB24,

            
pCodecCtx->width, pCodecCtx->height);

 

    
int
 
y_size = pCodecCtx->width * pCodecCtx->height;

 

    
packet = (AVPacket *) 
malloc
(
sizeof
(AVPacket)); 
//分配一个packet

    
av_new_packet(packet, y_size); 
//分配packet的数据

 

    
av_dump_format(pFormatCtx, 0, file_path, 0); 
//输出视频信息

 

    
int
 
index = 0;

 

    
while
 
(1)

    
{

        
if
 
(av_read_frame(pFormatCtx, packet) < 0)

        
{

            
break
//这里认为视频读取完了

        
}

 

        
if
 
(packet->stream_index == videoStream) {

            
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);

 

            
if
 
(ret < 0) {

                
printf
("decode error.

");

                
return
 
-1;

            
}

 

            
if
 
(got_picture) {

                
sws_scale(img_convert_ctx,

                        
(uint8_t 
const
 
const
 
*) pFrame->data,

                        
pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,

                        
pFrameRGB->linesize);

 

                
SaveFrame(pFrameRGB, pCodecCtx->width,pCodecCtx->height,index++); 
//保存图片

                
if
 
(index > 50) 
return
 
0; 
//这里我们就保存50张图片

            
}

        
}

        
av_free_packet(packet);

    
}

    
av_free(out_buffer);

    
av_free(pFrameRGB);

    
avcodec_close(pCodecCtx);

    
avformat_close_input(&pFormatCtx);

 

    
return
 
0;

}

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐