ffmpeg封装h264裸流为mp4文件,视频播放速度特别慢

ccdn2022 2014-04-21 11:00:34

大家好。我将实时采集的x264编码后的H264流直接封装到mp4文件(不是从h264文件读取的)后,视频可以播放,但是播放速度非常慢。录制的10秒钟的视频变成了75秒左右,参考了ffmpeg网站上面的muxing.c,还有这里的帖子http://bbs.csdn.net/topics/360190139,我现在用的是最新版本的ffmpeg烦请高人指点下啊。
代码如下:
其中,data是存放nalu的缓冲区,nLen为该nalu的大小


#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include "Stream2Mp4.h"

#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libswresample/swresample.h>

#define STREAM_FRAME_RATE 25
#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */

static int ptsInc = 0;
static int vi = -1;
static int waitkey = 1;

// < 0 = error
// 0 = I-Frame
// 1 = P-Frame
// 2 = B-Frame
// 3 = S-Frame
int getVopType( const void *p, int len )
{
if ( !p || 6 >= len )
return -1;

unsigned char *b = (unsigned char*)p;

// Verify NAL marker
if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
{ b++;
if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
return -1;
} // end if

b += 3;

// Verify VOP id
if ( 0xb6 == *b )
{ b++;
return ( *b & 0xc0 ) >> 6;
} // end if

switch( *b )
{ case 0x65 : return 0;
case 0x61 : return 1;
case 0x01 : return 2;
} // end switch

return -1;
}

int get_nal_type( void *p, int len )
{
if ( !p || 5 >= len )
return -1;

unsigned char *b = (unsigned char*)p;

// Verify NAL marker
if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
{ b++;
if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
return -1;
} // end if

b += 3;

return *b;
}


/* Add an output stream */
AVStream *add_stream(AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id)
{
AVCodecContext *c;
AVStream *st;

/* find the encoder */
*codec = avcodec_find_encoder(codec_id);
if (!*codec)
{
printf("could not find encoder for '%s' \n", avcodec_get_name(codec_id));
exit(1);
}

st = avformat_new_stream(oc, *codec);
if (!st)
{
printf("could not allocate stream \n");
exit(1);
}
st->id = oc->nb_streams-1;
c = st->codec;
vi = st->index;

switch ((*codec)->type)
{
case AVMEDIA_TYPE_AUDIO:
c->sample_fmt = (*codec)->sample_fmts ? (*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
c->bit_rate = 64000;
c->sample_rate = 44100;
c->channels = 2;
break;

case AVMEDIA_TYPE_VIDEO:
c->codec_id = codec_id;
c->bit_rate = 90000;
c->width = 480;
c->height = 354;
c->time_base.den = 15;
c->time_base.num = 1;
c->gop_size = 12;
c->pix_fmt = STREAM_PIX_FMT;
if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO)
{
c->max_b_frames = 2;
}
if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO)
{
c->mb_decision = 2;
}
break;

default:
break;
}

if (oc->oformat->flags & AVFMT_GLOBALHEADER)
{
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
}

return st;
}



void open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st)
{
int ret;
AVCodecContext *c = st->codec;

/* open the codec */
ret = avcodec_open2(c, codec, NULL);
if (ret < 0)
{
printf("could not open video codec");
//exit(1);
}

}

int CreateMp4(AVFormatContext *&m_pOc, void *p, int len)
{
int ret; // 成功返回0,失败返回1
const char* pszFileName = "output002.mp4";
AVOutputFormat *fmt;
AVCodec *video_codec;
AVStream *m_pVideoSt;

if (0x67 != get_nal_type(p, len))
{
printf("can not detect nal type");
return -1;
}
av_register_all();

avformat_alloc_output_context2(&m_pOc, NULL, NULL, pszFileName);
if (!m_pOc)
{
printf("Could not deduce output format from file extension: using MPEG. \n");
avformat_alloc_output_context2(&m_pOc, NULL, "mpeg", pszFileName);
}
if (!m_pOc)
{
return 1;
}

fmt = m_pOc->oformat;

if (fmt->video_codec != AV_CODEC_ID_NONE)
{
m_pVideoSt = add_stream(m_pOc, &video_codec, fmt->video_codec);
}

if (m_pVideoSt)
{
open_video(m_pOc, video_codec, m_pVideoSt);
}

av_dump_format(m_pOc, 0, pszFileName, 1);

/* open the output file, if needed */
if (!(fmt->flags & AVFMT_NOFILE))
{
ret = avio_open(&m_pOc->pb, pszFileName, AVIO_FLAG_WRITE);
if (ret < 0)
{
printf("could not open '%s': %s\n", pszFileName);
return 1;
}
}

/* Write the stream header, if any */
ret = avformat_write_header(m_pOc, NULL);
if (ret < 0)
{
printf("Error occurred when opening output file");
return 1;
}
}


