ffmpeg中的时间单位

101 篇文章 4 订阅

视频的显示和存放原理

对于一个电影,帧是这样来显示的:I B B P。现在我们需要在显示B帧之前知道P帧中的信息。因此,帧可能会按照这样的方式来存储:IPBB。这就是为什么我们会有一个解码时间戳和一个显示时间戳的原因。解码时间戳告诉我们什么时候需要解码,显示时间戳告诉我们什么时候需要显示。所以,在这种情况下,我们的流可以是这样的:

PTS: 1 4 2 3
DTS: 1 2 3 4
Stream: I P B B

通常PTS和DTS只有在流中有B帧的时候会不同。

DTS和PTS

音频和视频流都有一些关于以多快速度和什么时间来播放它们的信息在里面。音频流有采样,视频流有每秒的帧率。然而,如果我们只是简单的通过数帧和乘以帧率的方式来同步视频,那么就很有可能会失去同步。于是作为一种补充,在流中的包有种叫做DTS(解码时间戳)和PTS(显示时间戳)的机制。为了这两个参数,你需要了解电影存放的方式。像MPEG等格式,使用被叫做B帧(B表示双向bidrectional)的方式。另外两种帧被叫做I帧和P帧(I表示关键帧,P表示预测帧)。I帧包含了某个特定的完整图像。P帧依赖于前面的I帧和P帧并且使用比较或者差分的方式来编码。B帧与P帧有点类似,但是它是依赖于前面和后面的帧的信息的。这也就解释了为什么我们可能在调用avcodec_decode_video以后会得不到一帧图像。

ffmpeg中的时间单位

AV_TIME_BASE

ffmpeg中的内部计时单位(时间基),ffmepg中的所有时间都是于它为一个单位,比如AVStream中的duration即以为着这个流的长度为duration个AV_TIME_BASE。AV_TIME_BASE定义为:

#define         AV_TIME_BASE   1000000

 

AV_TIME_BASE_Q

ffmpeg内部时间基的分数表示,实际上它是AV_TIME_BASE的倒数。从它的定义能很清楚的看到这点:

#define         AV_TIME_BASE_Q   (AVRational){1, AV_TIME_BASE}

 

AVRatioal的定义如下:

typedef struct AVRational{
int num; //numerator
int den; //denominator
} AVRational;

ffmpeg提供了一个把AVRatioal结构转换成double的函数:

static inline double av_q2d(AVRational a){
/**
* Convert rational to double.
* @param a rational to convert
**/
    return a.num / (double) a.den;
}

现在可以根据pts来计算一桢在整个视频中的时间位置:

timestamp(秒) = pts * av_q2d(st->time_base)
 

计算视频长度的方法:

time(秒) = st->duration * av_q2d(st->time_base)
 

这里的st是一个AVStream对象指针。

时间基转换公式

  • timestamp(ffmpeg内部时间戳) = AV_TIME_BASE * time(秒)
  • time(秒) = AV_TIME_BASE_Q * timestamp(ffmpeg内部时间戳)

所以当需要把视频跳转到N秒的时候可以使用下面的方法:

int64_t timestamp = N * AV_TIME_BASE; 
2
av_seek_frame(fmtctx, index_of_video, timestamp, AVSEEK_FLAG_BACKWARD);

ffmpeg同样为我们提供了不同时间基之间的转换函数:

int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)

这个函数的作用是计算a * bq / cq,来把时间戳从一个时基调整到另外一个时基。在进行时基转换的时候,我们应该首选这个函数,因为它可以避免溢出的情况发生。







FFmpeg里有两种时间戳:DTS(Decoding Time Stamp)和PTS(Presentation Time Stamp)。 顾名思义,前者是解码的时间,后者是显示的时间。要仔细理解这两个概念,需要先了解FFmpeg中的packet和frame的概念。

FFmpeg中用AVPacket结构体来描述解码前或编码后的压缩包,用AVFrame结构体来描述解码后或编码前的信号帧。 对于视频来说,AVFrame就是视频的一帧图像。这帧图像什么时候显示给用户,就取决于它的PTS。DTS是AVPacket里的一个成员,表示这个压缩包应该什么时候被解码。 如果视频里各帧的编码是按输入顺序(也就是显示顺序)依次进行的,那么解码和显示时间应该是一致的。可事实上,在大多数编解码标准(如H.264或HEVC)中,编码顺序和输入顺序并不一致。 于是才会需要PTS和DTS这两种不同的时间戳。







