H264 编码 - 无法使用 VLC Player 播放视频
H264 Encoding - Could not play video using VLC Player
我在使用 FFmpeg libav 正确编码 H264 视频时遇到问题。我无法在 VLC 媒体播放器 中播放编码视频,虽然我可以在 MPC-HC 上播放视频,但时间显示 00:00/00:00
.显然我错过了一些东西。
来自 MPC-HC 的媒体信息显示:
General
Format : AVC
Format/Info : Advanced Video Codec
File size : 110 KiB
Duration : 2s 400ms
Overall bit rate : 375 Kbps
Writing library : x264 core 148 r2665 a01e339
Encoding settings : cabac=0 / ref=3 / deblock=1:0:0 / analyse=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
Video
Format : AVC
Format/Info : Advanced Video Codec
Format profile : Baseline@L2.1
Format settings, CABAC : No
Format settings, ReFrames : 3 frames
Format settings, GOP : M=1, N=12
Duration : 2s 400ms
Bit rate : 2 000 Kbps
Width : 320 pixels
Height : 240 pixels
Display aspect ratio : 4:3
Frame rate mode : Variable
Frame rate : 20.833 fps
Color space : YUV
Chroma subsampling : 4:2:0
Bit depth : 8 bits
Scan type : Progressive
Bits/(Pixel*Frame) : 1.250
Stream size : 586 KiB
Writing library : x264 core 148 r2665 a01e339
Encoding settings : cabac=0 / ref=3 / deblock=1:0:0 / analyse=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();
}
从 libav 邮件列表中得到了答案(感谢 Gonzalo),我想在这里分享我的答案。
我弄乱了格式,因为 av_guess_format("h264", NULL, NULL)
将 pFormatCtx->oformat
设置为以下内容:
pFormatCtx->oformat.name 0x009df614 "h264"
pFormatCtx->oformat.long_name 0x009df619 "raw H.264 video"
pFormatCtx->oformat.mime_type 0x00000000
pFormatCtx->oformat.extensions 0x009df629 "h264,264"
pFormatCtx->oformat.audio_codec AV_CODEC_ID_NONE
pFormatCtx->oformat.video_codec AV_CODEC_ID_H264
pFormatCtx->oformat.subtitle_codec AV_CODEC_ID_NONE
pFormatCtx->oformat.flags 128
pFormatCtx->oformat.codec_tag 0x00000000
pFormatCtx->oformat.priv_class 0x00000000 {class_name=??? item_name=??? option=??? ...}
pFormatCtx->oformat.next 0x009980c0 {name=0x009bb03e "hds" long_name=0x009bb042 "HDS Muxer" mime_type=0x00000000 ...}
pFormatCtx->oformat.priv_data_size 0
pFormatCtx->oformat.write_header 0x005bad70
pFormatCtx->oformat.write_packet 0x005bac40
pFormatCtx->oformat.write_trailer 0x00000000
更正确的调用是 av_guess_format(NULL, filename, NULL)
,它设置为以下内容:
pFormatCtx->oformat.name 0x009ce84c "mp4"
pFormatCtx->oformat.long_name 0x009ce850 "MP4 (MPEG-4 Part 14)"
pFormatCtx->oformat.mime_type 0x009ce689 "video/mp4"
pFormatCtx->oformat.extensions 0x009ce84c "mp4"
pFormatCtx->oformat.audio_codec AV_CODEC_ID_AAC
pFormatCtx->oformat.video_codec AV_CODEC_ID_H264
pFormatCtx->oformat.subtitle_codec AV_CODEC_ID_NONE
pFormatCtx->oformat.flags 327744
pFormatCtx->oformat.codec_tag 0x009ce868
pFormatCtx->oformat.priv_class 0x009ce880 {class_name=0x009ce870 "mp4 muxer" item_name=0x0062bc70 option=0x009cf300 ...}
pFormatCtx->oformat.next 0x0099ae00 {name=0x009d104c "mpeg" long_name=0x009d1054 "MPEG-1 Systems / MPEG program stream" mime_type=0x009d0ec8 "video/mpeg" ...}
pFormatCtx->oformat.priv_data_size 176
pFormatCtx->oformat.write_header 0x0056ad20
pFormatCtx->oformat.write_packet 0x0056a260
pFormatCtx->oformat.write_trailer 0x0056a7f0
话虽如此,如果我使用 avformat_alloc_output_context2( &pFormatCtx,NULL, NULL, filename )
,生活会简单很多,因为它可以用 avformat_alloc_context()
和 av_guess_format()
.
完成我需要的事情
我在使用 FFmpeg libav 正确编码 H264 视频时遇到问题。我无法在 VLC 媒体播放器 中播放编码视频,虽然我可以在 MPC-HC 上播放视频,但时间显示 00:00/00:00
.显然我错过了一些东西。
来自 MPC-HC 的媒体信息显示:
General
Format : AVC
Format/Info : Advanced Video Codec
File size : 110 KiB
Duration : 2s 400ms
Overall bit rate : 375 Kbps
Writing library : x264 core 148 r2665 a01e339
Encoding settings : cabac=0 / ref=3 / deblock=1:0:0 / analyse=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.00Video
Format : AVC
Format/Info : Advanced Video Codec
Format profile : Baseline@L2.1
Format settings, CABAC : No
Format settings, ReFrames : 3 frames
Format settings, GOP : M=1, N=12
Duration : 2s 400ms
Bit rate : 2 000 Kbps
Width : 320 pixels
Height : 240 pixels
Display aspect ratio : 4:3
Frame rate mode : Variable
Frame rate : 20.833 fps
Color space : YUV
Chroma subsampling : 4:2:0
Bit depth : 8 bits
Scan type : Progressive
Bits/(Pixel*Frame) : 1.250
Stream size : 586 KiB
Writing library : x264 core 148 r2665 a01e339
Encoding settings : cabac=0 / ref=3 / deblock=1:0:0 / analyse=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();
}
从 libav 邮件列表中得到了答案(感谢 Gonzalo),我想在这里分享我的答案。
我弄乱了格式,因为 av_guess_format("h264", NULL, NULL)
将 pFormatCtx->oformat
设置为以下内容:
pFormatCtx->oformat.name 0x009df614 "h264"
pFormatCtx->oformat.long_name 0x009df619 "raw H.264 video"
pFormatCtx->oformat.mime_type 0x00000000
pFormatCtx->oformat.extensions 0x009df629 "h264,264"
pFormatCtx->oformat.audio_codec AV_CODEC_ID_NONE
pFormatCtx->oformat.video_codec AV_CODEC_ID_H264
pFormatCtx->oformat.subtitle_codec AV_CODEC_ID_NONE
pFormatCtx->oformat.flags 128
pFormatCtx->oformat.codec_tag 0x00000000
pFormatCtx->oformat.priv_class 0x00000000 {class_name=??? item_name=??? option=??? ...}
pFormatCtx->oformat.next 0x009980c0 {name=0x009bb03e "hds" long_name=0x009bb042 "HDS Muxer" mime_type=0x00000000 ...}
pFormatCtx->oformat.priv_data_size 0
pFormatCtx->oformat.write_header 0x005bad70
pFormatCtx->oformat.write_packet 0x005bac40
pFormatCtx->oformat.write_trailer 0x00000000
更正确的调用是 av_guess_format(NULL, filename, NULL)
,它设置为以下内容:
pFormatCtx->oformat.name 0x009ce84c "mp4"
pFormatCtx->oformat.long_name 0x009ce850 "MP4 (MPEG-4 Part 14)"
pFormatCtx->oformat.mime_type 0x009ce689 "video/mp4"
pFormatCtx->oformat.extensions 0x009ce84c "mp4"
pFormatCtx->oformat.audio_codec AV_CODEC_ID_AAC
pFormatCtx->oformat.video_codec AV_CODEC_ID_H264
pFormatCtx->oformat.subtitle_codec AV_CODEC_ID_NONE
pFormatCtx->oformat.flags 327744
pFormatCtx->oformat.codec_tag 0x009ce868
pFormatCtx->oformat.priv_class 0x009ce880 {class_name=0x009ce870 "mp4 muxer" item_name=0x0062bc70 option=0x009cf300 ...}
pFormatCtx->oformat.next 0x0099ae00 {name=0x009d104c "mpeg" long_name=0x009d1054 "MPEG-1 Systems / MPEG program stream" mime_type=0x009d0ec8 "video/mpeg" ...}
pFormatCtx->oformat.priv_data_size 176
pFormatCtx->oformat.write_header 0x0056ad20
pFormatCtx->oformat.write_packet 0x0056a260
pFormatCtx->oformat.write_trailer 0x0056a7f0
话虽如此,如果我使用 avformat_alloc_output_context2( &pFormatCtx,NULL, NULL, filename )
,生活会简单很多,因为它可以用 avformat_alloc_context()
和 av_guess_format()
.