/* write h264 data to mp4 file
* 创建mp4文件返回2;写入数据帧返回0 */

void WriteVideo(AVFormatContext *&m_pOc,void* data, int nLen)
{
int ret;

if ( 0 > vi )
{
printf("vi less than 0");
//return -1;
}
AVStream *pst = m_pOc->streams[ vi ];

// Init packet
AVPacket pkt;

// 我的添加,为了计算pts
AVCodecContext *c = pst->codec;

av_init_packet( &pkt );
pkt.flags |= ( 0 >= getVopType( data, nLen ) ) ? AV_PKT_FLAG_KEY : 0;

pkt.stream_index = pst->index;
pkt.data = (uint8_t*)data;
pkt.size = nLen;

// Wait for key frame
if ( waitkey )
if ( 0 == ( pkt.flags & AV_PKT_FLAG_KEY ) )
return ;
else
waitkey = 0;


pkt.pts = (ptsInc++) * (90000/STREAM_FRAME_RATE);
//pkt.dts = (ptsInc++) * (90000/STREAM_FRAME_RATE);

ret = av_interleaved_write_frame( m_pOc, &pkt );
if (ret < 0)
{
printf("cannot write frame");
}


}

void CloseMp4(AVFormatContext *&m_pOc)
{
waitkey = -1;
vi = -1;

if (m_pOc)
av_write_trailer(m_pOc);

if (m_pOc && !(m_pOc->oformat->flags & AVFMT_NOFILE))
avio_close(m_pOc->pb);

if (m_pOc)
{
avformat_free_context(m_pOc);
m_pOc = NULL;
}

}

...全文
24460 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
许磊 2016-11-24
  • 打赏
  • 举报
回复
楼主解决了吗?求个联系方式,我的联系方式1458658434
WANG5467 2016-11-16
  • 打赏
  • 举报
回复
楼主怎么解决的
一枪尽骚丶魂 2014-11-27
  • 打赏
  • 举报
回复
有Demo么?
Elven_lsy 2014-08-08
  • 打赏
  • 举报
回复
不知道楼主问题有没有解决,解决了的话麻烦贴个Demo code,谢谢!
ccdn2022 2014-05-03
  • 打赏
  • 举报
回复
引用 8 楼 bomengwutao 的回复:
[quote=引用 7 楼 ybsun2010 的回复:] [quote=引用 6 楼 bomengwutao 的回复:] c->time_base.den = 1000000; c->time_base.num = 1; st->time_base.den=c->time_base.den; st->time_base.num=c->time_base.den; 然后再调用pkt.pts = av_rescale_q((ptsInc++)*3000, fStream->codec->time_base,fStream->time_base);ptsInc初始值为0 每次加一 在WriteVideo()函数中调用的参数是一个NUL单元吧?我就是用一个裸的h264文件测试的 每次传递一个nul写入。文件的长度不对是你的 c->time_base.den不对,这个参数在用来计算文件长度的。你这样该一下试试。 这句pkt.pts =av_rescale_q((ptsInc++)*3000, fStream->codec->time_base,fStream->time_base)可以不改。
我把上面几行加在add_stream函数里面了,现在用暴风查看详细信息可以看到数据速率1206kbps,总比特率1206kbps,帧速率342帧/秒。我的WriteVideo传入的是从sps和pps开始的nal包,每个nal包起始码都是00 00 00 01。是不是写入mp4的时候只需要些nal里面的数据就行了,不需要把00 00 00 01以及sps和pps写进去呢?[/quote] 要 写00 00 00 01 以及sps和pps,你先看看能不能播放。我测试的时候是这样写的,ffmpeg在movenc.c中会去掉00 00 00 01。我测试的时候用这种方式生存了一个mp4文件,用linux下的播放器可以播放,vlc播放不了。 又换了一种封装方法,每次封装一个完整的帧,都能播放,但是会有花屏的现象。 你先试试吧。高不明白看ffmpeg中的源码。我也是菜鸟。有啥问题一起交流[/quote] 你好,我又重新试了一下,生成的文件放到FLash Media Server上面还是没法播放。我找了些资料说,封装的时候需要把sps和pps写到videoContext->extradata中的extradata里面,也就是说在write_frame()函数里面传入的是sps.pps之后的nal包,请问你是这样做的吗?谢谢了
ccdn2022 2014-04-29
  • 打赏
  • 举报
