解码mp4文件分别存储为pcm,yuv文件

// 解码分别写入对应文件
#include "myLog.h"
#include <iostream>extern "C"
{
#include <libavformat\avformat.h>
#include <libavutil\avutil.h>
#include <libavcodec\avcodec.h>
#include <libavutil\imgutils.h>
#include <libavutil\samplefmt.h>
}
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avcodec.lib")class Decoder
{
public:Decoder(std::string src_filename, std::string dst_audio_filename, std::string dst_video_filename);~Decoder();// 解封装int demuxer();// 解码int decodec();// 打印音视频播放格式void print_audio_play_str();
private:// 打开解码器int open_decodec();// 解码pktint decode_pkt(AVCodecContext* codec_ctx, AVPacket* pkt);// 输出视频帧int output_video_frame(AVFrame* frame);// 输出音频帧int output_audio_frame(AVFrame* frame);// 获取音频格式int get_format_from_sample_fmt(std::string& fmtStr, enum AVSampleFormat sample_fmt);
private:AVFormatContext* ifmt_ctx_;					// 格式上下文AVCodecContext*  video_decode_ctx_;			// 视频解码器上下文AVCodecContext*  audio_decode_ctx_;			// 音频解码器上下文int video_idx_;								// 视频索引						AVStream* video_stream_;					// 视频流int audio_idx_;								// 音频索引AVStream* audio_stream_;					// 音频流std::string src_filename_;					// 输入文件路径std::string dst_audio_filename_;			// 输出音频pcm文件路径std::string dst_video_filename_;			// 输出视频yuv路径							FILE* dst_video_fp_;FILE* dst_audio_fp_;AVFrame* frame_;uint8_t* video_dst_data_[4];				// 记录video一行数据int video_dst_buf_size_;					// 分配的真实buf大小 int linesize_[4];							// 每行数据大小(YUV RGB 第四个为保留字段)
};Decoder::Decoder(std::string src_filename, std::string dst_audio_filename, std::string dst_video_filename)
: ifmt_ctx_(nullptr)
, video_decode_ctx_(nullptr)
, audio_decode_ctx_(nullptr)
, video_stream_(nullptr)
, audio_stream_(nullptr)
, dst_video_fp_(nullptr)
, dst_audio_fp_(nullptr)
, frame_(nullptr)
, src_filename_(src_filename)
, dst_audio_filename_(dst_audio_filename)
, dst_video_filename_(dst_video_filename)
{for (int i = 0; i < 4; i++){video_dst_data_[i] = NULL;}
}Decoder::~Decoder()
{// 释放相关资源if (ifmt_ctx_){avformat_close_input(&ifmt_ctx_);}if (audio_decode_ctx_){avcodec_free_context(&audio_decode_ctx_);}if (video_decode_ctx_){avcodec_free_context(&video_decode_ctx_);}if (dst_audio_fp_){fclose(dst_audio_fp_);}	if (dst_video_fp_){fclose(dst_video_fp_);}if (frame_){av_frame_free(&frame_);}if (video_dst_data_){av_free(video_dst_data_[0]);	// 释放数据指针(其它三个指针无需释放) data[0]指向缓冲区开始位置,data[1] - data[3]也是指向data[0]的数据指针}
}int Decoder::demuxer()
{// 1. 打开文件int nRet = avformat_open_input(&ifmt_ctx_, src_filename_.c_str(), NULL, NULL);if (nRet < 0){LOG_WARNING("avformat_open_input error\n");return -1;}// 2. 查找流信息nRet = avformat_find_stream_info(ifmt_ctx_, NULL);if (nRet < 0){avformat_close_input(&ifmt_ctx_);LOG_WARNING("avformat_find_stream_info error\n");return -2;}// 3. 获取音视频流索引audio_idx_ = av_find_best_stream(ifmt_ctx_, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);video_idx_ = av_find_best_stream(ifmt_ctx_, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);return 0;
}int Decoder::open_decodec()
{// 打开音频解码器if (audio_idx_ >= 0){audio_stream_ = ifmt_ctx_->streams[audio_idx_];// 查找解码器AVCodec* audio_decodec = avcodec_find_decoder(audio_stream_->codecpar->codec_id);if (!audio_decodec){LOG_WARNING("avcodec_find_decoder error\n");return AVERROR(ENOMEM);}// 给解码上下文分配内存audio_decode_ctx_ = avcodec_alloc_context3(audio_decodec);if (!audio_decode_ctx_){LOG_WARNING("avcodec_alloc_context3 error\n");return AVERROR(ENOMEM);}// 拷贝解码参数到解码上下文int nRet = avcodec_parameters_to_context(audio_decode_ctx_, audio_stream_->codecpar);if (nRet < 0){LOG_WARNING("avcodec_parameters_to_context error\n");return -1;}// 打开解码器nRet = avcodec_open2(audio_decode_ctx_, audio_decodec, NULL);if (nRet < 0){LOG_WARNING("avcodec_open2 error\n");return -2;}dst_audio_fp_ = fopen(dst_audio_filename_.c_str(), "wb");if (!dst_audio_fp_){LOG_WARNING("fopen dst_audio_filename_ error\n");return -1;}}// 打开视频解码器if (video_idx_ >= 0){video_stream_ = ifmt_ctx_->streams[video_idx_];// 查找解码器AVCodec* video_decodec = avcodec_find_decoder(video_stream_->codecpar->codec_id);if (!video_decodec){LOG_WARNING("avcodec_find_decoder error\n");return AVERROR(ENOMEM);}// 给解码上下文分配内存video_decode_ctx_ = avcodec_alloc_context3(video_decodec);if (!video_decode_ctx_){LOG_WARNING("avcodec_alloc_context3 error\n");return AVERROR(ENOMEM);}// 拷贝解码参数到解码上下文int nRet = avcodec_parameters_to_context(video_decode_ctx_, video_stream_->codecpar);if (nRet < 0){LOG_WARNING("avcodec_parameters_to_context error\n");return -1;}// 打开解码器nRet = avcodec_open2(video_decode_ctx_,video_decodec, NULL);if (nRet < 0){LOG_WARNING("avcodec_open2 error\n");return -2;}video_dst_buf_size_ = av_image_alloc(video_dst_data_, linesize_,video_decode_ctx_->width, video_decode_ctx_->height, video_decode_ctx_->pix_fmt, 1);dst_video_fp_ = fopen(dst_video_filename_.c_str(), "wb");if (!dst_video_fp_){LOG_WARNING("fopen dst_video_filename_ error\n");return -1;}}
}int Decoder::decodec()
{// 打开解码器int nRet = open_decodec();if (nRet < 0){LOG_WARNING("open_decodec error\n");return -666;}AVPacket* pkt = av_packet_alloc();av_init_packet(pkt);pkt->data = NULL;pkt->size = 0;while (av_read_frame(ifmt_ctx_, pkt) >= 0){if (pkt->stream_index == audio_idx_)	// 音频包{nRet = decode_pkt(audio_decode_ctx_, pkt);}else if (pkt->stream_index == video_idx_) // 视频包{nRet = decode_pkt(video_decode_ctx_, pkt);}av_packet_unref(pkt);if (nRet < 0){break;}}// 冲刷解码器if (video_decode_ctx_){decode_pkt(video_decode_ctx_, NULL);}if (audio_decode_ctx_){decode_pkt(video_decode_ctx_, NULL);}return 0;
}int Decoder::decode_pkt(AVCodecContext* codec_ctx, AVPacket* pkt)
{int nRet = avcodec_send_packet(codec_ctx, pkt);if (nRet < 0){LOG_WARNING("avcodec_send_packet fflush codec\n");return -1;}// 一个包可能对应多个frameframe_ = av_frame_alloc();while (nRet >= 0){nRet = avcodec_receive_frame(codec_ctx, frame_);if (nRet < 0){if (nRet == AVERROR_EOF || nRet == AVERROR(EAGAIN))return 0;return nRet;}// 写数据if (codec_ctx->codec->type == AVMEDIA_TYPE_AUDIO){nRet = output_audio_frame(frame_);}else if (codec_ctx->codec->type == AVMEDIA_TYPE_VIDEO){nRet = output_video_frame(frame_);}av_frame_unref(frame_);if (nRet < 0)return nRet;}
}int Decoder::output_video_frame(AVFrame* frame)
{if (video_dst_buf_size_ < 0){LOG_WARNING("av_image_alloc error\n");return -2;}// 写入帧到文件if (frame->width != video_decode_ctx_->width ||frame->height != video_decode_ctx_->height ||frame->format != video_decode_ctx_->pix_fmt){LOG_WARNING("frame and video_decode_ctx_ error\n");return -3;}// 填充数据到 video_dst_dataav_image_copy(video_dst_data_, linesize_,(const uint8_t**)(frame->data), frame->linesize,video_decode_ctx_->pix_fmt, video_decode_ctx_->width, video_decode_ctx_->height);size_t write_video_size = fwrite(video_dst_data_[0], 1, video_dst_buf_size_, dst_video_fp_);fflush(dst_video_fp_);return 0;
}int Decoder::output_audio_frame(AVFrame* frame)
{// 计算一帧音频数据的bytessize_t a_sample_point_bytes = frame->nb_samples * av_get_bytes_per_sample((AVSampleFormat)frame->format);// 写入数据到文件fwrite(frame->extended_data[0], 1, a_sample_point_bytes, dst_audio_fp_);fflush(dst_audio_fp_);return 0;
}int Decoder::get_format_from_sample_fmt(std::string& fmtStr, enum AVSampleFormat sample_fmt)
{struct sample_fmt_entry {enum AVSampleFormat sample_fmt; std::string fmt_be;std::string fmt_le;} sample_fmt_entries[] = {{ AV_SAMPLE_FMT_U8, "u8", "u8" },{ AV_SAMPLE_FMT_S16, "s16be", "s16le" },{ AV_SAMPLE_FMT_S32, "s32be", "s32le" },{ AV_SAMPLE_FMT_FLT, "f32be", "f32le" },{ AV_SAMPLE_FMT_DBL, "f64be", "f64le" },};for (int i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) {struct sample_fmt_entry *entry = &sample_fmt_entries[i];if (sample_fmt == entry->sample_fmt) {fmtStr = AV_NE(entry->fmt_be, entry->fmt_le);return 0;}}fprintf(stderr, "sample format %s is not supported as output format\n", av_get_sample_fmt_name(sample_fmt));return -1;
}void Decoder::print_audio_play_str()
{if (audio_stream_) {enum AVSampleFormat sfmt = audio_decode_ctx_->sample_fmt;int n_channels = audio_decode_ctx_->channels;std::string fmtStr;if (av_sample_fmt_is_planar(sfmt))	// 是planar格式{const char *packed = av_get_sample_fmt_name(sfmt);LOG_INFO("Warning: the sample format the decoder produced is planar ""(%s). This example will output the first channel only.\n",packed ? packed : "?");sfmt = av_get_packed_sample_fmt(sfmt);n_channels = 1;}int ret = -1;if ((ret = get_format_from_sample_fmt(fmtStr, sfmt)) < 0)return;LOG_INFO("Play the output audio file with the command:\n""ffplay -f %s -ac %d -ar %d %s\n",fmtStr.c_str(), n_channels, audio_decode_ctx_->sample_rate,dst_audio_filename_.c_str());LOG_INFO("play video yuv:\nffplay -video_size %dx%d -f rawvideo -pixel_format yuv420p %s",video_decode_ctx_->width, video_decode_ctx_->height, dst_video_filename_.c_str());}
}int main()
{std::string src_filename = "./MediaFile/HTA_13s.mp4";	// input_5sstd::string dst_audio_filename = "./MediaFile/HTA_13s_decodec_audio.pcm"; // HTA_13s_decodec_audiostd::string dst_video_filename = "./MediaFile/HTA_13s_decodec_video.yuv"; // HTA_13s_decodec_videoDecoder decoder(src_filename, dst_audio_filename, dst_video_filename);// 解复用int nRet = decoder.demuxer();if (nRet < 0){LOG_WARNING("main demuxer error\n");return -666;}// 解码写入到文件nRet = decoder.decodec();if (nRet < 0){LOG_WARNING("main decodec error\n");return -777;}LOG_INFO("decode finish, write file success\n");decoder.print_audio_play_str();getchar();return 0;
}

注意:使用av_image_alloc为一个char* data[4]的数组分配内存时,其内部只是为data[0]分配内存,其余的data[1] - data[3]指针指向data[0]的某个位置(根据像素格式的不同)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/596658.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为ensp中高级acl (控制列表) 原理和配置命令 (详解)

作者主页&#xff1a;点击&#xff01; ENSP专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月6日23点18分 高级acl&#xff08;Access Control List&#xff09;是一种访问控制列表&#xff0c;可以根据数据包的源IP地址、目标IP地址、源端口、目标端口、协议…

多路转接-epoll/Reactor(2)

epoll 上次说到了poll&#xff0c;它存在效率问题&#xff0c;因此出现了改进的poll----epoll。 目前epoll是公认的效率最高的多路转接的方案。 快速了解epoll接口 epoll_create&#xff1a; 这个参数其实已经被废弃了。 这个值只要大于0就可以了。 这是用来创建一个epoll模…

应用性能分析工具CPU Profiler

简介 本文档介绍应用性能分析工具CPU Profiler的使用方法&#xff0c;该工具为开发者提供性能采样分析手段&#xff0c;可在不插桩情况下获取调用栈上各层函数的执行时间&#xff0c;并展示在时间轴上。 开发者可通过该工具查看TS/JS代码及NAPI代码执行过程中的时序及耗时情况…

torchvision中的数据集使用

torchvision中的数据集使用 使用和下载CIFAR10数据集 输出测试集中的第一个元素&#xff08;输出img信息和target&#xff09; 查看分类classes 打断点–>右键Debug–>找到classes 代码 import torchvisiontrain_set torchvision.datasets.CIFAR10(root"./data…

【PyQt5篇】使用QtDesigner添加控件和槽

文章目录 &#x1f354;使用QtDesigner进行设计&#x1f6f8;在代码中添加信号和槽 &#x1f354;使用QtDesigner进行设计 我们首先使用QtDesigner设计界面 得到代码login.ui <?xml version"1.0" encoding"UTF-8"?> <ui version"4.0&q…

基于Springboot中小企业设备管理系统设计与实现(论文+源码)_kaic

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&a…

html骨架以及常见标签

推荐一个网站mdn。 html语法 双标签&#xff1a;<标签 属性"属性值">内容</标签> 属性&#xff1a;给标签提供附加信息。大多数属性以键值对的形式存在。如果属性名和属性值一样&#xff0c;可以致谢属性值。 单标签&#xff1a;<标签 属性"属…

C++ 【桥接模式】

简单介绍 桥接模式属于 结构型模式 | 可将一个大类或一系列紧密相关的类拆分 为抽象和实现两个独立的层次结构&#xff0c; 从而能在开发时分别使用。 聚合关系&#xff1a;两个类处于不同的层次&#xff0c;强调了一个整体/局部的关系,当汽车对象销毁时&#xff0c;轮胎对象…

记录重装ubuntu系统

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言无法进入ubuntu图形化界面进入ubuntu的tty模式开始解决误打误撞进入X-Window界面&#xff0c;拷贝ubuntu系统文件重装ubuntu系统 前言 提示&#xff1a;这里可…

面试(04)————JavaWeb

1、网络通讯部分 1.1、 TCP 与 UDP 区别&#xff1f; 1.2、什么是 HTTP 协议&#xff1f; 1.3、TCP 的三次握手&#xff0c;为什么&#xff1f; 1.4、HTTP 中重定向和请求转发的区别&#xff1f; 1.5、 Get 和 Post 的区别&#xff1f; 2、cookie 和 session 的区别&am…

Linux:部署搭建zabbix6(1)

1.基础介绍 Zabbix&#xff1a;企业级开源监控解决方案https://www.zabbix.com/cn这个是zabbix的官网&#xff0c;你可以进去看到由官方给你提供的专业介绍和获取到最新版本的功能介绍&#xff0c;还有各种安装&#xff0c;由于官方安装是多种复杂的&#xff0c;我这里就单独挑…

第21次修改了可删除可持久保存的前端html备忘录:重新布局

第21次修改了可删除可持久保存的前端html备忘录&#xff1a;更改了布局 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&quo…