FFMPEG之TimeBase成员理解

FFMPEG的很多结构中有AVRational time_base;这样的一个成员,它是AVRational结构的

typedef struct AVRational{
    int num; ///< numerator
    int den; ///< denominator
} AVRational;

AVRational这个结构标识一个分数,num为分数,den为分母。

 

实际上time_base的意思就是时间的刻度:

如(1,25),那么时间刻度就是1/25

(1,9000),那么时间刻度就是1/90000

那么,在刻度为1/25的体系下的time=5,转换成在刻度为1/90000体系下的时间time为(5*1/25)/(1/90000) = 3600*5=18000

ffmpeg中做pts计算时,存在大量这种转换

 

在以下结构中都有

AVCodecContext:编解码上下文。

AVStream:文件或其它容器中的某一个track。

 

如果由某个解码器产生固定帧率的码流

AVCodecContext中的AVRational根据帧率来设定,如25帧,那么num = 1,den=25

AVStream中的time_base一般根据其采样频率设定,如(1,90000)

 

在某些场景下涉及到PTS的计算时,就涉及到两个Time的转换,以及到底取哪里的time_base进行转换:

场景1:编码器产生的帧,直接存入某个容器的AVStream中,那么此时packet的Time要从AVCodecContext的time转换成目标AVStream的time

 

场景2:从一种容器中demux出来的源AVStream的frame,存入另一个容器中某个目的AVStream。

            此时的时间刻度应该从源AVStream的time,转换成目的AVStream timebase下的时间。

 

其实,问题的关键还是要理解,不同的场景下取到的数据帧的time是相对哪个时间体系的。

demux出来的帧的time:是相对于源AVStream的timebase

编码器出来的帧的time:是相对于源AVCodecContext的timebase

mux存入文件等容器的time:是相对于目的AVStream的timebase

这里的time指pts。






  

最简单的基于FFmpeg的封装格式处理:视音频复用器(muxer)

标签: ffmpegmux复用封装
  18367人阅读  评论(72)  收藏  举报
  分类:
 
 

目录(?)[+]

=====================================================

最简单的基于FFmpeg的封装格式处理系列文章列表:

最简单的基于FFmpeg的封装格式处理:视音频分离器简化版(demuxer-simple)

最简单的基于FFmpeg的封装格式处理:视音频分离器(demuxer)

最简单的基于FFmpeg的封装格式处理:视音频复用器(muxer)

最简单的基于FFMPEG的封装格式处理:封装格式转换(remuxer)

=====================================================


简介

打算记录一下基于FFmpeg的封装格式处理方面的例子。包括了视音频分离,复用,封装格式转换。这是第3篇。

本文记录一个基于FFmpeg的视音频复用器(Simplest FFmpeg muxer)。视音频复用器(Muxer)即是将视频压缩数据(例如H.264)和音频压缩数据(例如AAC)合并到一个封装格式数据(例如MKV)中去。如图所示。在这个过程中并不涉及到编码和解码。



 
本文记录的程序将一个H.264编码的视频码流文件和一个MP3编码的音频码流文件,合成为一个MP4封装格式的文件。

流程

程序的流程如下图所示。从流程图中可以看出,一共初始化了3个AVFormatContext,其中2个用于输入,1个用于输出。3个AVFormatContext初始化之后,通过avcodec_copy_context()函数可以将输入视频/音频的参数拷贝至输出视频/音频的AVCodecContext结构体。然后分别调用视频输入流和音频输入流的av_read_frame(),从视频输入流中取出视频的AVPacket,音频输入流中取出音频的AVPacket,分别将取出的AVPacket写入到输出文件中即可。其间用到了一个不太常见的函数av_compare_ts(),是比较时间戳用的。通过该函数可以决定该写入视频还是音频。


