首页 > 代码库 > FFmpeg YUV视频序列编码为视频

FFmpeg YUV视频序列编码为视频

上一篇已经写了如何配置好开发环境,这次就先小试牛刀,来个视频的编码。搞视频处理的朋友肯定比较熟悉YUV视频序列,很多测试库提供的视频数据都是YUV视频序列,我们这里就用用YUV视频序列来做视频。关于YUV视频序列,我就不多讲了,可以看书学习,通常的视频序列都是YUV420格式的。

步骤也就那几步,添加视频流,打开编码器,开辟相应的内存空间,然后就可以打开YUV序列逐帧写入数据了,so easy!记得最后要做好文件的关闭和内存的释放,因为FFmpeg是c风格的(不知道新版本是否是c++风格的),这些工作都需要自己做好啊。过多的说明是没用的,直接上代码:

这里我补充一下,大多数的视频格式好像只支持YUV格式的视频帧AVFrame,我试图直接把RGB的视频序列直接编码到视频这条路好像走不通,都需要把RGB的视频帧再转成YUV视频帧才行,不知道高手有没有其他高见。

#include <stdio.h>#include <string.h>extern "C"{#include <libavcodec\avcodec.h>#include <libavformat\avformat.h>#include <libswscale\swscale.h>};void main(int argc, char ** argv){    AVFormatContext* oc;    AVOutputFormat* fmt;    AVStream* video_st;    double video_pts;    uint8_t* video_outbuf;    uint8_t* picture_buf;    AVFrame* picture;//     AVFrame* pictureRGB;    int size;    int ret;    int video_outbuf_size;    FILE *fin = fopen("akiyo_qcif.yuv", "rb"); //视频源文件     const char* filename = "test.mpg";//     const char* filename;//     filename = argv[1];    av_register_all();//     avcodec_init(); // 初始化codec库//     avcodec_register_all(); // 注册编码器    fmt = guess_format(NULL, filename, NULL);    oc = av_alloc_format_context();    oc->oformat = fmt;    snprintf(oc->filename, sizeof(oc->filename), "%s", filename);    video_st = NULL;    if (fmt->video_codec != CODEC_ID_NONE)    {        AVCodecContext* c;        video_st = av_new_stream(oc, 0);        c = video_st->codec;        c->codec_id = fmt->video_codec;        c->codec_type = CODEC_TYPE_VIDEO;        c->bit_rate = 400000;        c->width = 176;        c->height = 144;        c->time_base.num = 1;        c->time_base.den = 25;         c->gop_size = 12;        c->pix_fmt = PIX_FMT_YUV420P;        if (c->codec_id == CODEC_ID_MPEG2VIDEO)        {            c->max_b_frames = 2;        }        if (c->codec_id == CODEC_ID_MPEG1VIDEO)        {            c->mb_decision = 2;        }        if (!strcmp(oc->oformat->name, "mp4") || !strcmp(oc->oformat->name, "mov") || !strcmp(oc->oformat->name, "3gp"))        {            c->flags |= CODEC_FLAG_GLOBAL_HEADER;        }    }    if (av_set_parameters(oc, NULL)<0)    {        return;    }        dump_format(oc, 0, filename, 1);    if (video_st)    {        AVCodecContext* c;        AVCodec* codec;        c = video_st->codec;        codec = avcodec_find_encoder(c->codec_id);        if (!codec)        {            return;        }        if (avcodec_open(c, codec) < 0)        {            return;        }        if (!(oc->oformat->flags & AVFMT_RAWPICTURE))        {            video_outbuf_size = 200000;            video_outbuf = (uint8_t*)av_malloc(video_outbuf_size);        }        picture = avcodec_alloc_frame();        size = avpicture_get_size(c->pix_fmt, c->width, c->height);        picture_buf = (uint8_t*)av_malloc(size);        if (!picture_buf)        {            av_free(picture);        }        avpicture_fill((AVPicture*)picture, picture_buf, c->pix_fmt, c->width, c->height);    }    if (!(fmt->flags & AVFMT_NOFILE))    {        if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0)        {            return;        }    }    av_write_header(oc);    for (int i=0; i<300; i++)    {        if (video_st)        {            video_pts = (double)(video_st->pts.val * video_st->time_base.num / video_st->time_base.den);        }        else        {            video_pts = 0.0;        }        if (!video_st/* || video_pts >= 5.0*/)        {            break;        }        AVCodecContext* c;        c = video_st->codec;        size = c->width * c->height;        if (fread(picture_buf, 1, size*3/2, fin) < 0)        {            break;        }                picture->data[0] = picture_buf;  // 亮度        picture->data[1] = picture_buf+ size;  // 色度         picture->data[2] = picture_buf+ size*5/4; // 色度         // 如果是rgb序列,可能需要如下代码//         SwsContext* img_convert_ctx;//         img_convert_ctx = sws_getContext(c->width, c->height, PIX_FMT_RGB24, c->width, c->height, c->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL);//         sws_scale(img_convert_ctx, pictureRGB->data, pictureRGB->linesize, 0, c->height, picture->data, picture->linesize);        if (oc->oformat->flags & AVFMT_RAWPICTURE)        {            AVPacket pkt;            av_init_packet(&pkt);            pkt.flags |= PKT_FLAG_KEY;            pkt.stream_index = video_st->index;            pkt.data = (uint8_t*)picture;            pkt.size = sizeof(AVPicture);            ret = av_write_frame(oc, &pkt);        }        else        {            int out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture);            if (out_size > 0)            {                AVPacket pkt;                av_init_packet(&pkt);                pkt.pts = av_rescale_q(c->coded_frame->pts, c->time_base, video_st->time_base);                if (c->coded_frame->key_frame)                {                    pkt.flags |= PKT_FLAG_KEY;                }                pkt.stream_index = video_st->index;                pkt.data = video_outbuf;                pkt.size = out_size;                ret = av_write_frame(oc, &pkt);            }        }    }    if (video_st)    {        avcodec_close(video_st->codec);//         av_free(picture->data[0]);        av_free(picture);        av_free(video_outbuf);//         av_free(picture_buf);    }    av_write_trailer(oc);    for (int i=0; i<oc->nb_streams; i++)    {        av_freep(&oc->streams[i]->codec);        av_freep(&oc->streams[i]);    }    if (!(fmt->flags & AVFMT_NOFILE))    {        url_fclose(oc->pb);    }    av_free(oc);}

 

