在windows下使用ffmpeg7.1推流遇到问题
extern “C”
{
#define __STDC_CONSTANT_MACROS 1
#include “librtmp/rtmp.h”
#include <openssl/hmac.h>
#include “libavutil/avutil.h”
#include “libavdevice/avdevice.h”
#include “libavformat/avformat.h”
#include “libavcodec/avcodec.h”
#include “libavutil/time.h”
}
#include
#include
#include
#include
#include
using std::cout;
using std::ifstream;
using std::string;
static FILE* open_flv(char *flv_name) {
FILE *fp = NULL;
fp = fopen(flv_name, "rb");
if (!fp) {
printf("Failed to open flv: %s", flv_name);
return NULL;
}
fseek(fp, 9, SEEK_SET); //跳过 9 字节的 FLV Header
fseek(fp, 4, SEEK_CUR); //跳过 4 字节的PreTagSize
return fp;
}
//connect rtmp server
static RTMP* conect_rtmp_server(char *rtmpaddr) {
RTMP *rtmp = NULL;
//1. 创建RTMP对象,并进行初始化
rtmp = RTMP_Alloc();
if (!rtmp) {
printf("NO Memory, Failed to alloc RTMP object!\n");
goto __ERROR;
}
RTMP_Init(rtmp);
//2.先设置RTMP服务地址,以及设置连接超时间
rtmp->Link.timeout = 10;
RTMP_SetupURL(rtmp, rtmpaddr);
//3. 设置是推流还是拉流
//如果设置了该开关,就是推流(publish),如果未设置就是拉流(play)
RTMP_EnableWrite(rtmp);
//4. 建立连接
if (!RTMP_Connect(rtmp, NULL)) {
printf("Failed to Connect RTMP Server!\n");
goto __ERROR;
}
//5. create stream
RTMP_ConnectStream(rtmp, 0);
return rtmp;
__ERROR:
//释放资源
if (rtmp) {
RTMP_Close(rtmp);
RTMP_Free(rtmp);
}
return NULL;
}
//分配RTMPPacket空间
static RTMPPacket* alloc_packet() {
RTMPPacket *pack = NULL;
pack = (RTMPPacket*)malloc(sizeof(RTMPPacket));
if (!pack) {
printf("No Memory, Failed to alloc RTMPPacket!\n");
return NULL;
}
if (!RTMPPacket_Alloc(pack, 64 * 1024)) {
//
}
RTMPPacket_Reset(pack);
pack->m_hasAbsTimestamp = 0;
pack->m_nChannel = 0x4;
return pack;
}
static int read_u8(FILE *fp, unsigned int *u8) {
unsigned int tmp;
if (fread(&tmp, 1, 1, fp) != 1) {
printf(“Failed to read_u8!\n”);
return -1;
}
*u8 = tmp & 0xFF;
return 0;
}
/*
…| | | | | …
…x1, x2, x3, x4, x5, xxxxx…
…|x1|x2|x3| | …
…|x3|x2|x1| | …
*/
static int read_u24(FILE *fp, unsigned int *u24) {
unsigned int tmp;
if (fread(&tmp, 1, 3, fp) != 3) {
printf(“Failed to read_u24!\n”);
return -1;
}
*u24 = ((tmp >> 16) & 0xFF) | ((tmp << 16) & 0xFF0000) | (tmp & 0xFF00);
return 0;
}
static int read_u32(FILE *fp, unsigned int *u32) {
unsigned int tmp;
if (fread(&tmp, 1, 4, fp) != 4) {
printf(“Failed to read_u32!\n”);
return -1;
}
*u32 = ((tmp >> 24) & 0xFF) | ((tmp >> 8) & 0xFF00) |
((tmp << 8) & 0xFF0000) | ((tmp << 24) & 0xFF000000);
return 0;
}
static int read_ts(FILE *fp, unsigned int *ts) {
unsigned int tmp;
if (fread(&tmp, 1, 4, fp) != 4) {
printf(“Failed to read_ts!\n”);
return -1;
}
*ts = ((tmp >> 16) & 0xFF) | ((tmp << 16) & 0xFF0000) | (tmp & 0xFF00) | (tmp & 0xFF000000);
return 0;
}
/**
@param[in] fp : flv file
@param[out] packet the data from flv
@return 0: success, -1: failure
*/
static int read_data(FILE *fp, RTMPPacket **packet) {
/*
/*
unsigned int tt;
unsigned int tag_data_size;
unsigned int ts;
unsigned int streamid;
unsigned int tag_pre_size;
if (read_u8(fp, &tt)) {
goto __ERROR;
}
if (read_u24(fp, &tag_data_size)) {
goto __ERROR;
}
if (read_ts(fp, &ts)) {
goto __ERROR;
}
if (read_u24(fp, &streamid)) {
goto __ERROR;
}
printf(“tag header, ts: %u, tt: %d, datasize:%d \n”, ts, tt, tag_data_size);
datasize = fread((*packet)->m_body, 1, tag_data_size, fp);
if (tag_data_size != datasize) {
printf(“Failed to read tag body from flv, (datasize=%zu:tds=%d)\n”,
datasize,
tag_data_size);
goto __ERROR;
}
//设置pakcet数据
(*packet)->m_headerType = RTMP_PACKET_SIZE_LARGE;
(*packet)->m_nTimeStamp = ts;
(*packet)->m_packetType = tt;
(*packet)->m_nBodySize = tag_data_size;
read_u32(fp, &tag_pre_size);
ret = 0;
__ERROR:
return ret;
}
//向流媒体服务器推流
static void send_data(FILE *fp, RTMP *rtmp) {
//1. 创建 RTMPPacket 对象
RTMPPacket *packet = NULL;
packet = alloc_packet();
packet->m_nInfoField2 = rtmp->m_stream_id;
unsigned int pre_ts = 0;
while (1) {
//2.从flv文件中读取数据
if (read_data(fp, &packet)) {
printf("over!\n");
break;
}
//3. 判断RTMP连接是否正常
if (!RTMP_IsConnected(rtmp)) {
printf("Disconnect....\n");
break;
}
unsigned int diff = packet->m_nTimeStamp - pre_ts;
std::this_thread::sleep_for(std::chrono::milliseconds(diff * 1000));
//4. 发送数据
RTMP_SendPacket(rtmp, packet, 0);
pre_ts = packet->m_nTimeStamp;
}
return;
}
void publish_stream() {
char *flv = (char*)"D:/1.flv";
char *rtmpaddr = (char*) "rtmp://192.168.3.2:1935/live/room";
//1. 读 flv 文件
FILE *fp = open_flv(flv);
//2. 连接 RTMP 服务器
RTMP *rtmp = conect_rtmp_server(rtmpaddr);
//3. publish audio/video data
send_data(fp, rtmp);
//4. release rtmp
RTMP_Close(rtmp);
RTMP_Free(rtmp);
return;
}
int main()
{
publish_stream();
return 0;
}
我在网上找了以下代码,推流成功了
extern “C” {
#include <libavutil/frame.h>
#include <libavutil/imgutils.h>
#include <libavutil/time.h>
#include <libswscale/swscale.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include “libavdevice/avdevice.h”
#include “libswresample/swresample.h”
#include <libavutil/dict.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
}
#include
using std::cout;
static double r2d(AVRational r)
{
return (r.den == 0) ? 0 : (double)r.num / (double)r.den;
}
int ff_push_flv_to_rtmp_stream(char *input_filename, char *output_filename)
{
int ret = 0;
char err[1024];
// in stream
AVFormatContext *ctx = NULL;
// out stream
AVFormatContext *octx = NULL;
// 1.打开输入文件,返回 AVFormatContext
ret = avformat_open_input(&ctx, input_filename, NULL, NULL);
if (ret < 0)
{
av_strerror(ret, err, 1024);
cout << "avformat_open_input:" << ret << " : " << err << '\n';
return -1;
}
// 2.查找流
ret = avformat_find_stream_info(ctx, NULL);
if (ret < 0)
{
av_strerror(ret, err, 1024);
cout << "avformat_find_stream_info:" << ret << " : " << err << '\n';
return -1;
}
//创建输出流上下文
ret = avformat_alloc_output_context2(&octx, 0, "flv", output_filename);
if (!octx) {
av_strerror(ret, err, 1024);
cout << "avformat_alloc_output_context2:" << ret << " : " << err << '\n';
return 0;
}
// 创建输出流
for (int i = 0; i < ctx->nb_streams; ++i) {
AVStream *s = ctx->streams[i];
const AVCodec * codec = avcodec_find_decoder(s->codecpar->codec_id);
AVStream *out = avformat_new_stream(octx, codec);
//拷贝相关编码器信息
avcodec_parameters_copy(out->codecpar, s->codecpar);
// out->codecpar->codec_tag = 0;
}
//打开输出io流
ret = avio_open2(&octx->pb, output_filename, AVIO_FLAG_WRITE, &octx->interrupt_callback, NULL);
if (!octx->pb)
{
av_strerror(ret, err, 1024);
cout << "avio_open2:" << ret << " : " << err << '\n';
avformat_free_context(octx);
octx = NULL;
return 0;
}
av_opt_set(octx->priv_data, "pes_payload_size", "0", 0);
//写入头信息
ret = avformat_write_header(octx, 0);
if (ret < 0)
{
av_strerror(ret, err, 1024);
cout << "avformat_write_header:" << ret << " : " << err << '\n';
if (octx && !(octx->oformat->flags & AVFMT_NOFILE))
{
avio_close(octx->pb);
octx->pb = NULL;
}
avformat_free_context(octx);
octx = NULL;
return 0;
}
AVPacket *avPacket = NULL;
avPacket = av_packet_alloc();
int64_t start_time = av_gettime();
while (av_read_frame(ctx, avPacket) >= 0)
{
//计算转换pts dts
AVRational i_tb = ctx->streams[avPacket->stream_index]->time_base;
AVRational o_tb = octx->streams[avPacket->stream_index]->time_base;
avPacket->pts = av_rescale_q_rnd(avPacket->pts, i_tb, o_tb, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
avPacket->dts = av_rescale_q_rnd(avPacket->pts, i_tb, o_tb, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
avPacket->duration = av_rescale_q_rnd(avPacket->duration, i_tb, o_tb, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
avPacket->pos = -1;
//视频帧推送速度
if (ctx->streams[avPacket->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
AVRational tb = ctx->streams[avPacket->stream_index]->time_base;
//已经过去的时间
int64_t now = av_gettime() - start_time;
int64_t dts = avPacket->dts * (1000 * 1000 * r2d(tb));
if (dts > now)
av_usleep(dts - now);
}
//写入流数据
ret = av_interleaved_write_frame(octx, avPacket);
if (ret < 0)
{
break;
}
//unref AVPacket
av_packet_unref(avPacket);
if (ret < 0)
break;
}
return 1;
}
int main()
{
char inputFile{ (char)“d:/1.flv” };
char outPutFile{ (char)“rtmp://localhost/live/room” };
ff_push_flv_to_rtmp_stream(inputFile, outPutFile);
return 0;
}