本文介绍的视音频复用器,输入的视频不一定是H.264裸流文件,音频也不一定是纯音频文件。可以选择两个封装过的视音频文件作为输入。程序会从视频输入文件中“挑”出视频流,音频输入文件中“挑”出音频流,再将“挑选”出来的视音频流复用起来。
PS1:对于某些封装格式(例如MP4/FLV/MKV等)中的H.264,需要用到名称为“h264_mp4toannexb”的bitstream filter。
PS2:对于某些封装格式(例如MP4/FLV/MKV等)中的AAC,需要用到名称为“aac_adtstoasc”的bitstream filter。

简单介绍一下流程中各个重要函数的意义:
avformat_open_input():打开输入文件。
avcodec_copy_context():赋值AVCodecContext的参数。
avformat_alloc_output_context2():初始化输出文件。
avio_open():打开输出文件。
avformat_write_header():写入文件头。
av_compare_ts():比较时间戳,决定写入视频还是写入音频。这个函数相对要少见一些。
av_read_frame():从输入文件读取一个AVPacket。
av_interleaved_write_frame():写入一个AVPacket到输出文件。
av_write_trailer():写入文件尾。


代码

下面贴上代码:

[cpp]  view plain  copy
  1. /** 
  2.  * 最简单的基于FFmpeg的视音频复用器 
  3.  * Simplest FFmpeg Muxer 
  4.  * 
  5.  * 雷霄骅 Lei Xiaohua 
  6.  * leixiaohua1020@126.com 
  7.  * 中国传媒大学/数字电视技术 
  8.  * Communication University of China / Digital TV Technology 
  9.  * http://blog.csdn.net/leixiaohua1020 
  10.  * 
  11.  * 本程序可以将视频码流和音频码流打包到一种封装格式中。 
  12.  * 程序中将AAC编码的音频码流和H.264编码的视频码流打包成 
  13.  * MPEG2TS封装格式的文件。 
  14.  * 需要注意的是本程序并不改变视音频的编码格式。 
  15.  * 
  16.  * This software mux a video bitstream and a audio bitstream  
  17.  * together into a file. 
  18.  * In this example, it mux a H.264 bitstream (in MPEG2TS) and  
  19.  * a AAC bitstream file together into MP4 format file. 
  20.  * 
  21.  */  
  22.   
  23. #include <stdio.h>  
  24.   
  25. #define __STDC_CONSTANT_MACROS  
  26.   
  27. #ifdef _WIN32  
  28. //Windows  
  29. extern "C"  
  30. {  
  31. #include "libavformat/avformat.h"  
  32. };  
  33. #else  
  34. //Linux...  
  35. #ifdef __cplusplus  
  36. extern "C"  
  37. {  
  38. #endif  
  39. #include <libavformat/avformat.h>  
  40. #ifdef __cplusplus  
  41. };  
  42. #endif  
  43. #endif  
  44.   
  45. /* 
  46. FIX: H.264 in some container format (FLV, MP4, MKV etc.) need  
  47. "h264_mp4toannexb" bitstream filter (BSF) 
  48.   *Add SPS,PPS in front of IDR frame 
  49.   *Add start code ("0,0,0,1") in front of NALU 
  50. H.264 in some container (MPEG2TS) don't need this BSF. 
  51. */  
  52. //'1': Use H.264 Bitstream Filter   
  53. #define USE_H264BSF 0  
  54.   
  55. /* 
  56. FIX:AAC in some container format (FLV, MP4, MKV etc.) need  
  57. "aac_adtstoasc" bitstream filter (BSF) 
  58. */  
  59. //'1': Use AAC Bitstream Filter   
  60. #define USE_AACBSF 0  
  61.   
  62.   
  63.   
  64. int main(int argc, char* argv[])  
  65. {  
  66.     AVOutputFormat *ofmt = NULL;  
  67.     //Input AVFormatContext and Output AVFormatContext  
  68.     AVFormatContext *ifmt_ctx_v = NULL, *ifmt_ctx_a = NULL,*ofmt_ctx = NULL;  
  69.     AVPacket pkt;  
  70.     int ret, i;  
  71.     int videoindex_v=-1,videoindex_out=-1;  
  72.     int audioindex_a=-1,audioindex_out=-1;  
  73.     int frame_index=0;  
  74.     int64_t cur_pts_v=0,cur_pts_a=0;  
  75.   
  76.     //const char *in_filename_v = "cuc_ieschool.ts";//Input file URL  
  77.     const char *in_filename_v = "cuc_ieschool.h264";  
  78.     //const char *in_filename_a = "cuc_ieschool.mp3";  
  79.     //const char *in_filename_a = "gowest.m4a";  
  80.     //const char *in_filename_a = "gowest.aac";  
  81.     const char *in_filename_a = "huoyuanjia.mp3";  
  82.   
  83.     const char *out_filename = "cuc_ieschool.mp4";//Output file URL  
  84.     av_register_all();  
  85.     //Input  
  86.     if ((ret = avformat_open_input(&ifmt_ctx_v, in_filename_v, 0, 0)) < 0) {  
  87.         printf( "Could not open input file.");  
  88.         goto end;  
  89.     }  
  90.     if ((ret = avformat_find_stream_info(ifmt_ctx_v, 0)) < 0) {  
  91.         printf( "Failed to retrieve input stream information");  
  92.         goto end;  
  93.     }  
  94.   
  95.     if ((ret = avformat_open_input(&ifmt_ctx_a, in_filename_a, 0, 0)) < 0) {  
  96.         printf( "Could not open input file.");  
  97.         goto end;  
  98.     }  
  99.     if ((ret = avformat_find_stream_info(ifmt_ctx_a, 0)) < 0) {  
  100.         printf( "Failed to retrieve input stream information");  
  101.         goto end;  
  102.     }  
  103.     printf("===========Input Information==========\n");  
  104.     av_dump_format(ifmt_ctx_v, 0, in_filename_v, 0);  
  105.     av_dump_format(ifmt_ctx_a, 0, in_filename_a, 0);  
  106.     printf("======================================\n");  
  107.     //Output  
  108.     avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);  
  109.     if (!ofmt_ctx) {  
  110.         printf( "Could not create output context\n");  
  111.         ret = AVERROR_UNKNOWN;  
  112.         goto end;  
  113.     }  
  114.     ofmt = ofmt_ctx->oformat;  
  115.   
  116.     for (i = 0; i < ifmt_ctx_v->nb_streams; i++) {  
  117.         //Create output AVStream according to input AVStream  
  118.         if(ifmt_ctx_v->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){  
  119.         AVStream *in_stream = ifmt_ctx_v->streams[i];  
  120.         AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);  
  121.         videoindex_v=i;  
  122.         if (!out_stream) {  
  123.             printf( "Failed allocating output stream\n");  
  124.             ret = AVERROR_UNKNOWN;  
  125.             goto end;  
  126.         }  
  127.         videoindex_out=out_stream->index;  
  128.         //Copy the settings of AVCodecContext  
  129.         if (avcodec_copy_context(out_stream->codec, in_stream->codec) < 0) {  
  130.             printf( "Failed to copy context from input to output stream codec context\n");  
  131.             goto end;  
  132.         }  
  133.         out_stream->codec->codec_tag = 0;  
  134.         if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)  
  135.             out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;  
  136.         break;  
  137.         }  
  138.     }  
  139.   
  140.     for (i = 0; i < ifmt_ctx_a->nb_streams; i++) {  
  141.         //Create output AVStream according to input AVStream  
  142.         if(ifmt_ctx_a->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){  
  143.             AVStream *in_stream = ifmt_ctx_a->streams[i];  
  144.             AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);  
  145.             audioindex_a=i;  
  146.             if (!out_stream) {  
  147.                 printf( "Failed allocating output stream\n");  
  148.                 ret = AVERROR_UNKNOWN;  
  149.                 goto end;  
  150.             }  
  151.             audioindex_out=out_stream->index;  
  152.             //Copy the settings of AVCodecContext  
  153.             if (avcodec_copy_context(out_stream->codec, in_stream->codec) < 0) {  
  154.                 printf( "Failed to copy context from input to output stream codec context\n");  
  155.                 goto end;  
  156.             }  
  157.             out_stream->codec->codec_tag = 0;  
  158.             if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)  
  159.                 out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;  
  160.   
  161.             break;  
  162.         }  
  163.     }  
  164.   
  165.     printf("==========Output Information==========\n");  
  166.     av_dump_format(ofmt_ctx, 0, out_filename, 1);  
  167.     printf("======================================\n");  
  168.     //Open output file  
  169.     if (!(ofmt->flags & AVFMT_NOFILE)) {  
  170.         if (avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE) < 0) {  
  171.             printf( "Could not open output file '%s'", out_filename);  
  172.             goto end;  
  173.         }  
  174.     }  
  175.     //Write file header  
  176.     if (avformat_write_header(ofmt_ctx, NULL) < 0) {  
  177.         printf( "Error occurred when opening output file\n");  
  178.         goto end;  
  179.     }  
  180.   
  181.   
  182.     //FIX  
  183. #if USE_H264BSF  
  184.     AVBitStreamFilterContext* h264bsfc =  av_bitstream_filter_init("h264_mp4toannexb");   
  185. #endif  
  186. #if USE_AACBSF  
  187.     AVBitStreamFilterContext* aacbsfc =  av_bitstream_filter_init("aac_adtstoasc");   
  188. #endif  
  189.   
  190.     while (1) {  
  191.         AVFormatContext *ifmt_ctx;  
  192.         int stream_index=0;  
  193.         AVStream *in_stream, *out_stream;  
  194.   
  195.         //Get an AVPacket  
  196.         if(av_compare_ts(cur_pts_v,ifmt_ctx_v->streams[videoindex_v]->time_base,cur_pts_a,ifmt_ctx_a->streams[audioindex_a]->time_base) <= 0){  
  197.             ifmt_ctx=ifmt_ctx_v;  
  198.             stream_index=videoindex_out;  
  199.   
  200.             if(av_read_frame(ifmt_ctx, &pkt) >= 0){  
  201.                 do{  
  202.                     in_stream  = ifmt_ctx->streams[pkt.stream_index];  
  203.                     out_stream = ofmt_ctx->streams[stream_index];  
  204.   
  205.                     if(pkt.stream_index==videoindex_v){  
  206.                         //FIX:No PTS (Example: Raw H.264)  
  207.                         //Simple Write PTS  
  208.                         if(pkt.pts==AV_NOPTS_VALUE){  
  209.                             //Write PTS  
  210.                             AVRational time_base1=in_stream->time_base;  
  211.                             //Duration between 2 frames (us)  
  212.                             int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(in_stream->r_frame_rate);  
  213.                             //Parameters  
  214.                             pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);  
  215.                             pkt.dts=pkt.pts;  
  216.                             pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);  
  217.                             frame_index++;  
  218.                         }  
  219.   
  220.                         cur_pts_v=pkt.pts;  
  221.                         break;  
  222.                     }  
  223.                 }while(av_read_frame(ifmt_ctx, &pkt) >= 0);  
  224.             }else{  
  225.                 break;  
  226.             }  
  227.         }else{  
  228.             ifmt_ctx=ifmt_ctx_a;  
  229.             stream_index=audioindex_out;  
  230.             if(av_read_frame(ifmt_ctx, &pkt) >= 0){  
  231.                 do{  
  232.                     in_stream  = ifmt_ctx->streams[pkt.stream_index];  
  233.                     out_stream = ofmt_ctx->streams[stream_index];  
  234.   
  235.                     if(pkt.stream_index==audioindex_a){  
  236.   
  237.                         //FIX:No PTS  
  238.                         //Simple Write PTS  
  239.                         if(pkt.pts==AV_NOPTS_VALUE){  
  240.                             //Write PTS  
  241.                             AVRational time_base1=in_stream->time_base;  
  242.                             //Duration between 2 frames (us)  
  243.                             int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(in_stream->r_frame_rate);  
  244.                             //Parameters  
  245.                             pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);  
  246.                             pkt.dts=pkt.pts;  
  247.                             pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);  
  248.                             frame_index++;  
  249.                         }  
  250.                         cur_pts_a=pkt.pts;  
  251.   
  252.                         break;  
  253.                     }  
  254.                 }while(av_read_frame(ifmt_ctx, &pkt) >= 0);  
  255.             }else{  
  256.                 break;  
  257.             }  
  258.   
  259.         }  
  260.   
  261.         //FIX:Bitstream Filter  
  262. #if USE_H264BSF  
  263.         av_bitstream_filter_filter(h264bsfc, in_stream->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);  
  264. #endif  
  265. #if USE_AACBSF  
  266.         av_bitstream_filter_filter(aacbsfc, out_stream->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);  
  267. #endif  
  268.   
  269.   
  270.         //Convert PTS/DTS  
  271.         pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));  
  272.         pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));  
  273.         pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);  
  274.         pkt.pos = -1;  
  275.         pkt.stream_index=stream_index;  
  276.   
  277.         printf("Write 1 Packet. size:%5d\tpts:%lld\n",pkt.size,pkt.pts);  
  278.         //Write  
  279.         if (av_interleaved_write_frame(ofmt_ctx, &pkt) < 0) {  
  280.             printf( "Error muxing packet\n");  
  281.             break;  
  282.         }  
  283.         av_free_packet(&pkt);  
  284.   
  285.     }  
  286.     //Write file trailer  
  287.     av_write_trailer(ofmt_ctx);  
  288.   
  289. #if USE_H264BSF  
  290.     av_bitstream_filter_close(h264bsfc);  
  291. #endif  
  292. #if USE_AACBSF  
  293.     av_bitstream_filter_close(aacbsfc);  
  294. #endif  
  295.   
  296. end:  
  297.     avformat_close_input(&ifmt_ctx_v);  
  298.     avformat_close_input(&ifmt_ctx_a);  
  299.     /* close output */  
  300.     if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))  
  301.         avio_close(ofmt_ctx->pb);  
  302.     avformat_free_context(ofmt_ctx);  
  303.     if (ret < 0 && ret != AVERROR_EOF) {  
  304.         printf( "Error occurred.\n");  
  305.         return -1;  
  306.     }  
  307.     return 0;  
  308. }  