上一篇介绍了视频编码的小例子,视频解码跟编码差不多,只是要在视频文件中寻找视频流,找到后对流逐帧解码,就这样简单。闲言少叙,上code:

int main(int argc, char *argv[]){    AVFormatContext *pFormatCtx;    int             i, videoStream;    AVCodecContext  *pCodecCtx;    AVCodec         *pCodec;    AVFrame         *pFrame;     AVFrame         *pFrameRGB;    AVPacket        packet;    int             frameFinished;    int             numBytes;    uint8_t         *buffer;    struct SwsContext *img_convert_ctx;    if(argc < 2)    {        printf("Please provide a movie file\n");        return -1;    }    // Register all formats and codecs    // 初始化ffmpeg库    av_register_all();    // Open video file    if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)        return -1; // Couldn‘t open file    // Retrieve stream information    // 查找文件的流信息    if(av_find_stream_info(pFormatCtx)<0)        return -1; // Couldn‘t find stream information    // Dump information about file onto standard error    // dump只是一个调试函数,输出文件的音、视频流的基本信息:帧率、分辨率、音频采样等等    dump_format(pFormatCtx, 0, argv[1], 0);    // Find the first video stream    // 遍历文件的流,找到第一个视频流,并记录流的编码信息    videoStream=-1;    for(i=0; i<pFormatCtx->nb_streams; i++)    {        if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO)        {            videoStream=i;            break;        }    }    if(videoStream==-1)        return -1; // Didn‘t find a video stream    // Get a pointer to the codec context for the video stream    // 得到视频流编码的上下文指针    pCodecCtx=pFormatCtx->streams[videoStream]->codec;    // construct the scale context, conversing to PIX_FMT_RGB24    // 根据编码信息设置渲染格式    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,             pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);    if(img_convert_ctx == NULL)    {        fprintf(stderr, "Cannot initialize the conversion context!\n");//         exit(1);        return -1;    }    // Find the decoder for the video stream    // 在库里面查找支持该格式的解码器    pCodec=avcodec_find_decoder(pCodecCtx->codec_id);    if(pCodec==NULL)    {        fprintf(stderr, "Unsupported codec!\n");        return -1; // Codec not found    }    // Open codec    // 打开解码器    if(avcodec_open(pCodecCtx, pCodec)<0)        return -1; // Could not open codec    // Allocate video frame    // 分配一个帧指针,指向解码后的原始帧    pFrame=avcodec_alloc_frame();    // Allocate an AVFrame structure    // 分配一个帧指针,指向存放转换成rgb后的帧    pFrameRGB=avcodec_alloc_frame();    if(pFrameRGB==NULL)        return -1;    // Determine required buffer size and allocate buffer    numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);    buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));    // buffer = new uint8_t[numBytes];    // Assign appropriate parts of buffer to image planes in pFrameRGB    // Note that pFrameRGB is an AVFrame, but AVFrame is a superset    // of AVPicture    // 给pFrameRGB帧附加上分配的内存    avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);    // Read frames and save first five frames to disk    i=0;    while(av_read_frame(pFormatCtx, &packet)>=0)    // 读取一个帧    {        // Is this a packet from the video stream?        if(packet.stream_index==videoStream)        {            // Decode video frame            // 解码该帧            avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, packet.data, packet.size);            // Did we get a video frame?            if(frameFinished)            {                // Convert the image from its native format to RGB                // img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24,                 //                 (AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width,                 //              pCodecCtx->height);                                // 把该帧转换成rgb                // 如果只提取关键帧,加上这句                // if (pFrame->key_frame == 1)                sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);                    // Save the frame to disk                // 保存前5帧                if(++i<=5)                {//                     char pic[200];//                     sprintf(pic,"pic%d.bmp",i);//                     av_create_bmp(pic, pFrameRGB->data[0], pCodecCtx->width, pCodecCtx->height, 24);                    SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);                }            }        }        // Free the packet that was allocated by av_read_frame        // 释放读取的帧内存        av_free_packet(&packet);    }    // Free the RGB image    av_free(buffer);    av_free(pFrameRGB);    // Free the YUV frame    av_free(pFrame);    // Close the codec    avcodec_close(pCodecCtx);    // Close the video file    av_close_input_file(pFormatCtx);    return 0;}

 

FFmpeg浅尝辄止(四)——音频的解码和编码

音频和视频其实是一样的,在文件中寻找音频流,然后解压出来,得到音频帧的数据,同样也可以按照设定的编码格式进行压缩,我这里把音频的解码和编码做成了两个工程,也是直接上代码:

#include <stdio.h>#include <stdlib.h>extern "C"{#include <libavcodec\avcodec.h>#include <libavformat\avformat.h>#include <libswscale\swscale.h>}int main(char arg,char *argv[]){    char *filename = argv[1];    av_register_all();    //注册所有可解码类型    AVFormatContext *pInFmtCtx=NULL;    //文件格式    AVCodecContext *pInCodecCtx=NULL;    //编码格式     if (av_open_input_file(&pInFmtCtx, filename, NULL, 0, NULL)!=0)    //获取文件格式        printf("av_open_input_file error\n");    if (av_find_stream_info(pInFmtCtx) < 0)    //获取文件内音视频流的信息        printf("av_find_stream_info error\n");    unsigned int j;    // Find the first audio stream    int audioStream = -1;    for (j=0; j<pInFmtCtx->nb_streams; j++)    //找到音频对应的stream    {        if (pInFmtCtx->streams[j]->codec->codec_type == CODEC_TYPE_AUDIO)        {            audioStream = j;            break;        }    }    if (audioStream == -1)    {        printf("input file has no audio stream\n");        return 0; // Didn‘t find a audio stream    }    printf("audio stream num: %d\n",audioStream);    pInCodecCtx = pInFmtCtx->streams[audioStream]->codec; //音频的编码上下文    AVCodec *pInCodec = NULL;    pInCodec = avcodec_find_decoder(pInCodecCtx->codec_id); //根据编码ID找到用于解码的结构体    if (pInCodec == NULL)    {        printf("error no Codec found\n");        return -1 ; // Codec not found    }    if(avcodec_open(pInCodecCtx, pInCodec)<0)//将两者结合以便在下面的解码函数中调用pInCodec中的对应解码函数    {        printf("error avcodec_open failed.\n");        return -1; // Could not open codec    }    static AVPacket packet;    printf(" bit_rate = %d \r\n", pInCodecCtx->bit_rate);    printf(" sample_rate = %d \r\n", pInCodecCtx->sample_rate);    printf(" channels = %d \r\n", pInCodecCtx->channels);    printf(" code_name = %s \r\n", pInCodecCtx->codec->name);    printf(" block_align = %d\n",pInCodecCtx->block_align);    uint8_t *pktdata;    int pktsize;    int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*100;    uint8_t * inbuf = (uint8_t *)malloc(out_size);    FILE* pcm;    pcm = fopen("result.pcm","wb");    long start = clock();    while (av_read_frame(pInFmtCtx, &packet) >= 0)//pInFmtCtx中调用对应格式的packet获取函数    {        if(packet.stream_index==audioStream)//如果是音频        {            pktdata = packet.data;            pktsize = packet.size;            while(pktsize>0)            {                out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*100;                //解码                int len = avcodec_decode_audio2(pInCodecCtx, (short*)inbuf, &out_size, pktdata, pktsize);                if (len < 0)                {                    printf("Error while decoding.\n");                    break;                }                if(out_size > 0)                {                    fwrite(inbuf,1,out_size,pcm);//pcm记录                    fflush(pcm);                }                pktsize -= len;                pktdata += len;            }        }         av_free_packet(&packet);    }    long end = clock();    printf("cost time :%f\n",double(end-start)/(double)CLOCKS_PER_SEC);    free(inbuf);    fclose(pcm);    if (pInCodecCtx!=NULL)    {        avcodec_close(pInCodecCtx);    }    av_close_input_file(pInFmtCtx);    return 0;}

解码后的文件保存为result.pcm中,现在用这个文件压缩出新的音频文件,代码如下:

void main(){    int16_t *samples;    uint8_t *audio_outbuf;    int audio_outbuf_size;    int audio_input_frame_size;    double audio_pts;        const char* filename = "test.wav";    FILE *fin = fopen("result.pcm", "rb"); //音频源文件     AVOutputFormat *fmt;    AVFormatContext *oc;    AVStream * audio_st;    av_register_all();    fmt = guess_format(NULL, filename, NULL);    oc = av_alloc_format_context();    oc->oformat = fmt;    snprintf(oc->filename, sizeof(oc->filename), "%s", filename);    audio_st = NULL;    if (fmt->audio_codec != CODEC_ID_NONE)    {        AVCodecContext *c;        audio_st = av_new_stream(oc, 1);        c = audio_st->codec;        c->codec_id = fmt->audio_codec;        c->codec_type = CODEC_TYPE_AUDIO;        c->bit_rate = 128000;        c->sample_rate = 44100;        c->channels = 2;    }    if (av_set_parameters(oc, NULL) < 0)    {        return;    }    dump_format(oc, 0, filename, 1);    if (audio_st)    {        AVCodecContext* c;        AVCodec* codec;        c = audio_st->codec;        codec = avcodec_find_encoder(c->codec_id);        avcodec_open(c, codec);        audio_outbuf_size = 10000;        audio_outbuf = (uint8_t*)av_malloc(audio_outbuf_size);        if (c->frame_size <= 1)        {            audio_input_frame_size = audio_outbuf_size / c->channels;            switch (audio_st->codec->codec_id)            {            case CODEC_ID_PCM_S16LE:            case CODEC_ID_PCM_S16BE:            case CODEC_ID_PCM_U16LE:            case CODEC_ID_PCM_U16BE:                audio_input_frame_size >>= 1;                break;            default:                break;            }        }        else        {            audio_input_frame_size = c->frame_size;        }        samples = (int16_t*)av_malloc(audio_input_frame_size*2*c->channels);    }    if (!fmt->flags & AVFMT_NOFILE)    {        if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0)        {            return;        }    }    av_write_header(oc);    for (;;)    {        if (audio_st)        {            audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den;        }        else        {            audio_pts = 0.0;        }        if (!audio_st || audio_pts >= 360.0)        {            break;        }        if (fread(samples, 1, audio_input_frame_size*2*audio_st->codec->channels, fin) <= 0)        {            break;        }        AVCodecContext* c;        AVPacket pkt;        av_init_packet(&pkt);        c = audio_st->codec;        pkt.size = avcodec_encode_audio(c, audio_outbuf, audio_outbuf_size, samples);        pkt.pts = av_rescale_q(c->coded_frame->pts, c->time_base, audio_st->time_base);        pkt.flags |= PKT_FLAG_KEY;        pkt.stream_index = audio_st->index;        pkt.data = audio_outbuf;        if (av_write_frame(oc, &pkt) != 0)        {            return;        }    }    if (audio_st)    {        avcodec_close(audio_st->codec);        av_free(samples);        av_free(audio_outbuf);    }    av_write_trailer(oc);    for (int i=0; i<oc->nb_streams; i++)    {        av_freep(&oc->streams[i]->codec);        av_freep(&oc->streams[i]);    }    if (!(fmt->flags & AVFMT_NOFILE))    {        url_fclose(oc->pb);    }    av_free(oc);    fclose(fin);}

对应的下载链接:

http://download.csdn.net/detail/yang_xian521/4398576

音频解码:http://download.csdn.net/detail/yang_xian521/4399219

音频编码:http://download.csdn.net/detail/yang_xian521/4399293

至此,我已经实现了视频的解码编码,音频的解码编码,相信有心人肯定可以在此基础上实现视频音频的同步压缩,不过要再看一些网上的资料,也是很轻松的。至于视频的显示播放,大多数是要结合SDL实现,由于我只是浅尝辄止,只实现了图片的显示和简单的视频播放,比起我之前推荐的几个链接是弱爆了的,就不写下去了,这个系列就到这里吧,希望能给像我一样的一些入门级选手点帮助。也欢迎大神来多多指教。

(附: 例子文件见360云盘的  所有文件-> work -> 音视频处理)

FFmpeg YUV视频序列编码为视频