ChinaFFmpeg

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 22769|回复: 6

[源码] h.264裸流如何封装成mp4文件

[复制链接]
发表于 2014-4-20 16:10:41 | 显示全部楼层 |阅读模式
大家好!我想把x264编码后的h264视频流直接用ffmpeg封装成MP4文件,参考了ffmpeg上面的muxing.c,还有这里的帖子http://bbs.csdn.net/topics/360190139,我现在用的是最新版本的ffmpeg,在pts的设置这里实在不知道怎么做了,现在做到的程度是封装后的mp4文件播放的特别快,跪求高人帮忙解答,谢谢了。我的代码如下:
其中,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.dts = AV_NOPTS_VALUE;
        ////pkt.dts = 0;
        //pkt.pts = AV_NOPTS_VALUE;
        ////pkt.pts = 0;
       
        //pkt.dts = (ptsInc++) * (90000/STREAM_FRAME_RATE);
        //pkt.pts = (ptsInc++) * (1000000/STREAM_FRAME_RATE);
        if (c->coded_frame->pts != AV_NOPTS_VALUE)
                pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, pst->time_base);

          //av_write_frame( m_pOc, &pkt );
        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;
        }

}


回复

使用道具 举报

发表于 2014-4-20 19:48:04 | 显示全部楼层
看上去你是就没有设pts 和dts,这个可以参考spec,一般video的pts dts可以以3600递增,试一下吧
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-4-20 20:54:41 | 显示全部楼层
孙悟空 发表于 2014-4-20 19:48
看上去你是就没有设pts 和dts,这个可以参考spec,一般video的pts dts可以以3600递增,试一下吧 ...

谢谢大师兄。我刚才按照你说的试了一下,加了这句 pkt.pts = (ptsInc++) * (90000/STREAM_FRAME_RATE); 其中ptsInc从0开始,STREAM_FRAME_RATE为25,这样就是按照3600递增了。加上这句后,视频能播放了,但是录制了15s的视频变成了75s,播放的特别慢。另外,我加上 pts.dts =  (ptsInc++) * (90000/STREAM_FRAME_RATE);就生不成mp4文件了,只有一个mp4的文件头。
并且,我将pts递增的值改变了之后就会快放或者慢放,请问大师兄,这可咋整啊
回复 支持 反对

使用道具 举报

发表于 2014-6-3 09:42:12 | 显示全部楼层
[C] 纯文本查看 复制代码
        pkt.size         = framesize ;		

        pkt.flags |= AV_PKT_FLAG_KEY;
        pkt.stream_index = st->index;
        pkt.data         = buf ;

        pkt.dts = pkt.pts = gAudioPts;
        pkt.duration = av_rescale_q(16000, (AVRational){ 1, c->sample_rate }, c->time_base);
        
        gAudioPts+=1024;
        
        pkt.pts = av_rescale_q_rnd(pkt.pts, c->time_base, st->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
        pkt.dts = av_rescale_q_rnd(pkt.dts, c->time_base, st->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
        pkt.duration = av_rescale_q(pkt.duration, c->time_base, st->time_base);

音频部分的pts dts 可以这样赋值,


        AVPacket pkt;
        av_init_packet(&pkt);

        if (frameType)
            pkt.flags |= AV_PKT_FLAG_KEY;

        pkt.stream_index = st->index;
        pkt.data         = decodedBuf;
        pkt.size         = frameSize;

        c->coded_frame->pts++;

        pkt.pts = gVideoPts;
        pkt.dts = gVideoPts - 2;

        ++gVideoPts;
        
        pkt.pts = av_rescale_q_rnd(pkt.pts, c->time_base, st->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
        pkt.dts = av_rescale_q_rnd(pkt.dts, c->time_base, st->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
        pkt.duration = av_rescale_q(pkt.duration, c->time_base, st->time_base);

视频部分可以这样赋值,
初始值如下:
    gVideoPts = 0;
    gAudioPts = -1600;


我目前也是用的最新的ffmpeg,目前封装的视频是完全正常的,具体为什么pts dts要怎么算我也没有弄清楚,我这个是调试那个muxing得到的结果。
回复 支持 反对

使用道具 举报

发表于 2014-6-20 17:42:50 | 显示全部楼层
顶一个,
回复

使用道具 举报

发表于 2015-11-20 16:42:44 | 显示全部楼层
合成mp4文件的avpacket有中有哪些参数是必须的?我参考muxing.c写出来的可以被暴风影音之类的第三方播放器播放,但是android手机自带的播放器不能播放,这可能是什么原因?
回复 支持 反对

使用道具 举报

发表于 2015-11-20 17:11:47 | 显示全部楼层
楼兰主 发表于 2014-6-3 09:42
[mw_shl_code=c,true]        pkt.size         = framesize ;               

        pkt.flags |= AV_PKT_FLAG_KEY;

请问你封装的mp4文件一些自带的播放器也能播放吗?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|Archiver|ChinaFFmpeg

GMT+8, 2025-4-3 23:51 , Processed in 0.070999 second(s), 15 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表