H264编码 - 无法使用VLC播放器播放视频

时间:2016-03-30 06:00:17

标签: c++ ffmpeg libavcodec

我无法使用 FFmpeg libav 正确编码H264视频。我无法在 VLC媒体播放器中播放编码视频,虽然我可以在 MPC-HC 上播放视频但时间显示为00:00/00:00。显然,我错过了一些东西。

来自 MPC-HC 的媒体信息显示了这一点:

  

一般
     格式:AVC
     格式/信息:高级视频编解码器
     文件大小:110 KiB
     持续时间:2s 400ms
     总比特率:375 Kbps
     编写库:x264 core 148 r2665 a01e339
     编码设置:cabac = 0 / ref = 3 / deblock = 1:0:0 / analyze = 0x1:0x111 / me = hex / subme = 7 / psy = 1 / psy_rd = 1.00:0.00 / mixed_ref = 1 / me_range = 16 / chroma_me = 1 / trellis = 1 / 8x8dct = 0 / cqm = 0 / deadzone = 21,11 / fast_pskip = 1 / chroma_qp_offset = -2 / threads = 7 / lookahead_threads = 1 / sliced_threads = 0 / nr = 0 / decimate = 1 / interlaced = 0 / bluray_compat = 0 / constrained_intra = 0 / bframes = 0 / weightp = 0 / keyint = 12 / keyint_min = 1 / scenecut = 40 / intra_refresh = 0 / rc_lookahead = 12 / rc = abr / mbtree = 1 / bitrate = 2000 / ratetol = 1.0 / qcomp = 0.60 / qpmin = 0 / qpmax = 69 / qpstep = 4 / ip_ratio = 1.40 / aq = 1:1.00

     

视频
     格式:AVC
     格式/信息:高级视频编解码器
     格式简介:Baseline@L2.1
     格式设置,CABAC:否
     格式设置,ReFrames:3帧
     格式设置,GOP:M = 1,N = 12
     持续时间:2s 400ms
     比特率:2 000 Kbps
     宽度:320像素
     高度:240像素
     显示宽高比:4:3
     帧率模式:变量
     帧率:20.833 fps
     色彩空间:YUV
     色度子采样:4:2:0
     位深度:8位
     扫描类型:渐进式
     位/(像素*框架):1.250
     流大小:586 KiB
     编写库:x264 core 148 r2665 a01e339
     编码设置:cabac = 0 / ref = 3 / deblock = 1:0:0 / analyze = 0x1:0x111 / me = hex / subme = 7 / psy = 1 / psy_rd = 1.00:0.00 / mixed_ref = 1 / me_range = 16 / chroma_me = 1 / trellis = 1 / 8x8dct = 0 / cqm = 0 / deadzone = 21,11 / fast_pskip = 1 / chroma_qp_offset = -2 / threads = 7 / lookahead_threads = 1 / sliced_threads = 0 / nr = 0 / decimate = 1 / interlaced = 0 / bluray_compat = 0 / constrained_intra = 0 / bframes = 0 / weightp = 0 / keyint = 12 / keyint_min = 1 / scenecut = 40 / intra_refresh = 0 / rc_lookahead = 12 / rc = abr / mbtree = 1 / bitrate = 2000 / ratetol = 1.0 / qcomp = 0.60 / qpmin = 0 / qpmax = 69 / qpstep = 4 / ip_ratio = 1.40 / aq = 1:1.00

我在上面的信息中注意到一些奇怪的事情: - 帧速率为20.833 fps,而不是指定的10 fps。 - 2s 400ms的持续时间也不合适,因为视频播放时间超过4秒。

此外,(AVFrame* picture)->pict_type始终设置为AV_PICTURE_TYPE_NONE。我不认为这是正常的。

我使用的图书馆是 ffmpeg-20160219-git-98a0053-win32-dev 。如果你能帮我解决这个困惑,我真的很感激。

/*
 * Video encoding example
 */
