简体   繁体   English

ffmpeg API h264编码视频无法在所有平台上播放

[英]ffmpeg API h264 encoded video does not play on all platforms

Edit: In the previous version I used a very old ffmpeg API. 编辑:在以前的版本中,我使用了一个非常古老的ffmpeg API。 I now use the newest libraries. 我现在使用最新的库。 The problem has only changed slightly, from "Main" to "High". 问题只是略有改变,从“主要”到“高”。

I am using the ffmpeg C API to create a mp4 video in C++. 我正在使用ffmpeg C API在C ++中创建mp4视频。

I want the resulting video to be of the profile "Constrained Baseline", so that the resulting video can be played on as much platforms as possible, especially mobile, but I get "High" profile every time, even though I hard coded the codec profile to be FF_PROFILE_H264_CONSTRAINED_BASELINE . 我希望得到的视频属于“约束基线”,这样生成的视频可以在尽可能多的平台上播放,特别是移动,但我每次都获得“高”配置文件,即使我对编解码器进行了硬编码个人资料为FF_PROFILE_H264_CONSTRAINED_BASELINE As a result, the video does not play on all our testing platforms. 因此,视频无法在我们所有的测试平台上播放。

This is what "ffprobe video.mp4 -show_streams" tells about my video streams: 这就是“ffprobe video.mp4 -show_streams”讲述我的视频流:

  Metadata:
major_brand     : isom
minor_version   : 512
compatible_brands: isomiso2avc1mp41
creation_time   : 1970-01-01 00:00:00
encoder         : Lavf53.5.0
  Duration: 00:00:13.20, start: 0.000000, bitrate: 553 kb/s
Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 320x180,
424 kb/s, 15 fps, 15 tbr, 15 tbn, 30 tbc
Metadata:
  creation_time   : 1970-01-01 00:00:00
  handler_name    : VideoHandler
Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 44100 Hz, stereo, s16, 12
kb/s
Metadata:
  creation_time   : 1970-01-01 00:00:00
  handler_name    : SoundHandler
-------VIDEO STREAM--------
[STREAM] 
index=0
codec_name=h264
codec_long_name=H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10

profile=High <-- This should be "Constrained Baseline"

codec_type=video
codec_time_base=1/30
codec_tag_string=avc1
codec_tag=0x31637661
width=320
height=180
has_b_frames=0
sample_aspect_ratio=N/A
display_aspect_ratio=N/A
pix_fmt=yuv420p
level=30
timecode=N/A
is_avc=1
nal_length_size=4
id=N/A
r_frame_rate=15/1
avg_frame_rate=15/1
time_base=1/15
start_time=0.000000
duration=13.200000
bit_rate=424252
nb_frames=198
nb_read_frames=N/A
nb_read_packets=N/A
TAG:creation_time=1970-01-01 00:00:00
TAG:language=und
TAG:handler_name=VideoHandler
[/STREAM]
-------AUDIO STREAM--------
[STREAM]
index=1
codec_name=aac
codec_long_name=Advanced Audio Coding
profile=unknown
codec_type=audio
codec_time_base=1/44100
codec_tag_string=mp4a
codec_tag=0x6134706d
sample_fmt=s16
sample_rate=44100
channels=2
bits_per_sample=0
id=N/A
r_frame_rate=0/0
avg_frame_rate=0/0
time_base=1/44100
start_time=0.000000
duration=13.165714
bit_rate=125301
nb_frames=567
nb_read_frames=N/A
nb_read_packets=N/A
TAG:creation_time=1970-01-01 00:00:00
TAG:language=und
TAG:handler_name=SoundHandler
[/STREAM]

This is the function I use to add a video stream. 这是我用来添加视频流的功能。 All the values that come from ptr-> are defined from outside, do those values have to be specific values to get the correct profile?: 来自ptr->的所有值都是从外部定义的,这些值必须是特定值才能获得正确的配置文件吗?:

