• 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.
    ");
     64         return -1;
     65     }
     66     if (avformat_find_stream_info(pFormatCtx, NULL) <0)
     67     {
     68         printf("Couldn't find stream information.
    ");
     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.
    ");
     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.
    ");
     93         return -1;
     94     }
     95     // 初始化一个视音频编解码器的AVCodecContext
     96     if (avcodec_open2(pCodecCtx, pCodec, NULL)<0)
     97     {
     98         printf("Could not open codec.
    ");
     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 ----------------
    ");
    114     av_dump_format(pFormatCtx, 0, filepath, 0);
    115     printf("-------------------------------------------------
    ");
    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.
    ");
    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!
    ");
    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!
    ");
    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.
    ");
    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
    
    ", nFrame);
    234 
    235     printf("转换第几帧? 请输入: ");
    236     scanf("%d", &frame_num);
    237     printf("
    ");
    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.
    ");
    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.
    ");
    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
  • 相关阅读:
    const与readonly
    JQuery Tooltipster
    Log4Net使用
    asp.net mvc 4 beta 版已发布
    控件属性
    C# 获取当前路径
    对toLocaleString()、toString()、valueOf()的理解
    靶场练习3CSRF攻击
    计算字符串长度
    Android ListView 自定义适配器
  • 原文地址:https://www.cnblogs.com/ht-beyond/p/5265710.html
Copyright © 2020-2023  润新知