char filename[] = "test.mp4";
int main(int argc, char** argv)
{
    AVCodec *codec = NULL;
    AVCodecContext *codecCtx= NULL;
    AVFormatContext *pFormatCtx = NULL;
    AVStream * pVideoStream = NULL;
    AVFrame *picture = NULL;

    int i, x, y,            //
        ret,                // Return value
        got_packet_ptr;     // Data encoded into packet

    printf("Video encoding\n");

    // Register all formats and codecs
    av_register_all();

    // allocate context
    pFormatCtx = avformat_alloc_context();
    memcpy(pFormatCtx->filename,filename,
        min(strlen(filename), sizeof(pFormatCtx->filename)));

    // guess format
    pFormatCtx->oformat = av_guess_format("h264", NULL, NULL);
    if (NULL==pFormatCtx->oformat)
    {
        cerr << "Could not guess output format" << endl;
        return -1;
    }   

    // Find the codec.
    codec = avcodec_find_encoder(pFormatCtx->oformat->video_codec);
    if (codec == NULL) {
        fprintf(stderr, "Codec not found\n");
        return -1;
    }

    // Set context
    int framerate = 10;
    codecCtx = avcodec_alloc_context3(codec);
    avcodec_get_context_defaults3(codecCtx, codec); 
    codecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    codecCtx->profile = FF_PROFILE_H264_BASELINE; 
    // Resolution must be a multiple of two.
    codecCtx->width  = 320;
    codecCtx->height = 240;

    codecCtx->bit_rate = 2000000;
    codecCtx->time_base.den = framerate;
    codecCtx->time_base.num = 1;
    codecCtx->gop_size = 12; // emit one intra frame every twelve frames at most

    // Open the codec.  
    if (avcodec_open2(codecCtx, codec, NULL) < 0) 
    {
        printf("Cannot open video codec\n");
        return -1;
    }

    // Add stream to pFormatCtx
    pVideoStream = avformat_new_stream(pFormatCtx, codec);
    if (!pVideoStream) 
    {
        printf("Cannot add new video stream\n");
        return -1;
    }
    pVideoStream->codec = codecCtx;
    pVideoStream->time_base.den = framerate;
    pVideoStream->time_base.num = 1;

    if (avio_open2(&pFormatCtx->pb, filename, AVIO_FLAG_WRITE, NULL, NULL) < 0) 
    {
        printf("Cannot open file\n");
        return -1;
    }

    // Write file header.
    avformat_write_header(pFormatCtx, NULL);

    // Create frame
    picture= av_frame_alloc();
    picture->format = codecCtx->pix_fmt;
    picture->width  = codecCtx->width;
    picture->height = codecCtx->height;

    int bufferImgSize = av_image_get_buffer_size(codecCtx->pix_fmt, codecCtx->width,
                    codecCtx->height,1);    
    av_image_alloc(picture->data, picture->linesize, codecCtx->width, codecCtx->height,                 codecCtx->pix_fmt, 32);

    AVPacket avpkt;

    /* encode 1 second of video */
    for(i=0;i<50;i++) 
    {
        /* prepare a dummy image */
        /* Y */
        for(y=0;y<codecCtx->height;y++)
        {
            for(x=0;x<codecCtx->width;x++) 
            {
                picture->data[0][y * picture->linesize[0] + x] = x + y + i * 3;
            }
        }
        /* Cb and Cr */
        for(y=0;y<codecCtx->height/2;y++) 
        {
            for(x=0;x<codecCtx->width/2;x++) 
            {
                picture->data[1][y * picture->linesize[1] + x] = 128 + y + i * 2;
                picture->data[2][y * picture->linesize[2] + x] = 64 + x + i * 5;
            }
        }

        // Get timestamp
        picture->pts = (float) i * (1000.0/(float)(codecCtx->time_base.den)) * 90; 

        // Encode frame to packet
        av_init_packet(&avpkt);
        got_packet_ptr = 0;
        int error = avcodec_encode_video2(codecCtx, &avpkt, picture, &got_packet_ptr);
        if (!error && got_packet_ptr > 0) 
        {
            // Write packet with frame.
            ret = (av_interleaved_write_frame(pFormatCtx, &avpkt) == 0);        
        }   
        av_packet_unref(&avpkt); 
    }

    // Flush remaining encoded data
    while(1)
    {
        av_init_packet(&avpkt);
        got_packet_ptr = 0;
        // Encode frame to packet.
        int error = avcodec_encode_video2(codecCtx, &avpkt, NULL, &got_packet_ptr);
        if (!error && got_packet_ptr > 0) 
        {
            // Write packet with frame.
            ret = (av_interleaved_write_frame(pFormatCtx, &avpkt) == 0);        
        } 
        else 
        {
            break;
        }
        av_packet_unref(&avpkt); 
    }
    av_write_trailer(pFormatCtx);

    av_packet_unref(&avpkt);
    av_frame_free(&picture);

    avcodec_close(codecCtx);
    av_free(codecCtx);

    cin.get();
}