结果

输入文件为:
视频:cuc_ieschool.ts

音频:huoyuanjia.mp3


输出文件为:
cuc_ieschool.mp4
输出的文件视频为“cuc_ieschool”,配合“霍元甲”的音频。

下载


simplest ffmpeg format


项目主页

SourceForge:https://sourceforge.net/projects/simplestffmpegformat/

Github:https://github.com/leixiaohua1020/simplest_ffmpeg_format

开源中国:http://git.oschina.net/leixiaohua1020/simplest_ffmpeg_format


CSDN下载:

http://download.csdn.net/detail/leixiaohua1020/8005317


工程中包含4个例子:

simplest_ffmpeg_demuxer_simple:视音频分离器(简化版)。

simplest_ffmpeg_demuxer:视音频分离器。

simplest_ffmpeg_muxer:视音频复用器。

simplest_ffmpeg_remuxer:封装格式转换器。


更新-1.1==================================================

修复了以下问题:

(1)Release版本下的运行问题

(2)simplest_ffmpeg_muxer封装H.264裸流的时候丢失声音的错误

关于simplest_ffmpeg_muxer封装H.264裸流的时候丢失声音的问题目前已经解决。根源在于H.264裸流没有PTS,因此必须手动写入PTS。写入PTS的代码在在旧版本中已经包含:

