龙空技术网

音视频解封装格式详解

音视频开发老舅 210

前言:

现时咱们对“fflush怎么读”大概比较关注,兄弟们都需要剖析一些“fflush怎么读”的相关文章。那么小编同时在网摘上汇集了一些对于“fflush怎么读””的相关内容,希望大家能喜欢,你们一起来了解一下吧!

1. muxer 和 demuxer 分别是什么?

muxer:是封装的意思(像 FLV、MP4 都是既有音频流也有视频流的封装体) demuxer:是解封装的意思(比如把 MP4 解封装成 YUV 和 PCM 数据)

2. 如何需要播放一个封装格式的 MP4文件,主思路是什么?(必须要能说出四大步骤)3. 如何使用命令行,从 MP4 中提取 YUV 和 PCM?(作为后续代码提取的参照物)

ffmpeg -c:v h264 -c:a aac -i out_dog.mp4 out_dog_cmd_yuv -f f32le out_dog_cmd.pcm
4. 解封装关键代码如下ffmpegs.h

C++音视频开发学习资料:点击领取→音视频开发(资料文档+视频教程+面试题)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)

#ifndef FFMPEGS_H#define FFMPEGS_H#include <QFile>extern "C" {#include <libavcodec/avcodec.h>#include <libavformat/avformat.h>#include <libavutil/avutil.h>}typedef struct { const char *filename; int sampleRate; AVSampleFormat sampleFmt; int chLayout;} AudioDecodeSpec;typedef struct { const char *filename; int width; int height; AVPixelFormat pixFmt; int fps;} VideoDecodeSpec;class FFmpegs {public: FFmpegs(); void demux(const char *inFilename,            AudioDecodeSpec &aOut,            VideoDecodeSpec &vOut);  ///解封装上下文 AVFormatContext *_fmtCtx = nullptr;  ///解码上下文 AVCodecContext *_aDecodeCtx = nullptr, *_vDecodeCtx = nullptr;  ///流索引 int _aStreamIdx = 0, _vStreamIdx = 0; // 文件 QFile _aOutFile, _vOutFile; ///函数参数 AudioDecodeSpec *_aOut = nullptr; VideoDecodeSpec *_vOut = nullptr; ///存放解码前的数据//    AVPacket *_pkt = nullptr; ///存放解码后的数据 AVFrame *_frame = nullptr; ///存放一帧解码图片的缓冲区 uint8_t *_imgBuf[4] = {nullptr}; int _imgLinesizes[4] = {0}; int _imgSize = 0; ///每一个音频样本帧(包含所有声道)大小 int _sampleFrameSize = 0; ///每一个音频样本的大小(单声道) int _sampleSize = 0;  int initVideoInfo(); int initAudioInfo(); int initDecoder(AVCodecContext **decodeCtx,                 int *streamIdx,                 AVMediaType type); int decode(AVCodecContext *decodeCtx,            AVPacket *pkt,            void (FFmpegs::*func)()); void writeVideoFrame(); void writeAudioFrame();  };#endif // FFMPEGS_H

ffmpegs.m