回复
引用 8 楼 bomengwutao 的回复:
[quote=引用 7 楼 ybsun2010 的回复:] [quote=引用 6 楼 bomengwutao 的回复:] c->time_base.den = 1000000; c->time_base.num = 1; st->time_base.den=c->time_base.den; st->time_base.num=c->time_base.den; 然后再调用pkt.pts = av_rescale_q((ptsInc++)*3000, fStream->codec->time_base,fStream->time_base);ptsInc初始值为0 每次加一 在WriteVideo()函数中调用的参数是一个NUL单元吧?我就是用一个裸的h264文件测试的 每次传递一个nul写入。文件的长度不对是你的 c->time_base.den不对,这个参数在用来计算文件长度的。你这样该一下试试。 这句pkt.pts =av_rescale_q((ptsInc++)*3000, fStream->codec->time_base,fStream->time_base)可以不改。
我把上面几行加在add_stream函数里面了,现在用暴风查看详细信息可以看到数据速率1206kbps,总比特率1206kbps,帧速率342帧/秒。我的WriteVideo传入的是从sps和pps开始的nal包,每个nal包起始码都是00 00 00 01。是不是写入mp4的时候只需要些nal里面的数据就行了,不需要把00 00 00 01以及sps和pps写进去呢?[/quote] 要 写00 00 00 01 以及sps和pps,你先看看能不能播放。我测试的时候是这样写的,ffmpeg在movenc.c中会去掉00 00 00 01。我测试的时候用这种方式生存了一个mp4文件,用linux下的播放器可以播放,vlc播放不了。 又换了一种封装方法,每次封装一个完整的帧,都能播放,但是会有花屏的现象。 你先试试吧。高不明白看ffmpeg中的源码。我也是菜鸟。有啥问题一起交流[/quote] 好的,谢谢谢谢。我试试,前辈可以留个联系方式啥的吗?感激不尽。被这个问题困扰了很久了
bomengwutao 2014-04-29
  • 打赏
  • 举报
回复
引用 7 楼 ybsun2010 的回复:
[quote=引用 6 楼 bomengwutao 的回复:] c->time_base.den = 1000000; c->time_base.num = 1; st->time_base.den=c->time_base.den; st->time_base.num=c->time_base.den; 然后再调用pkt.pts = av_rescale_q((ptsInc++)*3000, fStream->codec->time_base,fStream->time_base);ptsInc初始值为0 每次加一 在WriteVideo()函数中调用的参数是一个NUL单元吧?我就是用一个裸的h264文件测试的 每次传递一个nul写入。文件的长度不对是你的 c->time_base.den不对,这个参数在用来计算文件长度的。你这样该一下试试。 这句pkt.pts =av_rescale_q((ptsInc++)*3000, fStream->codec->time_base,fStream->time_base)可以不改。
我把上面几行加在add_stream函数里面了,现在用暴风查看详细信息可以看到数据速率1206kbps,总比特率1206kbps,帧速率342帧/秒。我的WriteVideo传入的是从sps和pps开始的nal包,每个nal包起始码都是00 00 00 01。是不是写入mp4的时候只需要些nal里面的数据就行了,不需要把00 00 00 01以及sps和pps写进去呢?[/quote] 要 写00 00 00 01 以及sps和pps,你先看看能不能播放。我测试的时候是这样写的,ffmpeg在movenc.c中会去掉00 00 00 01。我测试的时候用这种方式生存了一个mp4文件,用linux下的播放器可以播放,vlc播放不了。 又换了一种封装方法,每次封装一个完整的帧,都能播放,但是会有花屏的现象。 你先试试吧。高不明白看ffmpeg中的源码。我也是菜鸟。有啥问题一起交流
ccdn2022 2014-04-28
  • 打赏
  • 举报
