我是靠谱客的博主 高大芹菜,最近开发中收集的这篇文章主要介绍手把手FFmpeg入门——视频解码+解封装,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

环境:

QT5.7   64位

 

目的:

将视频解码为PCM和PPM文件

 

 

基础:

有点杂,几乎不需要基础,能看英文文档就行

 

 

基本原理:

1.无非是:解协议->解封装->解码, 

这里没有协议层.

封装即各种文件格式,编码即文件内数据的存储格式

 

 

 

不到150行的代码:

#include <QDebug>
extern "C" {
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
}
struct Img
{
int w, h;
int pix_fmt;
uint8_t *data[4];
/*假装是4字节对齐*/
int linesize[4];
int bufsize;
};
struct Demuxe
{
AVFormatContext* fmtctx;
AVCodecContext* codec_ctx;
AVCodec* codec;
AVStream* st;
int st_idx;
char *filename;
FILE *fp;
};
static Img img;
static Demuxe audio;
static Demuxe video;
static Demuxe src;
int refcounted = 0;
int decode(AVPacket *pkt, AVFrame *frame)
{
int ret, got_frame, decoded = pkt->size;
if(pkt->stream_index == video.st_idx){
ret = avcodec_decode_video2(video.codec_ctx, frame, &got_frame, pkt);
if(ret<0) exit(1);
if(got_frame){
av_image_copy(img.data, img.linesize,
(const uint8_t**)frame->data, frame->linesize,
(AVPixelFormat)frame->format, frame->width, frame->height);
fwrite(img.data[0], img.bufsize, 1, video.fp);
}
}else if(pkt->stream_index == audio.st_idx){
ret = avcodec_decode_audio4(audio.codec_ctx, frame, &got_frame, pkt);
decoded = FFMIN(pkt->size, ret);
int size =
frame->nb_samples*av_get_bytes_per_sample((AVSampleFormat)frame->format);
fwrite(frame->extended_data[0], size, 1, audio.fp);
}
if(got_frame && refcounted)
av_frame_unref(frame);
return decoded;/*返回解码的字节数*/
}
int construct_context(int media_type, Demuxe *dst)
{
dst->st_idx = av_find_best_stream(src.fmtctx, (AVMediaType)media_type, -1, -1, NULL, 0);
dst->st = src.fmtctx->streams[dst->st_idx];
dst->codec = avcodec_find_decoder(dst->st->codecpar->codec_id);
dst->codec_ctx = avcodec_alloc_context3(dst->codec);
avcodec_parameters_to_context(dst->codec_ctx, dst->st->codecpar);
AVDictionary *dict;
av_dict_set(&dict, "refcounted", refcounted?"1":"0", 0);
return avcodec_open2(dst->codec_ctx, dst->codec, &dict);
}
int main()
{
int ret =-1;
src.filename = "D:/fmt_mkv.mkv";
audio.filename = "D:/audiomp4.pcm";
video.filename = "D:/videomp4.ppm";
src.fmtctx = avformat_alloc_context();
avformat_open_input(&src.fmtctx, src.filename, NULL, NULL);
avformat_find_stream_info(src.fmtctx, NULL);
if(!construct_context(AVMEDIA_TYPE_VIDEO, &video)){
video.fp = fopen(video.filename, "wb+");
img.w = video.codec_ctx->width;
img.h = video.codec_ctx->height;
img.pix_fmt = video.codec_ctx->pix_fmt;
ret = av_image_alloc(img.data, img.linesize, img.w, img.h, (AVPixelFormat)img.pix_fmt, 1);
if(ret < 0)
goto end;
else
img.bufsize = ret;
}
if(!construct_context(AVMEDIA_TYPE_AUDIO, &audio)){
audio.fp = fopen(audio.filename, "wb+");
}
//
解码
AVFrame *frame = av_frame_alloc();
AVPacket *pkt = av_packet_alloc();
qDebug() << "pkt.data=" << pkt->data << ", pkt.size=" << pkt->size;
while(av_read_frame(src.fmtctx, pkt)>=0){
decode(pkt, frame);
}
//
根据dump的格式就可以用ffplay查看数据
av_dump_format(src.fmtctx, 0, src.filename, 0);
end:
avformat_close_input(&src.fmtctx);
avcodec_free_context(&audio.codec_ctx);
avcodec_free_context(&video.codec_ctx);
if(audio.fp)
fclose(audio.fp);
if(video.fp)
fclose(video.fp);
av_frame_free(&frame);
av_free(img.data[0]);
}

 

 

 

注意:

1. 对于音频只能读取第一个通道的数据

2. 当启用帧的引用计数时,将拷贝decoder的缓冲区内容到frame的ref buf中,所以不用了以后一定要记得用av_frame_unref取引用

3. 文档上说必须要av_image_copy视频帧数据,因为raw格式是非字节对齐的。

因此这里猜想在ffmpeg的image里存储方式是字节对齐的,linesize和实际大小不一样。

切勿盲目手动操作img数据

4. 使用ffplay播放视频命令如下:

ffplay -f rawvideo -pix_fmt yuv420p -video_size 1280x720 D:/videomp4.ppm

ffplay -f 视频格式 -pix_fmt 像素格式 -video_size  宽x高      路径

播放音频命令:

ffplay -f   f32le          -ac     1           -ar     48000        D:/audiomp4.pcm

ffplay -f 音频格式    -ac  通道数    -ar     采样率          路径 

 

就上面这种格式的电影《东方不败》

Input #0, matroska,webm, from 'D:/fmt_mkv.mkv':

Metadata:

encoder : libebml v1.2.3 + libmatroska v1.3.0

creation_time : 2017-05-06T06:43:21.000000Z

Duration: 01:47:36.32, start: 0.000000, bitrate: 1967 kb/s

Stream #0:0: Video: h264 (High), yuv420p(progressive), 1280x720 [SAR 1:1 DAR 16:9], 24 fps, 24 tbr, 1k tbn, 48 tbc (default)

Stream #0:1: Audio: aac (LC), 48000 Hz, stereo, fltp (default)

Stream #0:2: Audio: aac (LC), 48000 Hz, stereo, fltp

 

解码以后的纯数据格式视频(166GB),音频(单通道990MB),

而h264编码下mkv文件的总大小只有1.47GB,压缩率10倍以上,可以说很nice

但解码时间也长,大约用时20min

 

转载于:https://www.cnblogs.com/dusty-cjh/p/9367012.html

最后

以上就是高大芹菜为你收集整理的手把手FFmpeg入门——视频解码+解封装的全部内容,希望文章能够帮你解决手把手FFmpeg入门——视频解码+解封装所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部