#include "ffmpegs.h"#include <QDebug>#include <QFile>extern "C" {#include <libavutil/imgutils.h>}#define ERROR_BUF \ char errbuf[1024]; \ av_strerror(ret, errbuf, sizeof (errbuf));#define END(func) \ if (ret < 0) { \     ERROR_BUF; \     qDebug() << #func << "error" << errbuf; \     goto end; \ }#define RET(func) \ if (ret < 0) { \     ERROR_BUF; \     qDebug() << #func << "error" << errbuf; \     return ret; \ }FFmpegs::FFmpegs() {}void FFmpegs::demux(const char *inFilename,                 AudioDecodeSpec &aOut,                 VideoDecodeSpec &vOut) { ///保留参数 _aOut = &aOut; _vOut = &vOut;  AVPacket *pkt = nullptr;  ///返回结果 int ret = 0;  ///创建解封装上下文、打开文件 ret = avformat_open_input(&_fmtCtx, inFilename, nullptr, nullptr); END(avformat_open_input);  ///检索流信息 ret = avformat_find_stream_info(_fmtCtx, nullptr); END(avformat_find_stream_info);  ///打印流信息到控制台 av_dump_format(_fmtCtx, 0, inFilename, 0); fflush(stderr);  ///初始化音频信息 ret = initAudioInfo(); if (ret < 0) {     goto end; }  ///初始化视频信息 ret = initVideoInfo(); if (ret < 0) {     goto end; }  ///初始化 frame _frame = av_frame_alloc(); if (!_frame) {     qDebug() << "av_frame_alloc error";     goto end; }  ///初始化 pkt pkt = av_packet_alloc(); pkt->data = nullptr; pkt->size = 0;  ///从输入文件中读取数据 while (av_read_frame(_fmtCtx, pkt) == 0) {     if (pkt->stream_index == _aStreamIdx) { ///读取到的是音频数据         ret = decode(_aDecodeCtx, pkt, &FFmpegs::writeAudioFrame);     } else if (pkt->stream_index == _vStreamIdx) {///读取到的是视频数据         ret = decode(_vDecodeCtx, pkt, &FFmpegs::writeVideoFrame);     }     ///释放 pkt 内部指针指向的一些额外内存     av_packet_unref(pkt);     if (ret < 0) {         goto end;     } }  ///刷新缓冲区 decode(_aDecodeCtx, nullptr, &FFmpegs::writeAudioFrame); decode(_vDecodeCtx, nullptr, &FFmpegs::writeVideoFrame); end: _aOutFile.close(); _vOutFile.close(); avcodec_free_context(&_aDecodeCtx); avcodec_free_context(&_vDecodeCtx); avformat_close_input(&_fmtCtx); av_frame_free(&_frame); av_packet_free(&pkt); av_freep(&_imgBuf[0]);}///初始化音频信息int FFmpegs::initAudioInfo() { ///初始化解码器 int ret = initDecoder(&_aDecodeCtx, &_aStreamIdx, AVMEDIA_TYPE_AUDIO); RET(initDecoder);  ///打开文件 _aOutFile.setFileName(_aOut->filename); if (!_aOutFile.open(QFile::WriteOnly)) {     qDebug() << "file open error" << _aOut->filename;     return -1; }  ///保存音频参数 _aOut->sampleRate = _aDecodeCtx->sample_rate; _aOut->sampleFmt = _aDecodeCtx->sample_fmt; _aOut->chLayout = _aDecodeCtx->channel_layout;  // 音频样本帧的大小 _sampleSize = av_get_bytes_per_sample(_aOut->sampleFmt); _sampleFrameSize = _sampleSize * _aDecodeCtx->channels;  return 0; }///初始化视频信息int FFmpegs::initVideoInfo() { ///初始化解码器 int ret = initDecoder(&_vDecodeCtx, &_vStreamIdx, AVMEDIA_TYPE_VIDEO); RET(initDecoder);  ///打开文件 _vOutFile.setFileName(_vOut->filename); if (!_vOutFile.open(QFile::WriteOnly)) {     qDebug() << "file open error" << _vOut->filename;     return -1; }  ///保存视频参数 _vOut->width = _vDecodeCtx->width; _vOut->height = _vDecodeCtx->height; _vOut->pixFmt = _vDecodeCtx->pix_fmt;  ///帧率 AVRational framerate = av_guess_frame_rate(_fmtCtx,                                            _fmtCtx->streams[_vStreamIdx],                                            nullptr);  _vOut->fps = framerate.num / framerate.den;  ///创建用于存放一帧解码图片的缓冲区 ret = av_image_alloc(_imgBuf, _imgLinesizes,                      _vOut->width, _vOut->height,                      _vOut->pixFmt, 1); RET(av_image_alloc); _imgSize = ret;  return 0;}///初始化解码器int FFmpegs::initDecoder(AVCodecContext **decodeCtx,                      int *streamIdx,                      AVMediaType type) { ///根据 type 寻找最合适的流信息 ///返回值是流索引 int ret = av_find_best_stream(_fmtCtx, type, -1, -1, nullptr, 0); RET(av_find_best_stream);  ///检验流 *streamIdx = ret; AVStream *stream = _fmtCtx->streams[*streamIdx]; if (!stream) {     qDebug() << "stream is empty";     return -1; }  ///为当前流找到合适的解码器 AVCodec *decoder = avcodec_find_decoder(stream->codecpar->codec_id); if (!decoder) {     qDebug() << "decoder not found" << stream->codecpar->codec_id;     return -1; }  /// 初始化解码上下文 *decodeCtx = avcodec_alloc_context3(decoder); if (!decodeCtx) {     qDebug() << "avcodec_alloc_context3 error";     return -1; }  ///从流中拷贝参数到解码上下文中 ret = avcodec_parameters_to_context(*decodeCtx, stream->codecpar); RET(avcodec_parameters_to_context);  ///打开解码器 ret = avcodec_open2(*decodeCtx, decoder, nullptr); RET(avcodec_open2);  return 0;}int FFmpegs::decode(AVCodecContext *decodeCtx,                 AVPacket *pkt,                 void (FFmpegs::*func)()) { ///发送压缩数据到解码器 int ret = avcodec_send_packet(decodeCtx, pkt); RET(avcodec_send_packet);  while (true) {     ///获取解码后的数据     ret = avcodec_receive_frame(decodeCtx, _frame);     if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {         return 0;     }     RET(avcodec_receive_frame)          ///执行写入文件的代码     (this->*func)(); }}void FFmpegs::writeVideoFrame() { ///拷贝 frame 的数据到 _imgBuf 缓冲区 av_image_copy(_imgBuf, _imgLinesizes,               (const uint8_t **)_frame->data, _frame->linesize,               _vOut->pixFmt, _vOut->width, _vOut->height); ///将缓冲区的数据写入到文件 _vOutFile.write((char *)_imgBuf[0], _imgSize);}void FFmpegs::writeAudioFrame() { ///libfdk_aac 解码器,解码出来的 PCM 格式:s16 ///aac 解码器,解码出来的 PCM 格式:ftlp  ///LLLL RRRR DDDD FFFF  if (av_sample_fmt_is_planar(_aOut->sampleFmt)) { ///planar     ///外层循环:每一个声道的样本数     ///si = sample index     for (int si = 0; si < _frame->nb_samples; si++) {         ///内层循环:有多少个声道         ///ci = channel index         for (int ci = 0; ci < _aDecodeCtx->channels; ci++) {             char *begin = (char*)(_frame->data[ci] + si * _sampleSize);             _aOutFile.write(begin, _sampleSize);         }     } } else { ///非 planar     _aOutFile.write((char *)_frame->data[0], _frame->nb_samples * _sampleFrameSize); } }

标签: #fflush怎么读