背景介绍
图片、视频、文本是我们日常生活中常见的形态,它们之所以被认为是图片、视频、文本,就是因为它们按照图片、视频、文本的封装规则去组装的。我们在接收一个文件的时候,不知道它们是图片、视频还是文本,但是我们会按照特定的规则是解析它们,如果能够解析成功,说明它们就是符合这样的规则的,我们就认定它们是图片、视频或者文本。
网络上很多文件类型,如果播放一个视频,你以为的是视频格式,但是给你的确实图片,你还用视频的解析规则是识别,那最终还能识别成功吗?
现在给一个网络链接:m.wdy5.com/vod/play/34… 其中视频的播放链接是:jx.shxcg.cn:4434/url/api/cur…
起始根据url的解析规则我们知道最终的播放url就是:zyxin.shxcg.cn:4434/upload/%E8%…
这个视频能不能播放呢?
我们使用ffprobe去探测一下:
[hls,applehttp @ 0x55647e5cb080] Opening 'https://sf1-ttcdn-tos.pstatp.com/obj/web.business.image/202104255d0de08d7b4d1ae04d66becc' for reading
[hls,applehttp @ 0x55647e5cb080] Could not find codec parameters for stream 0 (Video: png, none(pc)): unspecified size
Consider increasing the value for the 'analyzeduration' and 'probesize' options
Input #0, hls,applehttp, from 'https://zyxin.shxcg.cn:4434/upload/%E8%89%B2%E6%88%92.2007.BD1080p.m3u8':
Duration: 02:38:15.07, bitrate: 0 kb/s
Program 0
Metadata:
variant_bitrate : 0
Stream #0:0: Video: png, none(pc), 25 tbr, 25 tbn, 25 tbc
Metadata:
variant_bitrate : 0
复制代码
这个M3U8视频中只有一个视频流信息,而且这个视频流信息竟然是png格式,确实令人匪夷所思。如果真是这种格式肯定会播放失败的。
问题初探
我们再深入一点,既然是一个M3U8文件,那我们分析一下具体的分片文件吧。这是M3U8文件的第一个分片:sf1-ttcdn-tos.pstatp.com/obj/web.bus…
从这个url来看,似乎服务器有意让你将这个url识别为图片文件,很奇怪吧。
干脆我将这个文件下载下来。下面的video_0.ts就是这个文件。
我先用EasyICE识别一下,EasyICE是一个TS分析的工具,可以分析TS文件的基本信息和码流信息,很有帮助。
用这个识别工具探测发现这个文件确实是视频文件啊,视频流和音频流的信息能够识别的清清楚楚。
不死心。直接打开这个video_0.ts看看二进制文件信息吧。
发现这个文件的头部直接使用PNG的头部封装的,也就是说,服务器直接想让你识别成功PNG的格式,我们先不管服务器为什么要这么做,现在我们已经知道了根本原因,就是服务器下发了一个”PNG图片”,然后我们当成了“视频”,最后我们还没有识别成功,那能不能播放成功了?
我们现在的逻辑是使用ExoPlayer和Ijkplayer协同使用,互相辅助补充,共同解决播放问题,通过这样的协同,可以最大程度的解决播放失败的问题。
我直接去掉PNG头部信息,然后再利用ffprobe解析,得到的结果如下:
Input #0, mpegts, from 'error.ts':
Duration: 00:00:10.43, start: 1.566833, bitrate: 3281 kb/s
Program 1
Metadata:
service_name : ?domp4电影
service_provider: FFmpeg
Stream #0:0[0x100]: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 23.98 fps, 23.98 tbr, 90k tbn, 47.95 tbc
Stream #0:1[0x101](und): Audio: aac (HE-AAC) ([15][0][0][0] / 0x000F), 48000 Hz, 7.1, fltp, 255 kb/s
复制代码
ExoPlayer播放失败剖析
我们将jx.shxcg.cn:4434/url/api/cur… 放入ExoPlayer中,验证是否能够播放?
第一次尝试
结果是报错:
2021-05-20 10:07:19.756 17835-19205/com.jeffmony.videodemo E/playersdk.ExoPlayerImplInternal: Source error.
com.google.android.exoplayer2.source.UnrecognizedInputFormatException: None of the available extractors (Mp4Extractor, FragmentedMp4Extractor, TsExtractor, FlvExtractor, Mp3Extractor, MatroskaExtractor, AdtsExtractor, Ac3Extractor, OggExtractor, PsExtractor, WavExtractor, AmrExtractor) could read the stream.
at com.google.android.exoplayer2.source.ExtractorMediaPeriod$ExtractorHolder.selectExtractor(ExtractorMediaPeriod.java:1041)
at com.google.android.exoplayer2.source.ExtractorMediaPeriod$ExtractingLoadable.load(ExtractorMediaPeriod.java:949)
at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:324)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
2021-05-20 10:07:19.757 17835-17835/com.jeffmony.videodemo I/playersdk.ExoPlayerImpl: onPlayerError, error = com.google.android.exoplayer2.ExoPlaybackException, cause: com.google.android.exoplayer2.source.UnrecognizedInputFormatException: None of the available extractors (Mp4Extractor, FragmentedMp4Extractor, TsExtractor, FlvExtractor, Mp3Extractor, MatroskaExtractor, AdtsExtractor, Ac3Extractor, OggExtractor, PsExtractor, WavExtractor, AmrExtractor) could read the stream.
2021-05-20 10:07:19.757 17835-17835/com.jeffmony.videodemo E/playersdk.ExoPlayerImpl: isUnrecognizedInputFormat = mContentType =
复制代码
我们分析一下playersdk的逻辑,发现我们视频M3U8类型的逻辑有点问题。
exoplayer中Util.java中判断是不是M3U8类型的规则如下:
/**
* Makes a best guess to infer the type from a file name.
*
* @param fileName Name of the file. It can include the path of the file.
* @return The content type.
*/
@C.ContentType
public static int inferContentType(String fileName) {
fileName = Util.toLowerInvariant(fileName);
if (fileName.endsWith(".mpd")) {
return C.TYPE_DASH;
} else if (fileName.endsWith(".m3u8")) {
return C.TYPE_HLS;
} else if (fileName.matches(".*\\.ism(l)?(/manifest(\\(.+\\))?)?")) {
return C.TYPE_SS;
} else {
return C.TYPE_OTHER;
}
}
复制代码
也就是说当前url的fileName后缀是不是.m3u8来判断是不是M3U8类型。jx.shxcg.cn:4434/url/api/cur… 的fileName是curl.php,显然和M3U8没有任何关系。
这样判断M3U8类型是不是符合规则的?
我们先按下不表示,我们看一下Android系统中是如何判断M3U8类型的。
MediaPlayer中识别是不是M3U8类型的判断在/frameworks/av/media/libmediaplayerservice/MediaPlayerFactory.cpp文件中:
172 virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
173 const char* url,
174 float curScore) {
175 static const float kOurScore = 0.8;
176
177 if (kOurScore <= curScore)
178 return 0.0;
179
180 if (!strncasecmp("http://", url, 7)
181 || !strncasecmp("https://", url, 8)
182 || !strncasecmp("file://", url, 7)) {
183 size_t len = strlen(url);
184 if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) {
185 return kOurScore;
186 }
187
188 if (strstr(url,"m3u8")) {
189 return kOurScore;
190 }
191
192 if ((len >= 4 && !strcasecmp(".sdp", &url[len - 4])) || strstr(url, ".sdp?")) {
193 return kOurScore;
194 }
195 }
196
197 if (!strncasecmp("rtsp://", url, 7)) {
198 return kOurScore;
199 }
200
201 return 0.0;
202 }
复制代码
在188行可以看到只有url中包含m3u8就认为这是一个m3u8类型,大家可能会说这样的判断是不是太草率了,系统代码也能这么写?标准就是标准,你既然拿到了播放器中播放,我们就要尽可能放大条件。
ExoPlayer中的判断条件太紧了,导致第一关就判断失败了。我们还是按照标准放宽条件。
第二次尝试
修改ExoPlayer中代码,让jx.shxcg.cn:4434/url/api/cur… 被识别成为M3U8类型。
然后再去第二次尝试。还是出现报错:
2021-05-20 10:25:24.365 17835-20242/com.jeffmony.videodemo E/playersdk.ExoPlayerImplInternal: Internal runtime error.
java.lang.IllegalStateException
at android.media.MediaCodec.native_queueInputBuffer(Native Method)
at android.media.MediaCodec.queueInputBuffer(MediaCodec.java:2450)
at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.feedInputBuffer(MediaCodecRenderer.java:780)
at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:590)
at com.google.android.exoplayer2.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:521)
at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:300)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:230)
at android.os.HandlerThread.run(HandlerThread.java:67)
2021-05-20 10:25:24.397 17835-20242/com.jeffmony.videodemo E/playersdk.ExoPlayerImplInternal: Stop failed.
java.lang.IllegalStateException
at android.media.MediaCodec.native_stop(Native Method)
at android.media.MediaCodec.stop(MediaCodec.java:2147)
at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.releaseCodec(MediaCodecRenderer.java:529)
at com.googler.android.exoplayer2.mediacodec.MediaCodecRenderer.onDisabled(MediaCodecRenderer.java:485)
at com.google.android.exoplayer2.audio.MediaCodecAudioRenderer.onDisabled(MediaCodecAudioRenderer.java:480)
at com.google.android.exoplayer2.BaseRenderer.disable(BaseRenderer.java:153)
at com.google.android.exoplayer2.ExoPlayerImplInternal.disableRenderer(ExoPlayerImplInternal.java:1006)
at com.google.android.exoplayer2.ExoPlayerImplInternal.resetInternal(ExoPlayerImplInternal.java:773)
at com.google.android.exoplayer2.ExoPlayerImplInternal.stopInternal(ExoPlayerImplInternal.java:734)
at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:352)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:230)
at android.os.HandlerThread.run(HandlerThread.java:67)
复制代码
看到上面的报错大家是不是一脸懵逼,怎么就codec异常了,什么额外的信息都没有,这时候不要慌,先看看系统的log,既然是codec异常吗,系统的ACodec肯定会有一些额外的信息的。
2021-05-20 10:25:24.291 17835-20266/com.jeffmony.videodemo I/ACodec: [OMX.qcom.video.decoder.avc]configureCodec AMessage(what = 'conf', target = 1) = {
Buffer csd-1 = {
00000000: 00 00 01 68 eb 8f 2c ...h..,
}
int32_t max-height = 1080
int32_t max-width = 1920
string mime = "video/avc"
int32_t width = 1920
int32_t priority = 0
int32_t rotation-degrees = 0
int32_t max-input-size = 1566720
int32_t height = 1080
Buffer csd-0 = {
00000000: 00 00 01 67 64 00 28 ac 2c a5 01 e0 08 9f 97 01 ...gd.(.,.......
00000010: 10 00 00 3e 90 00 0b b8 0e 00 00 03 00 f4 24 00 ...>..........$.
00000020: 00 1e 84 82 ef 2e 0a 00 ........
}
RefBase *native-window = 0x7b0521f000
}
2021-05-20 10:25:24.345 17835-20271/com.jeffmony.videodemo I/ACodec: [OMX.google.aac.decoder]configureCodec AMessage(what = 'conf', target = 4) = {
int32_t sample-rate = 24000
string mime = "audio/mp4a-latm"
int32_t channel-count = 0
int32_t priority = 0
Buffer csd-0 = {
00000000: 13 00 ..
}
}
2021-05-20 10:25:24.345 17835-20271/com.jeffmony.videodemo E/ACodec: setupRawAudioFormat: incorrect numChannels: 0
2021-05-20 10:25:24.346 1489-28983/? E/OMXNodeInstance: setConfig(0xed20a900:google.aac.decoder, ConfigPriority(0x6f800002)) ERROR: Undefined(0x80001001)
2021-05-20 10:25:24.346 17835-20271/com.jeffmony.videodemo I/ACodec: codec does not support config priority (err -2147483648)
2021-05-20 10:25:24.346 1489-1569/? E/OMXNodeInstance: getConfig(0xed20a900:google.aac.decoder, ConfigAndroidVendorExtension(0x6f100004)) ERROR: Undefined(0x80001001)
2021-05-20 10:25:24.347 17835-20271/com.jeffmony.videodemo W/ExtendedACodec: Failed to get extension for extradata parameter
2021-05-20 10:25:24.349 890-989/? D/DispSync: getRefreshRate 0
2021-05-20 10:25:24.359 17835-17835/com.jeffmony.videodemo I/playersdk.ExoPlayerImpl: PlayerState ---> AudioDecodeStart
2021-05-20 10:25:24.360 1489-20272/? W/SoftAAC2: aacDecoder_ConfigRaw decoderErr = 0x0005
2021-05-20 10:25:24.360 17835-20271/com.jeffmony.videodemo E/ACodec: [OMX.google.aac.decoder] ERROR(0x80001001)
2021-05-20 10:25:24.361 17835-20271/com.jeffmony.videodemo E/ACodec: signalError(omxError 0x80001001, internalError -2147483648)
2021-05-20 10:25:24.361 17835-20271/com.jeffmony.videodemo E/MediaCodec: Codec reported err 0x80001001, actionCode 0, while in state 6
复制代码
视频流识别出来没有什么问题,但是音频流识别出来问题就大了,channel-count为0,sample-rate 竟然是24000,和我们之前用EasyICE软件识别出来的差距比较大。大家可以向上找一下,看看之前识别出来的EasyICE是什么结果。
而且系统也报错了:setupRawAudioFormat: incorrect numChannels: 0,说明这种情况下channel-count为0也是有问题的,说明ExoPlayer这儿的识别是有问题的。
接下来就要跟踪ExoPlayer系统源码了,Debug跟着系统的源码一步步走,发现最终识别Audio信息地方在AdtsReader.java中的parseAdtsHeader方法中。
/**
* Parses the sample header.
*/
private void parseAdtsHeader() throws ParserException {
adtsScratch.setPosition(0);
if (!hasOutputFormat) {
int audioObjectType = adtsScratch.readBits(2) + 1;
if (audioObjectType != 2) {
// The stream indicates AAC-Main (1), AAC-SSR (3) or AAC-LTP (4). When the stream indicates
// AAC-Main it's more likely that the stream contains HE-AAC (5), which cannot be
// represented correctly in the 2 bit audio_object_type field in the ADTS header. In
// practice when the stream indicates AAC-SSR or AAC-LTP it more commonly contains AAC-LC or
// HE-AAC. Since most Android devices don't support AAC-Main, AAC-SSR or AAC-LTP, and since
// indicating AAC-LC works for HE-AAC streams, we pretend that we're dealing with AAC-LC and
// hope for the best. In practice this often works.
// See: https://github.com/google/ExoPlayer/issues/774
// See: https://github.com/google/ExoPlayer/issues/1383
LogEx.w(TAG, "Detected audio object type: " + audioObjectType + ", but assuming AAC LC.");
audioObjectType = 2;
}
int sampleRateIndex = adtsScratch.readBits(4);
adtsScratch.skipBits(1);
int channelConfig = adtsScratch.readBits(3);
byte[] audioSpecificConfig = CodecSpecificDataUtil.buildAacAudioSpecificConfig(
audioObjectType, sampleRateIndex, channelConfig);
Pair<Integer, Integer> audioParams = CodecSpecificDataUtil.parseAacAudioSpecificConfig(
audioSpecificConfig);
Format format = Format.createAudioSampleFormat(formatId, MimeTypes.AUDIO_AAC, null,
Format.NO_VALUE, Format.NO_VALUE, audioParams.second, audioParams.first,
Collections.singletonList(audioSpecificConfig), null, 0, language);
// In this class a sample is an access unit, but the MediaFormat sample rate specifies the
// number of PCM audio samples per second.
sampleDurationUs = (C.MICROS_PER_SECOND * 1024) / format.sampleRate;
output.format(format);
hasOutputFormat = true;
} else {
adtsScratch.skipBits(10);
}
adtsScratch.skipBits(4);
int sampleSize = adtsScratch.readBits(13) - 2 /* the sync word */ - HEADER_SIZE;
if (hasCrc) {
sampleSize -= CRC_SIZE;
}
setReadingSampleState(output, sampleDurationUs, 0, sampleSize);
}
复制代码
这个方法的主要作用是识别Atds格式的头部信息,头部信息中有channel-count和sample-rate信息,但是audioObjectType识别是有问题的,为什么总是2,我对源码的这儿是有存疑的。
int audioObjectType = adtsScratch.readBits(2) + 1;
if (audioObjectType != 2) {
// The stream indicates AAC-Main (1), AAC-SSR (3) or AAC-LTP (4). When the stream indicates
// AAC-Main it's more likely that the stream contains HE-AAC (5), which cannot be
// represented correctly in the 2 bit audio_object_type field in the ADTS header. In
// practice when the stream indicates AAC-SSR or AAC-LTP it more commonly contains AAC-LC or
// HE-AAC. Since most Android devices don't support AAC-Main, AAC-SSR or AAC-LTP, and since
// indicating AAC-LC works for HE-AAC streams, we pretend that we're dealing with AAC-LC and
// hope for the best. In practice this often works.
// See: https://github.com/google/ExoPlayer/issues/774
// See: https://github.com/google/ExoPlayer/issues/1383
LogEx.w(TAG, "Detected audio object type: " + audioObjectType + ", but assuming AAC LC.");
audioObjectType = 2;
}
复制代码
那应该是多少,回到“问题初探”章节,audio的类型应该是AAC-HE类型,什么类型都直接赋给2我觉得有点欠妥。但还是要深入分析一下,还好真的不是我一个人遇到这样的问题。
找一下ExoPlayer之前的issue:github.com/google/ExoP… ,这个issue中最后提出的一个方法是采用FFmpeg拓展来解决识别audio 7.1的识别问题。很显然google的开发者也没有找到很好的修复方案。
这一块需要我们努力加油了,如果能解决现有的ExoPlayer问题,成为一个ExoPlayer committer,也是很棒的体验,给我们提一个要求哈。
IjkPlayer播放失败剖析
既然ExoPlayer的实现难度比较大,那我们还是使用IjkPlayer去补充解决这个问题。下面分析一下IjkPlayer是否能播放jx.shxcg.cn:4434/url/api/cur…
使用IjkPlayer播放视频的视频报如下的问题:
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo W/IJKMEDIA: Could not find codec parameters for stream 0 (Video: png, none(pc)): unspecified size
Consider increasing the value for the 'analyzeduration' and 'probesize' options
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: max_frame_duration: 10.000
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: Input #0, hls,applehttp, from 'https://jx.shxcg.cn:4434/url/api/curl.php?url=https://zyxin.shxcg.cn:4434/upload/%E8%89%B2%E6%88%92.2007.BD1080p.m3u8':
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: Duration:
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: 02:38:15.07
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: , bitrate:
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: N/A
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: Program 0
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: Metadata:
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: variant_bitrate :
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: 0
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: Stream #0:0
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: : Video: png, none(pc)
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: ,
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: 25 tbr,
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: 25 tbn,
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: 25 tbc
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: Metadata:
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: variant_bitrate :
2021-05-20 10:59:40.968 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: 0
2021-05-20 10:59:40.969 21218-21313/com.jeffmony.playerdemo I/IJKMEDIA: VideoCodec: avcodec, png
2021-05-20 10:59:40.969 21218-21313/com.jeffmony.playerdemo W/IJKMEDIA: fps: 25.000000 (normal)
复制代码
这个问题乍看一下还以为是ffmpeg没有接入png类型格式了,通过我们上面对ExoPlayer的分析,我们刚开始就能确定,肯定是识别分片的格式出现问题。
经过一顿操作,找到IjkPlayer中识别视频类型的地方:./android/contrib/ffmpeg-armv7a/libavformat/format.c中的av_probe_input_format3函数。
AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened,
int *score_ret)
{
AVProbeData lpd = *pd;
const AVInputFormat *fmt1 = NULL;
AVInputFormat *fmt = NULL;
int score, score_max = 0;
void *i = 0;
const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE];
enum nodat {
NO_ID3,
ID3_ALMOST_GREATER_PROBE,
ID3_GREATER_PROBE,
ID3_GREATER_MAX_PROBE,
} nodat = NO_ID3;
if (!lpd.buf)
lpd.buf = (unsigned char *) zerobuffer;
if (lpd.buf_size > 10 && ff_id3v2_match(lpd.buf, ID3v2_DEFAULT_MAGIC)) {
int id3len = ff_id3v2_tag_len(lpd.buf);
if (lpd.buf_size > id3len + 16) {
if (lpd.buf_size < 2LL*id3len + 16)
nodat = ID3_ALMOST_GREATER_PROBE;
lpd.buf += id3len;
lpd.buf_size -= id3len;
} else if (id3len >= PROBE_BUF_MAX) {
nodat = ID3_GREATER_MAX_PROBE;
} else
nodat = ID3_GREATER_PROBE;
}
while ((fmt1 = av_demuxer_iterate(&i))) {
if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2"))
continue;
score = 0;
if (fmt1->read_probe) {
score = fmt1->read_probe(&lpd);
if (score)
av_log(NULL, AV_LOG_TRACE, "Probing %s score:%d size:%d\n", fmt1->name, score, lpd.buf_size);
if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {
switch (nodat) {
case NO_ID3:
score = FFMAX(score, 1);
break;
case ID3_GREATER_PROBE:
case ID3_ALMOST_GREATER_PROBE:
score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1);
break;
case ID3_GREATER_MAX_PROBE:
score = FFMAX(score, AVPROBE_SCORE_EXTENSION);
break;
}
}
} else if (fmt1->extensions) {
if (av_match_ext(lpd.filename, fmt1->extensions))
score = AVPROBE_SCORE_EXTENSION;
}
if (av_match_name(lpd.mime_type, fmt1->mime_type)) {
if (AVPROBE_SCORE_MIME > score) {
av_log(NULL, AV_LOG_DEBUG, "Probing %s score:%d increased to %d due to MIME type\n", fmt1->name, score, AVPROBE_SCORE_MIME);
score = AVPROBE_SCORE_MIME;
}
}
if (score > score_max) {
score_max = score;
fmt = (AVInputFormat*)fmt1;
} else if (score == score_max)
fmt = NULL;
}
if (nodat == ID3_GREATER_PROBE)
score_max = FFMIN(AVPROBE_SCORE_EXTENSION / 2 - 1, score_max);
*score_ret = score_max;
return fmt;
}
复制代码
我们这儿简单介绍一下IjkPlayer中是如何得到视频真正的封装格式的。在一个while循环中,将当前的视频数据放入,经过每种封装格式计算出一个score, score越大说明和当前的封装格式越匹配。
很显然,如果被识别为png格式,肯定是通过png得到的score最大。
但是我们播放的是视频,针对图片格式,不应该播放图片类型,其实我们在IjkPlayer中有很多剔除image的代码,所以得到最保险的方式应该是剔除png类型。
下面是我修复之后的代码:
if (score > score_max) {
if (!strcmp(fmt1->name, "png_pipe")) continue;
score_max = score;
fmt = (AVInputFormat*)fmt1;
} else if (score == score_max)
fmt = NULL;
复制代码
加了一行代码:if (!strcmp(fmt1->name, “png_pipe”)) continue;
思考
- 发现问题–>分析问题—>解决问题,其中比较重要的环节是“分析问题”,分析问题务求要分析透彻,从根本的角度解析问题,然后找到影响最小的解决方案。
- 能够在ExoPlayer上解决这个问题,给大家留下一个思考,希望集合打击的智慧在ExoPlayer上解决这个问题。