MPEG 解封装
因为工作需求,需要将MP4/MKV中的H264/H265裸流给拉出来单独保存。
网上搜索以后前人已经有了一些探索,比如:
最简单的基于FFmpeg的封装格式处理:视音频分离器简化版
从flv文件中提取h264码流(使用av_bsf_send_packet和av_bsf_receive_packet)
流程
大概流程就是拆包,拿到一帧帧的AVPacket
以后转存到文件中就行,但测试发现MP4和MKV中的packet没有SPS和PPS头,所以我们就需要使用h264_mp4toannexb
和hevc_mp4toannexb
这两个bitstream filter来对packet进行处理。
h264_mp4toannexb
@section h264_mp4toannexb
Convert an H.264 bitstream from length prefixed mode to start code
prefixed mode (as defined in the Annex B of the ITU-T H.264
specification).
This is required by some streaming formats, typically the MPEG-2
transport stream format (muxer @code{mpegts}).
For example to remux an MP4 file containing an H.264 stream to mpegts
format with @command{ffmpeg}, you can use the command:
贴上h264_mp4toannexb的相关信息,简单来说就是将bitstream从length prefixed mode
转为 start code prefixed mode
,更为具体的信息可以参考H.264的附录B(annexb)
hevc_mp4toannexb
@section hevc_mp4toannexb
Convert an HEVC/H.265 bitstream from length prefixed mode to start code
prefixed mode (as defined in the Annex B of the ITU-T H.265
specification).
This is required by some streaming formats, typically the MPEG-2
transport stream format (muxer @code{mpegts}).
For example to remux an MP4 file containing an HEVC stream to mpegts
format with @command{ffmpeg}, you can use the command:
@example
ffmpeg -i INPUT.mp4 -codec copy -bsf:v hevc_mp4toannexb OUTPUT.ts
@end example
Please note that this filter is auto-inserted for MPEG-TS (muxer
@code{mpegts}) and raw HEVC/H.265 (muxer @code{h265} or
@code{hevc}) output formats.
H.265情况与H.264情况类似。
代码
主要使用av_bsf_send_packet
和av_bsf_receive_packet
来实现。
int mp4toannexb(DEMUXER_T *demuxer, AVPacket *pkt)
{
int ret;
ret = av_bsf_send_packet(demuxer->bsf_ctx, pkt);
if (ret < 0) {
printf("bsf send packet failed, errno: %d \n", ret);
return -1;
}
for(;;) {
ret = av_bsf_receive_packet(demuxer->bsf_ctx, pkt);
if (AVERROR_EOF == ret || AVERROR(EAGAIN) == ret) {
return 0;
}
if (ret < 0) {
printf("Could not receive packet, errno: %d \n", ret);
return -1;
}
}
}
int create_filter(DEMUXER_T *demuxer) {
int ret, codec_id = demuxer->ctx->streams[demuxer->video_index]->codecpar->codec_id;
const AVBitStreamFilter *filter;
switch (codec_id) {
case AV_CODEC_ID_H264:
filter = av_bsf_get_by_name("h264_mp4toannexb");
break;
case AV_CODEC_ID_HEVC:
filter = av_bsf_get_by_name("hevc_mp4toannexb");
break;
default:
printf("Unspport Codec id: %d \n", codec_id);
return -1;
}
if (NULL == filter) {
printf("Could not create filter \n");
return -1;
}
ret = av_bsf_alloc(filter, &demuxer->bsf_ctx);
if (ret < 0) {
printf("Could not alloc bitstream filter \n");
return -1;
}
// avcodec_parameters_from_context
ret = avcodec_parameters_copy(demuxer->bsf_ctx->par_in, demuxer->ctx->streams[demuxer->video_index]->codecpar);
if (ret < 0) {
printf("Parameter copy filed, errno: %d \n", ret);
goto L_ERR_PARAMETERS_COPY;
}
ret = av_bsf_init(demuxer->bsf_ctx);
if (ret < 0) {
printf("BSF init failed, errno: %d \n", ret);
goto L_ERR_BSF_INIT;
}
return 0;
L_ERR_BSF_INIT:
L_ERR_PARAMETERS_COPY:
av_bsf_free(&demuxer->bsf_ctx);
return ret;
}
完整的代码已上传GitHub(mpeg_demuxer)。
ffmpeg命令行操作
代码写完后发现其实一条命令就能解决问题,-_-||
ffmpeg -i h264.mp4 -c:v copy -bsf:v h264_mp4toannexb -an out.h264
写在最后
一般来说只有H.264和hevc有裸流,其他编码格式一般没有。
比如mkv格式的vp9,我们一般将它转为ivf格式。
ffmpeg -i input.mkv -c copy stream.vp9.ivf
ffmpeg -i input.webm -c copy stream.ivf