1 个答案:

答案 0 :(得分:1)

从libav邮件列表中得到答案(感谢Gonzalo),我想在此分享我的答案。

我搞砸了格式,因为av_guess_format("h264", NULL, NULL)pFormatCtx->oformat设置为以下内容:

  

pFormatCtx-&gt; oformat.name 0x009df614“h264”
     pFormatCtx-&gt; oformat.long_name 0x009df619“原始H.264视频”
     pFormatCtx-&gt; oformat.mime_type 0x00000000
     pFormatCtx-&gt; oformat.extensions 0x009df629“h264,264”
     pFormatCtx-&gt; oformat.audio_codec AV_CODEC_ID_NONE
     pFormatCtx-&gt; oformat.video_codec AV_CODEC_ID_H264
     pFormatCtx-&gt; oformat.subtitle_codec AV_CODEC_ID_NONE
     pFormatCtx-&gt; oformat.flags 128
     pFormatCtx-&gt; oformat.codec_tag 0x00000000
     pFormatCtx-&gt; oformat.priv_class 0x00000000 {class_name = ??? ITEM_NAME = ???选项= ??? ...}
     pFormatCtx-&gt; oformat.next 0x009980c0 {name = 0x009bb03e“hds”long_name = 0x009bb042“HDS Muxer”mime_type = 0x00000000 ...}
     pFormatCtx-&gt; oformat.priv_data_size 0
     pFormatCtx-&gt; oformat.write_header 0x005bad70
     pFormatCtx-&gt; oformat.write_packet 0x005bac40
     pFormatCtx-&gt; oformat.write_trailer 0x00000000

更正确的调用是av_guess_format(NULL, filename, NULL),其中包含以下内容:

  

pFormatCtx-&gt; oformat.name 0x009ce84c“mp4”
     pFormatCtx-&gt; oformat.long_name 0x009ce850“MP4(MPEG-4 Part 14)”

     pFormatCtx-&gt; oformat.mime_type 0x009ce689“video / mp4”
     pFormatCtx-&gt; oformat.extensions 0x009ce84c“mp4”
     pFormatCtx-&gt; oformat.audio_codec AV_CODEC_ID_AAC
     pFormatCtx-&gt; oformat.video_codec AV_CODEC_ID_H264
     pFormatCtx-&gt; oformat.subtitle_codec AV_CODEC_ID_NONE
     pFormatCtx-&gt; oformat.flags 327744
     pFormatCtx-&gt; oformat.codec_tag 0x009ce868
     pFormatCtx-&gt; oformat.priv_class 0x009ce880 {class_name = 0x009ce870“mp4 muxer”item_name = 0x0062bc70 option = 0x009cf300 ...}
     pFormatCtx-&gt; oformat.next 0x0099ae00 {name = 0x009d104c“mpeg”long_name = 0x009d1054“MPEG-1 Systems / MPEG program stream”mime_type = 0x009d0ec8“video / mpeg”...}
     pFormatCtx-&gt; oformat.priv_data_size 176
     pFormatCtx-&gt; oformat.write_header 0x0056ad20
     pFormatCtx-&gt; oformat.write_packet 0x0056a260
     pFormatCtx-&gt; oformat.write_trailer 0x0056a7f0

话虽如此,如果我使用avformat_alloc_output_context2( &pFormatCtx,NULL, NULL, filename )完成avformat_alloc_context()av_guess_format()所需的工作,生活会变得更加简单。