回复
引用 6 楼 bomengwutao 的回复:
c->time_base.den = 1000000; c->time_base.num = 1; st->time_base.den=c->time_base.den; st->time_base.num=c->time_base.den; 然后再调用pkt.pts = av_rescale_q((ptsInc++)*3000, fStream->codec->time_base,fStream->time_base);ptsInc初始值为0 每次加一 在WriteVideo()函数中调用的参数是一个NUL单元吧?我就是用一个裸的h264文件测试的 每次传递一个nul写入。文件的长度不对是你的 c->time_base.den不对,这个参数在用来计算文件长度的。你这样该一下试试。 这句pkt.pts =av_rescale_q((ptsInc++)*3000, fStream->codec->time_base,fStream->time_base)可以不改。
我把上面几行加在add_stream函数里面了,现在用暴风查看详细信息可以看到数据速率1206kbps,总比特率1206kbps,帧速率342帧/秒。我的WriteVideo传入的是从sps和pps开始的nal包,每个nal包起始码都是00 00 00 01。是不是写入mp4的时候只需要些nal里面的数据就行了,不需要把00 00 00 01以及sps和pps写进去呢?
bomengwutao 2014-04-28
  • 打赏
  • 举报
回复
c->time_base.den = 1000000; c->time_base.num = 1; st->time_base.den=c->time_base.den; st->time_base.num=c->time_base.den; 然后再调用pkt.pts = av_rescale_q((ptsInc++)*3000, fStream->codec->time_base,fStream->time_base);ptsInc初始值为0 每次加一 在WriteVideo()函数中调用的参数是一个NUL单元吧?我就是用一个裸的h264文件测试的 每次传递一个nul写入。文件的长度不对是你的 c->time_base.den不对,这个参数在用来计算文件长度的。你这样该一下试试。 这句pkt.pts =av_rescale_q((ptsInc++)*3000, fStream->codec->time_base,fStream->time_base)可以不改。
ccdn2022 2014-04-26
  • 打赏
  • 举报
回复
引用 3 楼 bomengwutao 的回复:
还有就是 c->bit_rate = 90000; c->time_base.den = 15; c->time_base.num = 1;这些都是影响播放时间戳的单位,可以根据流的参数进行修改试试。播放画面正常,速度慢这就只能修改时间戳了。
这里的参数我也试着修改了一下,现在把pts按照您上面的修改了之后现在录制的没法用暴风正常播放了,可以打开但是一直不走。我想根据输入流来设置上面这些参数,但是我真的不知道该怎么获取这些参数,还请您给点提示,谢谢了!
ccdn2022 2014-04-26
  • 打赏
  • 举报
回复
引用 2 楼 bomengwutao 的回复:
用你的代码我写了一个,播放正常
pkt.pts = av_rescale_q((ptsInc++)*3000, fStream->codec->time_base,fStream->time_base);替换了


不好意思,回复晚了。我按照您的这个方法试了,还是不行。我录制了10S左右的,现在变成了6分多钟,并且我用暴风影音查看详细信息,数据速率、总比特率、帧速率都获取不到。下面是截图:


我获取h264视频流的函数是这样,您看看这样处理可以吗?第一次做视频相关的,菜鸟一个,麻烦您了。
int CGoDlg::Deal264(AVFormatContext* &m_pOc)
{
for(int i=0;i<15;i++)
{
if(this->m_p264[i][0]==1)
{
// m_p264[i]代表一个NALU单元,第一位为标志位,表示是否可用,第二位和第三位代表NALU长度,第四位开始为NALU数据
this->m_p264[i][0]=0;
//todo:
int cost,len;
len=this->m_p264[i][1]+((this->m_p264[i][2])<<8);
logfile(200,&(this->m_p264[i][3]),len);

// 此处添加处理NALU的代码
if (!m_pOc)
{
CreateMp4(m_pOc, &(this->m_p264[i][3]),len);
printf("create mp4");

}
if (m_pOc)
{
WriteVideo(m_pOc, &(this->m_p264[i][3]),len);
printf("write frame");
}
}

在另外的函数中判断是否按下了“停止采集”的按钮,然后调用CloseMp4(m_pOc);
bomengwutao 2014-04-24
  • 打赏
  • 举报
回复
还有就是 c->bit_rate = 90000; c->time_base.den = 15; c->time_base.num = 1;这些都是影响播放时间戳的单位,可以根据流的参数进行修改试试。播放画面正常,速度慢这就只能修改时间戳了。
bomengwutao 2014-04-24
  • 打赏
  • 举报
回复
用你的代码我写了一个,播放正常 pkt.pts = av_rescale_q((ptsInc++)*3000, fStream->codec->time_base,fStream->time_base);替换了
bomengwutao 2014-04-24
  • 打赏
  • 举报
回复
把你时间戳改改试试

2,275

社区成员

发帖
与我相关
我的任务
社区描述
多媒体/设计/Flash/Silverlight 开发 Flash流媒体开发
社区管理员
  • Flash流媒体开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