[cpp]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. //FIX:No PTS  
  2. //Simple Write PTS  
  3. if(pkt.pts==AV_NOPTS_VALUE){  
  4.     //Write PTS  
  5.     AVRational time_base1=in_stream->time_base;  
  6.     //Duration between 2 frames (us)  
  7.     int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(in_stream->r_frame_rate);  
  8.     //Parameters  
  9.     pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);  
  10.     pkt.dts=pkt.pts;  
  11.     pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);  
  12.     frame_index++;  
  13. }  
但是旧版本中这段代码的位置放错了,应该放在av_read_frame()之后,cur_pts_a/cur_pts_v赋值之前。换句话说,也就说要把这段代码“前移”。修改后问题解决。

CSDN下载地址:

http://download.csdn.net/detail/leixiaohua1020/8284309


更新-1.2 (2015.2.13)=========================================

这次考虑到了跨平台的要求,调整了源代码。经过这次调整之后,源代码可以在以下平台编译通过:

VC++:打开sln文件即可编译,无需配置。

cl.exe:打开compile_cl.bat即可命令行下使用cl.exe进行编译,注意可能需要按照VC的安装路径调整脚本里面的参数。编译命令如下。

[plain]  view plain  copy
  1. ::VS2010 Environment  
  2. call "D:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"  
  3. ::include  
  4. @set INCLUDE=include;%INCLUDE%  
  5. ::lib  
  6. @set LIB=lib;%LIB%  
  7. ::compile and link  
  8. cl simplest_ffmpeg_muxer.cpp /link avcodec.lib avformat.lib avutil.lib ^  
  9. avdevice.lib avfilter.lib postproc.lib swresample.lib swscale.lib /OPT:NOREF  

