YUV420P像素数据编码为JPEG图片

  1 /*
  2 FFmpeg环境配置:
  3 配置包含目录,库目录,附加依赖性
  4 添加dll到工程debug文件下
  5 */
  6 
  7 /*
  8 libavcodec        encoding/decoding library
  9 libavfilter        graph-based frame editing library
 10 libavformat        I/O and muxing/demuxing library
 11 libavdevice        special devices muxing/demuxing library
 12 libavutil        common utility library
 13 libswresample    audio resampling, format conversion and mixing
 14 libpostproc        post processing library
 15 libswscale        color conversion and scaling library
 16 */
 17 
 18 
 19 #include <stdio.h>
 20 
 21 extern "C" // 因为FFmpeg是纯C程序
 22 {
 23     // FFmpeg libraries
 24 #include "libavcodec/avcodec.h"
 25 #include "libavformat/avformat.h"
 26 #include "libswscale/swscale.h"
 27 };
 28 
 29 
 30 
 31 //#define STEP1        // 视频文件解码为YUV420数据
 32 #define STEP2        // YUV420P像素数据编码为JPEG图片
 33 
 34 
 35 #ifdef STEP1
 36 
 37 /////////////////////////////////////////////////////////////////////////////////////////
 38 // 视频文件解码为YUV数据
 39 
 40 int main(int argc, char* argv[])
 41 {
 42     AVFormatContext    *pFormatCtx;
 43     AVCodecContext    *pCodecCtx;
 44     AVCodec            *pCodec;
 45     AVFrame    *pFrame, *pFrameYUV;
 46     AVPacket *packet;
 47     struct SwsContext *img_convert_ctx;
 48     uint8_t *out_buffer;
 49 
 50     int    videoindex = -1;
 51     int y_size;
 52     int ret, got_picture;
 53 
 54     char filepath[] = "video1.mkv";
 55     FILE *fp_yuv = fopen("video1.yuv", "wb+");
 56 
 57     av_register_all();
 58     //avformat_network_init();
 59     pFormatCtx = avformat_alloc_context();
 60 
 61     if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0)
 62     {
 63         printf("Couldn't open input stream.\n");
 64         return -1;
 65     }
 66     if (avformat_find_stream_info(pFormatCtx, NULL) <0)
 67     {
 68         printf("Couldn't find stream information.\n");
 69         return -1;
 70     }
 71 
 72     for (int i = 0; i < pFormatCtx->nb_streams; i++)
 73     {
 74         if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
 75         {
 76             videoindex = i;
 77             break;
 78         }
 79     }
 80     if (videoindex == -1)
 81     {
 82         printf("Didn't find a video stream.\n");
 83         return -1;
 84     }
 85 
 86     pCodecCtx = pFormatCtx->streams[videoindex]->codec;
 87 
 88     // 根据编码器的ID查找FFmpeg的解码器
 89     pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
 90     if (pCodec == NULL)
 91     {
 92         printf("Codec not found.\n");
 93         return -1;
 94     }
 95     // 初始化一个视音频编解码器的AVCodecContext
 96     if (avcodec_open2(pCodecCtx, pCodec, NULL)<0)
 97     {
 98         printf("Could not open codec.\n");
 99         return -1;
100     }
101 
102     // 一些内存分配
103     packet = (AVPacket *)av_malloc(sizeof(AVPacket));
104     pFrame = av_frame_alloc();
105     pFrameYUV = av_frame_alloc();
106     out_buffer = (uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
107     // 为已经分配空间的结构体AVPicture挂上一段用于保存数据的空间
108     // AVFrame/AVPicture有一个data[4]的数据字段,buffer里面存放的只是yuv这样排列的数据,
109     // 而经过fill 之后,会把buffer中的yuv分别放到data[0],data[1],data[2]中。
110     avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
111 
112     //Output Info-----------------------------
113     printf("--------------- File Information ----------------\n");
114     av_dump_format(pFormatCtx, 0, filepath, 0);
115     printf("-------------------------------------------------\n");
116 
117     // 初始化一个SwsContext
118     // 参数:源图像的宽,源图像的高,源图像的像素格式,目标图像的宽,目标图像的高,目标图像的像素格式,设定图像拉伸使用的算法
119     img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
120         pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
121 
122     while (av_read_frame(pFormatCtx, packet) >= 0)
123     {
124         if (packet->stream_index == videoindex)
125         {
126             // 解码一帧视频数据。输入一个压缩编码的结构体AVPacket,输出一个解码后的结构体AVFrame
127             ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
128             if (ret < 0)
129             {
130                 printf("Decode Error.\n");
131                 return -1;
132             }
133             if (got_picture)
134             {
135                 // 转换像素
136                 sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
137                     pFrameYUV->data, pFrameYUV->linesize);
138 
139                 y_size = pCodecCtx->width * pCodecCtx->height;
140                 // 向文件写入一个数据块
141                 fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv);        //Y 
142                 fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv);  //U
143                 fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv);  //V
144                 //printf("Succeed to decode 1 frame!\n");
145 
146             }
147         }
148         av_free_packet(packet);
149     }
150 
151     //flush decoder
152     //FIX: Flush Frames remained in Codec
153     while (1)
154     {
155         ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
156         if (ret < 0)
157             break;
158         if (!got_picture)
159             break;
160         sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
161             pFrameYUV->data, pFrameYUV->linesize);
162 
163         int y_size = pCodecCtx->width*pCodecCtx->height;
164 
165         fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv);        //Y 
166         fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv);  //U
167         fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv);  //V
168         //printf("Flush Decoder: Succeed to decode 1 frame!\n");
169     }
170 
171     sws_freeContext(img_convert_ctx);
172     fclose(fp_yuv);
173     av_frame_free(&pFrameYUV);
174     av_frame_free(&pFrame);
175     avcodec_close(pCodecCtx);
176     avformat_close_input(&pFormatCtx);
177 
178     return 0;
179 }
180 
181 #endif
182 
183 
184 
185 #ifdef STEP2
186 
187 /////////////////////////////////////////////////////////////////////////////////////////
188 // YUV420P像素数据编码为JPEG图片
189 
190 int main(int argc, char* argv[])
191 {
192     AVFormatContext* pFormatCtx;
193     AVOutputFormat* fmt;
194     AVStream* video_st;
195     AVCodecContext* pCodecCtx;
196     AVCodec* pCodec;
197 
198     uint8_t* picture_buf;
199     AVFrame* picture;
200     AVPacket pkt;
201     int y_size;
202     int got_picture = 0;
203     int size;
204     int ret = 0;
205     //int in_w = 480, in_h = 272;
206     int in_w = 720, in_h = 480;
207     int frame_num = 1;
208 
209     FILE *in_file = NULL;
210     const char* out_file = "new1.jpg";
211 
212     in_file = fopen("video1.yuv", "rb");
213 
214     // 计算YUV420帧数量
215     const int WIDTH = 720;
216     const int HEIGHT = 480;
217     const int EachFrameSize = (int)(WIDTH*HEIGHT*1.5);
218     if (in_file == NULL)
219     {
220         printf("couldn't open file.\n");
221         return  -1;
222     }
223     // 将文件指针移到文件末尾
224     fseek(in_file, 0, SEEK_END);
225     // 得到文件尾相对于文件首的位移,即文件的总字节数
226     // 该函数对大于2^31 -1文件,即2.1G以上的文件操作时可能出错
227     long total_size = ftell(in_file);
228     // 重置文件指针指向文件头部
229     rewind(in_file);       
230 
231     // size/(WIDTH*HEIGHT*1.5)可获得了yuv420文件的总帧数
232     long nFrame = total_size / EachFrameSize;
233     printf("该YUV420总帧数:%ld\n\n", nFrame);
234 
235     printf("转换第几帧? 请输入: ");
236     scanf("%d", &frame_num);
237     printf("\n");
238 
239     fseek(in_file, (frame_num-1) * EachFrameSize, SEEK_SET);
240 
241 
242     av_register_all();
243     pFormatCtx = avformat_alloc_context();
244 
245     // 返回一个已经注册的最合适的输出格式
246     fmt = av_guess_format("mjpeg", NULL, NULL);
247     pFormatCtx->oformat = fmt;
248 
249     if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0)
250     {
251         printf("Couldn't open output file.");
252         return -1;
253     }
254 
255     // 上述更简单的方法:
256     //avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, out_file);
257     //fmt = pFormatCtx->oformat;
258 
259     video_st = avformat_new_stream(pFormatCtx, 0);
260     if (video_st == NULL)
261     {
262         return -1;
263     }
264     pCodecCtx = video_st->codec;
265     pCodecCtx->codec_id = fmt->video_codec;
266     pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
267     pCodecCtx->pix_fmt = AV_PIX_FMT_YUVJ420P;
268 
269     pCodecCtx->width = in_w;
270     pCodecCtx->height = in_h;
271 
272     pCodecCtx->time_base.num = 1;
273     pCodecCtx->time_base.den = 25;
274 
275     // Output some information
276     av_dump_format(pFormatCtx, 0, out_file, 1);
277 
278     pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
279     if (!pCodec)
280     {
281         printf("Codec not found.");
282         return -1;
283     }
284     if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
285     {
286         printf("Could not open codec.");
287         return -1;
288     }
289     picture = av_frame_alloc();
290     size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
291     picture_buf = (uint8_t *)av_malloc(size);
292     if (!picture_buf)
293     {
294         return -1;
295     }
296 
297     avpicture_fill((AVPicture *)picture, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
298 
299     // 写头部
300     avformat_write_header(pFormatCtx, NULL);
301 
302     y_size = pCodecCtx->width * pCodecCtx->height;
303     av_new_packet(&pkt, y_size * 3);
304 
305 
306 
307 
308     // Read YUV
309     if (fread(picture_buf, 1, y_size * 3 / 2, in_file) <= 0)
310     {
311         printf("Could not read input file.");
312         return -1;
313     }
314     picture->data[0] = picture_buf;                        // Y
315     picture->data[1] = picture_buf + y_size;                // U 
316     picture->data[2] = picture_buf + y_size * 5 / 4;        // V
317 
318     // 编码
319     ret = avcodec_encode_video2(pCodecCtx, &pkt, picture, &got_picture);
320     if (ret < 0)
321     {
322         printf("Encode Error.\n");
323         return -1;
324     }
325     if (got_picture == 1)
326     {
327         pkt.stream_index = video_st->index;
328         ret = av_write_frame(pFormatCtx, &pkt);
329     }
330 
331     av_free_packet(&pkt);
332 
333     av_write_trailer(pFormatCtx);
334 
335     printf("Encode Successful.\n");
336 
337     if (video_st)
338     {
339         avcodec_close(video_st->codec);
340         av_free(picture);
341         av_free(picture_buf);
342     }
343     avio_close(pFormatCtx->pb);
344     avformat_free_context(pFormatCtx);
345     fclose(in_file);
346 
347     return 0;
348 }
349 
350 #endif

 

posted @ 2016-03-11 15:00  ht-beyond  阅读(6904)  评论(0编辑  收藏  举报