[TOC]ios
经过上一章创建好相应的开发环境后,我们就开始撸代码了,这章简单介绍了AVFormat的解封装以及简单的应用,这章的前提知识是bash
咱们新建一个类微信
这个类就是写咱们今天的Demo,首先是要引入头文件网络
#include <iostream>
extern "C" {
#include <libavformat/avformat.h>
}
复制代码
使用std的命名空间,少打几个字母,没有错 我就是这么懒ide
using namespace std;
复制代码
这里咱们先来说一下解复用的流程测试
对应到代码上ui
/**
* avformat 的简单使用
*
* 分离视频
*
* @param url 视频的Url(本地/网络)
*/
void chapter05_h264(const char *url) {
//打开文件流
FILE *output = fopen("./output.h264", "wb+");
//返回状态码
int ret_code;
//寻找到指定的流下标
int media_index = -1;
//分配一个存储解封装后的Packet
AVPacket *packet = av_packet_alloc();
//初始化网络
avformat_network_init();
//分配AVFormatContext
AVFormatContext *avFormatContext = avformat_alloc_context();
if (avFormatContext == nullptr) {
cout << "[error] AVFormat alloc error" << endl;
goto failed;
}
//打开输入流
ret_code = avformat_open_input(&avFormatContext, url, nullptr, nullptr);
if (ret_code < 0) {
cout << "[error] open input failed " << av_err2str(AVERROR(ret_code)) << endl;
goto failed;
}
//读取媒体文件信息
ret_code = avformat_find_stream_info(avFormatContext, nullptr);
if (ret_code < 0) {
cout << "[error] find stream info failed " << av_err2str(AVERROR(ret_code)) << endl;
goto failed;
}
//寻找到指定的视频流
media_index = av_find_best_stream(avFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (media_index < 0) {
cout << "[error] find stream index error" << endl;
goto failed;
}
//读取pakcet
while (av_read_frame(avFormatContext, packet) == 0) {
//判断是否是指定流的packet
if (packet->stream_index == media_index) {
//写入到文件
fwrite(packet->data, 1, packet->size, output);
}
}
failed:
av_packet_free(&packet);
avformat_close_input(&avFormatContext);
}
复制代码
在main.cpp
中的main
方法中url
//
// Created by MirsFang on 2019-03-12.
//
#include <iostream>
extern "C"{
#include <libavformat/avformat.h>
}
#include "src/chapter_05/avformatuse.h"
using namespace std;
int main(){
int version =avformat_version();
cout<<"version:"<<version<<endl;
const char* url = "../video/test_video.mp4";
//分离H264
chapter05_h264(url);
return 0;
}
复制代码
而后咱们点击运行以后,在cmake-build-debug 目录 就会输出output.h264 文件 咱们经过命令行切换到那个目录执行 ffplay output.h264
咱们会发现什么?spa
没错 报错了! 啊哈哈哈哈 命令行
这个缘由是为何的,咱们经过了解H264的文件结构知道,若是想要播放,那么每一个NALU都会有头信息以及SPS,PPS信息才能播放,因此,若是要保存的数据能播放的话,要将每一个packet都处理一下,这里会用到AVBitStreamFilter
,他的用法我在注释里面解释的比较清楚,在此就很少BB了,咱们新写一个chapter05_h264_01(url)
方法
/**
* avformat 的简单使用
*
* 分离视频
* @param url 视频的Url(本地/网络)
*/
void chapter05_h264_01(const char *url) {
//打开文件流
FILE *output = fopen("./output_01.h264", "wb+");
//返回状态码
int ret_code;
//寻找到的指定的流下标
int media_index = -1;
//分配一个存储读取出来的数据的 packet
AVPacket *packet = av_packet_alloc();
//初始化网络
avformat_network_init();
//建立H264_mp4的filter
const AVBitStreamFilter *bsf = av_bsf_get_by_name("h264_mp4toannexb");
AVBSFContext *ctx = NULL;
//分配AVFormatContext
AVFormatContext *avFormatContext = avformat_alloc_context();
if (avFormatContext == nullptr) {
cout << "[error] AVFormat alloc error" << endl;
goto failed;
}
//打开输入流
ret_code = avformat_open_input(&avFormatContext, url, nullptr, nullptr);
if (ret_code < 0) {
cout << "[error] open input failed " << av_err2str(AVERROR(ret_code)) << endl;
goto failed;
}
//读取媒体文件信息
ret_code = avformat_find_stream_info(avFormatContext, nullptr);
if (ret_code < 0) {
cout << "[error] find stream info failed " << av_err2str(AVERROR(ret_code)) << endl;
goto failed;
}
//寻找到指定的视频流
media_index = av_find_best_stream(avFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (media_index < 0) {
cout << "[error] find stream index error" << endl;
goto failed;
}
//alloc bsf
ret_code = av_bsf_alloc(bsf, &ctx);
if (ret_code < 0) {
cout << "[error] BSF alloc failed " << av_err2str(AVERROR(ret_code)) << endl;
goto failed;
}
//复制解码器参数到BSFContext
ret_code = avcodec_parameters_copy(ctx->par_in, avFormatContext->streams[media_index]->codecpar);
if (ret_code < 0) {
cout << "[error] BSF copy parameter failed " << av_err2str(AVERROR(ret_code)) << endl;
goto failed;
}
//同步time_base
ctx->time_base_in = avFormatContext->streams[media_index]->time_base;
//初始化bsf
ret_code = av_bsf_init(ctx);
if (ret_code < 0) {
cout << "[error] BSF init failed " << av_err2str(AVERROR(ret_code)) << endl;
goto failed;
}
while (av_read_frame(avFormatContext, packet) == 0) {
if (packet->stream_index != media_index)continue;
//发送packet到BitStreamFilter
ret_code = av_bsf_send_packet(ctx, packet);
if (ret_code < 0) {
cout << "[error] BSF send packet failed " << av_err2str(AVERROR(ret_code)) << endl;
goto failed;
}
//接受添加sps pps头的packet
while ((ret_code = av_bsf_receive_packet(ctx, packet)) == 0) {
//写入到文件
fwrite(packet->data, 1, packet->size, output);
av_packet_unref(packet);
}
//须要输入数据
if (ret_code == AVERROR(EAGAIN)) {
cout << "[debug] BSF EAGAIN " << endl;
av_packet_unref(packet);
continue;
}
//已经读取到结尾
if (ret_code == AVERROR_EOF) {
cout << "[debug] BSF EOF " << endl;
break;
}
if (ret_code < 0) {
cout << "[error] BSF receive packet failed " << av_err2str(AVERROR(ret_code)) << endl;
goto failed;
}
}
//Flush
ret_code = av_bsf_send_packet(ctx, NULL);
if (ret_code < 0) {
cout << "[error] BSF flush send packet failed " << av_err2str(AVERROR(ret_code)) << endl;
goto failed;
}
while ((ret_code = av_bsf_receive_packet(ctx, packet)) == 0) {
fwrite(packet->data, 1, packet->size, output);
}
if (ret_code != AVERROR_EOF) {
cout << "[debug] BSF flush EOF " << endl;
goto failed;
}
failed:
//释放packet
av_packet_free(&packet);
//释放AVFormatContext
avformat_close_input(&avFormatContext);
//关闭网络流
avformat_network_deinit();
//释放BSFContext
av_bsf_free(&ctx);
//关闭文件流
fclose(output);
}
复制代码
在main
方法中
#include <iostream>
extern "C"{
#include <libavformat/avformat.h>
}
#include "src/chapter_05/avformatuse.h"
using namespace std;
int main(){
int version =avformat_version();
cout<<"version:"<<version<<endl;
const char* url = "../video/test_video.mp4";
//分离H264
// chapter05_h264(url);
chapter05_h264_01(url);
return 0;
}
复制代码
命令行执行ffplay output_01.h264
咱们就能够看到画面已经出来了😝
假如咱们换一个网络流(测试的时候求各位大佬不要用这个,,跑的都是个人CDN流量啊😭😭😭😭)
main
方法
#include <iostream>
extern "C"{
#include <libavformat/avformat.h>
}
#include "src/chapter_05/avformatuse.h"
using namespace std;
int main(){
int version =avformat_version();
cout<<"version:"<<version<<endl;
const char* httpUrl = "http://po79db9wc.bkt.clouddn.com/test_video.mp4";
const char* url = "../video/test_video.mp4";
//分离H264
// chapter05_h264(url);
chapter05_h264_01(httpUrl);
return 0;
}
复制代码
nice !!!
这个只是带你们简单熟悉一下分离的操做,分离音频流的操做也差很少,也要先知道相应的音频流的格式(AAC),手动去写入AAC的头信息(ADIF/ADTS),你们就须要本身探讨一下了😝
若是有不理解,能够加微信群一块儿讨论
若是二维码过时,能够加我微信,备注 音视频
未完持续。。。