MinGW:MinGW命令行下运行compile_mingw.sh即可使用MinGW的g++进行编译。编译命令如下。

[plain]  view plain  copy
  1. g++ simplest_ffmpeg_muxer.cpp -g -o simplest_ffmpeg_muxer.exe \  
  2. -I /usr/local/include -L /usr/local/lib -lavformat -lavcodec -lavutil  

GCC:Linux或者MacOS命令行下运行compile_gcc.sh即可使用GCC进行编译。编译命令如下。

[plain]  view plain  copy
  1. gcc simplest_ffmpeg_muxer.cpp -g -o simplest_ffmpeg_muxer.out -I /usr/local/include -L /usr/local/lib \  
  2. -lavformat -lavcodec -lavutil  
PS:相关的编译命令已经保存到了工程文件夹中

CSDN下载地址: http://download.csdn.net/detail/leixiaohua1020/8445303
SourceForge上已经更新。











视频、音频打时间戳的方法

一 固定帧率

1. 视频时间戳

     pts = inc++ *(1000/fps);  其中inc是一个静态的,初始值为0,每次打完时间戳inc加1.

    在ffmpeg,中的代码

    pkt.pts= m_nVideoTimeStamp++ * (m_VCtx->time_base.num * 1000 / m_VCtx->time_base.den);

 

2. 音频时间戳

    pts = inc++ * (frame_size * 1000 / sample_rate)

   在ffmpeg中的代码为

   pkt.pts= m_nAudioTimeStamp++ * (m_ACtx->frame_size * 1000 / m_ACtx->sample_rate);

采样频率是指将模拟声音波形进行数字化时,每秒钟抽取声波幅度样本的次数。

。正常人听觉的频率范围大约在20Hz~20kHz之间,根据奈奎斯特采样理论,为了保证声音不失真,采样频率应该在40kHz左右。常用的音频采样频率有8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz等,如果采用更高的采样频率,还可以达到DVD的音质

对采样率为44.1kHz的AAC音频进行解码时,一帧的解码时间须控制在23.22毫秒内。

背景知识:

(一个AAC原始帧包含一段时间内1024个采样及相关数据)

分析:

1 AAC

音频帧的播放时间=一个AAC帧对应的采样样本的个数/采样频率(单位为s)

一帧 1024个 sample。采样率 Samplerate 44100KHz,每秒44100个sample, 所以 根据公式   音频帧的播放时间=一个AAC帧对应的采样样本的个数/采样频率

当前AAC一帧的播放时间是= 1024*1000000/44100= 22.32ms(单位为ms)

2 MP3

 

mp3 每帧均为1152个字节, 则:

frame_duration = 1152 * 1000000 / sample_rate

例如:sample_rate = 44100HZ时, 计算出的时长为26.122ms,这就是经常听到的mp3每帧播放时间固定为26ms的由来。



二 可变帧率


有很多的采集卡,摄像头,在做采集的时候,明明设置的25FPS,但实际采集数据回调过来,发现并不是40毫秒的间隔,而是50,60,甚至100不等的时间间隔。

这就给编码后打时间戳带来很大的困难。

在libav里,我们的默认编码参数都是:

ptAvEncoder->ptAvStreamVideo->codec->time_base.den = s32Fps;

ptAvEncoder->ptAvStreamVideo->codec->time_base.num = 1;