static AVStream *add_video_stream( Cffmpeg_dll * ptr, AVFormatContext *oc, enum   CodecID codec_id )
{
AVCodecContext *c;
AVStream *st;  
AVCodec* codec;

// Get correct codec
codec = avcodec_find_encoder(codec_id);
if (!codec) {
    av_log(NULL, AV_LOG_ERROR, "%s","Video codec not found\n");
    exit(1);
}

// Create stream
st = avformat_new_stream(oc, codec);
if (!st) {
    av_log(NULL, AV_LOG_ERROR, "%s","Could not alloc stream\n");
    exit(1);
}

c = st->codec;

/* Get default values */
codec = avcodec_find_encoder(codec_id);
if (!codec) {
    av_log(NULL, AV_LOG_ERROR, "%s","Video codec not found (default values)\n");
    exit(1);
}
avcodec_get_context_defaults3(c, codec);

c->codec_id = codec_id;
c->codec_type = AVMEDIA_TYPE_VIDEO;

c->bit_rate = ptr->video_bit_rate;
av_log(NULL, AV_LOG_ERROR, " Bit rate: %i", c->bit_rate);

    c->qmin = ptr->qmin;
    c->qmax = ptr->qmax;
    c->me_method = ptr->me_method;
    c->me_subpel_quality = ptr->me_subpel_quality;
    c->i_quant_factor = ptr->i_quant_factor;
    c->qcompress = ptr->qcompress;
    c->max_qdiff = ptr->max_qdiff;

    // We need to set the level and profile to get videos that play (hopefully) on all platforms
    c->level = 30;
    c->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE;

c->width = ptr->dstWidth; 
c->height = ptr->dstHeight; 

c->time_base.den = ptr->fps;
c->time_base.num = 1;
c->gop_size = ptr->fps;
c->pix_fmt = STREAM_PIX_FMT;
c->max_b_frames = 0;

// some formats want stream headers to be separate
if(oc->oformat->flags & AVFMT_GLOBALHEADER)
    c->flags |= CODEC_FLAG_GLOBAL_HEADER;

return st;
}

Additional info: 附加信息:

As a reference video, I use the gizmo.mp4 that Mozilla serves as an example that plays on every platform/browser. 作为参考视频,我使用了Mozilla作为在每个平台/浏览器上播放的示例的gizmo.mp4。 It definitely has the "Constrained Baseline" profile, and definitely works on all our testing smartphones. 它绝对具有“约束基线”配置文件,绝对适用于我们所有的测试智能手机。 You can download it here. 您可以在这里下载。 Our self-created video doesn't work on all platforms and I'm convinced this is because of the profile. 我们自己创建的视频不适用于所有平台,我确信这是因为个人资料。

I am also using qt-faststart.exe to move the headers to the start of the file after creating the mp4, as this cannot be done in a good way in C++ directly. 我也在使用qt-faststart.exe在创建mp4后将标题移动到文件的开头,因为这不能直接在C ++中以良好的方式完成。 Could that be the problem? 这可能是问题吗?

Obviously, I am doing something wrong, but I don't know what it could be. 显然,我做错了什么 ,但我不知道它会是什么。 I'd be thankful for every hint ;) 我会感激每一个提示;)

I have the solution. 我有解决方案。 After spending some time and discussions in the ffmpeg bug tracker and browsing for profile setting examples, I finally figured out the solution. 在ffmpeg bug跟踪器中花了一些时间和讨论并浏览配置文件设置示例后,我终于找到了解决方案。

One needs to use av_opt_set(codecContext->priv_data, "profile", "baseline" (or any other desired profile), AV_OPT_SEARCH_CHILDREN) 需要使用av_opt_set(codecContext-> priv_data,“profile”,“baseline”(或任何其他所需的配置文件),AV_OPT_SEARCH_CHILDREN)

So in my case that would be: 所以在我的情况下,这将是:

Wrong: 错误:

// We need to set the level and profile to get videos that play (hopefully) on all platforms
c->level = 30;
c->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE;

Correct: 正确:

// Set profile to baseline
av_opt_set(c->priv_data, "profile", "baseline", AV_OPT_SEARCH_CHILDREN);

Completely unintuitive and contrary to the rest of the API usage, but that's ffmpeg philosophy. 完全不直观且与其他API使用相反,但这是ffmpeg哲学。 You don't need to understand it, you just need to understand how to use it ;) 你不需要理解它,你只需要了解如何使用它;)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM