前言:
现时咱们对“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.pcm4. 解封装关键代码如下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怎么读