这样在编码后的时间戳以1递增,只适合于固定帧率。

我们来改一下:

ptAvEncoder->ptAvStreamVideo->codec->time_base.den = s32Fps * 1000;

ptAvEncoder->ptAvStreamVideo->codec->time_base.num = 1* 1000;

这样就把时间戳的scale变成了毫秒,就可以以毫秒为单位进行计算了,如下:

tAvPacket.pts = ((s64)u32TimeStamp * (s64)s32Fps);

u32TimeStamp是从开始记录的时间差值,以毫秒为单位;s32Fps是帧率。

对于音频,mp4文件默认是采样率为tick的,时间戳计算为:

tAvPacket.pts = (AvEncoderAudioInSizeGet(hHandle) * ( (s64)(u32TimeStamp)) / (AvEncoderAudioInSizeGet(hHandle) * 1000 / ptAvEncoder->ptAvStreamAudio->codec->sample_rate);

AvEncoderAudioInSizeGet(hHandle) 每次编码器需要的PCM数据长度。

u32TimeStamp是从开始记录的时间差值,以毫秒为单位。

 ptAvEncoder->ptAvStreamAudio->codec->sample_rate PCM采样率,代表一秒的数据量。

因为乘以了1000,所以也化成了毫秒单位。

对于mp4,视频直接用绝对时间,音频用数据量,对rtmp,视频是毫秒计算,音频也换算成毫秒计算

### 回答1: 在FFmpeg,probesize是一个参数,用于指定在解码媒体文件时读取的最大数据量。它的单位是字节(byte)。默认是5MB(即5242880字节),可以通过在命令行使用-probesize选项来更改它的。例如,要将probesize设置为10MB,可以使用以下命令: ``` ffmpeg -probesize 10485760 -i input.mp4 output.avi ``` 这将将probesize设置为10MB,以便在解码input.mp4文件时读取更多的数据。 ### 回答2: 在FFmpeg,probesize是一个用来指定探测数据包大小的选项。它的单位是字节(bytes)。 在解码音视频时,FFmpeg需要通过探测器(prober)分析文件的内容来确定文件的格式和编码方式。探测器会读取文件的一部分数据进行分析,并尝试解析其的信息来确定文件的格式和编码信息。而probesize就是用来指定探测器读取的数据包的大小。 probesize的越大,探测器读取的数据就越多,可以提高探测的准确性,但也会增加解码的延迟和消耗更多的内存。相反,probesize的越小,解码会更快但可能导致探测错误,无法正确解码音视频文件。 通常情况下,FFmpeg默认的probesize是32K(即32 * 1024 bytes)。可以通过命令行参数或API接口来设置probesize的。例如,使用命令行参数设置probesize为64K可以使用以下命令: ffmpeg -probesize 64K -i input.mp4 output.mp4 总之,probesize是用来指定FFmpeg探测器读取的数据包大小的选项,单位是字节。通过适当设置probesize的可以平衡解码的准确性和速度,以及内存消耗。 ### 回答3: ffmepg的probesize是用来设置媒体文件探测的大小的参数。该参数的单位是字节数。 当我们使用ffmpeg来处理一个媒体文件时,ffmpeg会在读取文件之前确定文件的类型和格式。在确定文件类型和格式之前,ffmpeg会读取文件的一部分数据来进行探测。probesize就是用来设置读取的数据大小的。 如果我们将probesize设置得太小,ffmpeg可能会无法正确地识别文件类型和格式。这将导致ffmpeg无法正常处理文件。 相反,如果我们将probesize设置得太大,ffmpeg将会读取更多的数据,这可能会导致处理时间增加和内存占用增加。 因此,设置适当的probesize对于ffmpeg的正常运行非常重要。根据实际情况,可以根据文件大小、压缩格式等因素来确定probesize的大小。 需要注意的是,probesize只是用来探测文件类型和格式的,并不是用来处理文件内容的大小。所以,它并不会影响到文件的编码和解码过程。 总之,ffmpeg的probesize参数是用来设置媒体文件探测大小的,单位为字节数。设置适当的probesize对于保证ffmpeg正常运行是非常重要的。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值