本例修改Tstools的esmerge.c示例 部分更改库内容,完成读取H264文件和AAC文件封装成TS文件。
使用函数指针做参数传递,达到把读取源文件和写目标文件方法提到更好操作的地方。
1. 下载tstools源码
官方网站: http://tstool.sourceforge.net/
CSDN资源下载:http://download.csdn.net/download/u011298831/9596746
2. 编译
源码编译时我出现了错误
- LIBOPTS = -L$(LIBDIR) -ltstools $(ARCH_FLAGS)
+ LIBOPTS = -L$(LIBDIR) -ltstools $(ARCH_FLAGS) -lm
修改Makefile之后即可正常编译
3. 参照esmerge.c添加自己的esmerge_plus.c
Makefile中参考esmerge的方法添加esmerge_plus.c的方法
$(OBJDIR)/esmerge.o \
+$(OBJDIR)/esmerge_plus.o \
$(BINDIR)/esmerge \
+$(BINDIR)/esmerge_plus \
$(BINDIR)/esmerge:$(OBJDIR)/esmerge.o $(LIB)
$(CC) $< -o $(BINDIR)/esmerge $(LDFLAGS) $(LIBOPTS)
+$(BINDIR)/esmerge_plus:$(OBJDIR)/esmerge_plus.o $(LIB)
$(CC) $< -o $(BINDIR)/esmerge_plus $(LDFLAGS) $(LIBOPTS)
$(OBJDIR)/esmerge.o: esmerge.c misc_fns.h $(ACCESSUNIT_H) $(AUDIO_H) $(TSWRITE_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
+$(OBJDIR)/esmerge_plus.o: esmerge_plus.c misc_fns.h $(ACCESSUNIT_H) $(AUDIO_H) $(TSWRITE_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
这样执行make即可获得bin\下的esmerge_plus生成文件
4. 正式修改库和esmerge_plus.c文件
源esmerge.c文件是采用传递文件名字和文件句柄的方式,些ts目标文件也在较为深的地方,现在的目的是吧读源文件的和写目标文件提到main函数层次,方便实现read和write
先提供main函数。
- int main(int argc, char **argv)
- {
- int had_video_name = FALSE;
- int had_audio_name = FALSE;
- int had_output_name = FALSE;
- char *video_name = NULL;
- char *audio_name = NULL;
- char *output_name = NULL;
- int err = 0;
- ES_p video_es = NULL;
- access_unit_context_p h264_video_context = NULL;
- avs_context_p avs_video_context = NULL;
- int audio_file = -1;
- TS_writer_p output = NULL;
- int quiet = FALSE;
- int verbose = FALSE;
- int debugging = FALSE;
- int audio_samples_per_frame = ADTS_SAMPLES_PER_FRAME;
- int audio_sample_rate = DAT_RATE;
- int video_frame_rate = DEFAULT_VIDEO_FRAME_RATE;
- int audio_type = AUDIO_ADTS;
- int video_type = VIDEO_H264;
- int pat_pmt_freq = 0;
- int ii = 1;
-
- #if TEST_PTS_DTS
- test_pts();
- return 0;
- #endif
-
- if (argc < 2)
- {
- print_usage();
- return 0;
- }
-
- video_type = VIDEO_H264;
- read_file = video_name = argv[1];
- audio_read_file = audio_name = argv[2];
- write_file = output_name = argv[3];
- fprintf(stderr, "### esmerge: video_name:%s \n", video_name);
- fprintf(stderr, "### esmerge: audio_name:%s \n", audio_name);
- fprintf(stderr, "### esmerge: output_name:%s\n", output_name);
-
-
- err = open_elementary_stream_ex(video_read_func, &video_es);
- if (err)
- {
- fprintf(stderr, "### esmerge: "
- "Problem starting to read video as ES - abandoning reading\n");
- return 1;
- }
-
- if (video_type == VIDEO_H264)
- {
- err = build_access_unit_context(video_es, &h264_video_context);
- if (err)
- {
- fprintf(stderr, "### esmerge: "
- "Problem starting to read video as H.264 - abandoning reading\n");
- close_elementary_stream(&video_es);
- return 1;
- }
- }
- else
- {
- fprintf(stderr, "### esmerge: Unknown video type\n");
- return 1;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- err = tswrite_open_ex(TS_W_CALL, video_write_func, NULL, 0, quiet, &output);
- if (err)
- {
- fprintf(stderr, "### esmerge: "
- "Problem opening output file %s - abandoning reading\n",
- output_name);
- close_elementary_stream(&video_es);
- close_file(audio_file);
- free_access_unit_context(&h264_video_context);
- free_avs_context(&avs_video_context);
- return 1;
- }
-
- switch (audio_type)
- {
- case AUDIO_ADTS:
- audio_samples_per_frame = ADTS_SAMPLES_PER_FRAME;
- break;
- default:
- audio_samples_per_frame = ADTS_SAMPLES_PER_FRAME;
- break;
- }
-
- if (!quiet)
- {
- printf("Reading video from %s\n", video_name);
- printf("Writing output to %s\n", output_name);
- printf("Video frame rate: %dHz\n", video_frame_rate);
- }
-
- if (video_type == VIDEO_H264)
-
- err = merge_with_h264(h264_video_context, audio_read_func, output,
-
- AUDIO_ADTS_MPEG2,
- audio_samples_per_frame, audio_sample_rate,
- video_frame_rate,
- pat_pmt_freq,
- quiet, verbose, debugging);
- else
- {
- printf("### esmerge: Unknown video type\n");
- return 1;
- }
- if (err)
- {
- printf("### esmerge: Error merging video and audio streams\n");
- close_elementary_stream(&video_es);
- free_access_unit_context(&h264_video_context);
- (void)tswrite_close(output, quiet);
- return 1;
- }
-
- close_elementary_stream(&video_es);
- free_access_unit_context(&h264_video_context);
- err = tswrite_close(output, quiet);
- if (err)
- {
- printf("### esmerge: Error closing output %s\n", output_name);
- return 1;
- }
- return 0;
- }
本例只支持baseline/main profile的H264和-mp2adts AAC文件,读者自己根据源码做相应修改。
两个新添加的函数:
err = open_elementary_stream_ex(video_read_func, &video_es);
err = tswrite_open_ex(TS_W_CALL, video_write_func, NULL, 0, quiet, &output);
1)open_elementary_stream_ex (es.c跟踪open_elementary_stream函数添加以下代码)
- extern int open_elementary_stream_ex(int (*callreadfun)(char*, int),
- ES_p *es)
- {
- int err;
- int input;
-
- input = -1;
-
- err = build_elementary_stream_file_ex(callreadfun,es);
- if (err)
- {
- fprintf(stderr,"### build_elementary_stream_file_ex ");
- return 1;
- }
- return 0;
- }
同上,在相应位置添加build_elementary_stream_file_ex
(es.c)
方法
- extern int build_elementary_stream_file_ex(int (*callreadfun)(char*, int),
- ES_p *es)
- {
- ES_p new = malloc(SIZEOF_ES);
- if (new == NULL)
- {
- fprintf(stderr,"### Unable to allocate elementary stream datastructure\n");
- return 1;
- }
-
- new->self_input = 1;
- new->callreadfun = callreadfun;
- new->reading_ES = TRUE;
- new->input = -1;
- new->reader = NULL;
-
- setup_readahead(new);
-
- *es = new;
- return 0;
- }
这里出现两个ES_p结构原本没有的成员:self_input和callreadfun
struct elementary_stream
{
int reading_ES; // TRUE if we're reading ES data direct, FALSE if PES
+//self define read
+int self_input;
+int (*callreadfun)(char* data, int* len);
……
};
如上,在结构体elementary_stream(es_defns.h)中添加了self_input和 callreadfun,用作描述是否是个人定义的方式和传递读数据的函数方法
2)tswrite_open_ex(TS_W_CALL, video_write_func, NULL, 0, quiet, &output); (tswrite.c 跟踪tswrite_open函数添加以下代码)
- extern int tswrite_open_ex(TS_WRITER_TYPE how,
- void (*callfuct)(char*,int),
- char *multicast_if,
- int port,
- int quiet,
- TS_writer_p *tswriter)
- {
- TS_writer_p new;
- int err = tswrite_build(how,quiet,tswriter);
- if (err) return 1;
-
- new = *tswriter;
- switch (how)
- {
- case TS_W_CALL:
- if (!quiet) printf("TS_W_CALL\n");
- new->where.callfun = callfuct;
- break;
- default:
- printf("### Unexpected writer type %d to tswrite_open()\n",how);
- free(new);
- return 1;
- }
- return 0;
- }
同1)出现TS_writer_p结构出现where.callfun新的结构
union TS_writer_output
{
FILE *file;
SOCKET socket;
//for ts use
+void (*callfun)(char*,int);
};
如上,在联合体TS_writer_output(tswrite_defns.h)中添加了 callfun,写数据的函数方法
读者应该对tstools有一定的熟悉,了解其工作流程。所以对tswrite_write方法 tswrite_close_file方法 static inline int get_more_data(ES_p es)方法需要修改
下面给我我修改后的static inline int get_more_data(ES_p es)方法(es.c文件中)
- static inline int get_more_data(ES_p es)
- {
- if (es->reading_ES)
- {
- if(es->self_input == 1)
- {
- int len = 0;
- int ret = es->callreadfun(es->read_ahead,&len);
-
- if (ret == 0)
- return EOF;
- else if (ret == -1)
- {
- printf("### Error reading next bytes: %s\n",strerror(errno));
- return 1;
- }
-
- es->read_ahead_posn += es->read_ahead_len;
- es->read_ahead_len = len;
- es->data = es->read_ahead;
- es->data_end = es->data + len;
- es->data_ptr = es->data;
- }
- else
- {
-
-
- #ifdef _WIN32
- int len = _read(es->input,&es->read_ahead,ES_READ_AHEAD_SIZE);
- #else
- ssize_t len = read(es->input,&es->read_ahead,ES_READ_AHEAD_SIZE);
- #endif
- printf("### Error reading next bytes: %d\n",len);
- if (len == 0)
- return EOF;
- else if (len == -1)
- {
- printf("### Error reading next bytes: %s\n",strerror(errno));
- return 1;
- }
-
- es->read_ahead_posn += es->read_ahead_len;
- es->read_ahead_len = len;
- es->data = es->read_ahead;
- es->data_end = es->data + len;
- es->data_ptr = es->data;
- }
-
- return 0;
- }
- else
- {
- return get_next_pes_packet(es);
- }
- }
tswrite_write方法中,为了区分出是我们新定义的方式,我们使用TS_W_CALL这个值(tswrite.c文件中)
switch (tswriter->how)
{
……
+case TS_W_CALL:
+ tswriter->where.callfun(packet,TS_PACKET_SIZE);
+break;
……
}
tswrite_close_file方法中,为了区分出是我们新定义的方式,我们使用TS_W_CALL这个值(tswrite.c文件中)
switch (tswriter->how)
{
……
+case TS_W_CALL:
+ break;
……
}
TS_W_CALL所加在的地方,查找TS_W_FILE即可
enum TS_writer_type
{
TS_W_UNDEFINED,
TS_W_STDOUT, // standard output
TS_W_FILE, // a file
TS_W_TCP, // a socket, over TCP/IP
TS_W_UDP, // a socket, over UDP
+TS_W_CALL, // self define
};
如上,在枚举TS_writer_type(tswrite_defns.h)中添加了 TS_W_CALL
就这样,对原库代码的修改完成,回到esmerge_plus.c文件中。
3)merge_with_h264方法
- static int merge_with_h264(access_unit_context_p video_context,
- int(*callbackfun)(char*, int*),
- TS_writer_p output,
- int audio_type,
- int audio_samples_per_frame,
- int audio_sample_rate,
- int video_frame_rate,
- int pat_pmt_freq,
- int quiet,
- int verbose,
- int debugging)
- {
- int ii;
- int err;
- uint32_t prog_pids[2];
- byte prog_type[2];
-
- int video_frame_count = 0;
- int audio_frame_count = 0;
-
- uint32_t video_pts_increment = 90000 / video_frame_rate;
- uint32_t audio_pts_increment = (90000 * audio_samples_per_frame) / audio_sample_rate;
- uint64_t video_pts = 0;
- uint64_t audio_pts = 0;
-
-
-
- double audio_time = 0.0;
- double video_time = 0.0;
-
- int got_video = TRUE;
- int got_audio = TRUE;
-
- if (verbose)
- printf("Video PTS increment %u\n"
- "Audio PTS increment %u\n", video_pts_increment, audio_pts_increment);
-
-
-
-
- for (ii = 0; ii < 8; ii++)
- {
- err = write_TS_null_packet(output);
- if (err) return 1;
- }
-
-
-
- prog_pids[0] = DEFAULT_VIDEO_PID;
- prog_pids[1] = DEFAULT_AUDIO_PID;
- prog_type[0] = AVC_VIDEO_STREAM_TYPE;
-
- switch (audio_type)
- {
- case AUDIO_ADTS:
- case AUDIO_ADTS_MPEG2:
- case AUDIO_ADTS_MPEG4:
- prog_type[1] = ADTS_AUDIO_STREAM_TYPE;
- break;
- case AUDIO_L2:
- prog_type[1] = MPEG2_AUDIO_STREAM_TYPE;
- break;
- case AUDIO_AC3:
- prog_type[1] = ATSC_DOLBY_AUDIO_STREAM_TYPE;
- break;
- default:
- prog_type[1] = ADTS_AUDIO_STREAM_TYPE;
- break;
- }
- err = write_TS_program_data2(output,
- 1,
- 1,
- DEFAULT_PMT_PID,
- DEFAULT_VIDEO_PID,
- 2, prog_pids, prog_type);
- if (err)
- {
- fprintf(stderr, "### Error writing out TS program data\n");
- return 1;
- }
-
- while (got_video
- || got_audio
- )
- {
- access_unit_p access_unit;
- audio_frame_p aframe;
-
-
- if (got_video)
- {
- err = get_next_h264_frame(video_context, quiet, debugging, &access_unit);
- if (err == EOF)
- {
- if (verbose)
- fprintf(stderr, "EOF: no more video data\n");
- got_video = FALSE;
- }
- else if (err)
- {
- fprintf(stderr, "EOF: no more video data return 1;\n");
- return 1;
- }
-
- }
-
- if (got_video)
- {
- video_time = video_frame_count / (double)video_frame_rate;
- video_pts += video_pts_increment;
- video_frame_count++;
- if (verbose)
- printf("\n%s video frame %5d (@ %.2fs, " LLU_FORMAT ")\n",
- (is_I_or_IDR_frame(access_unit) ? "**" : "++"),
- video_frame_count, video_time, video_pts);
-
- if (pat_pmt_freq && !(video_frame_count % pat_pmt_freq))
- {
- if (verbose)
- {
- printf("\nwriting PAT and PMT (frame = %d, freq = %d).. ",
- video_frame_count, pat_pmt_freq);
- }
- err = write_TS_program_data2(output,
- 1,
- 1,
- DEFAULT_PMT_PID,
- DEFAULT_VIDEO_PID,
- 2, prog_pids, prog_type);
- }
-
-
-
-
-
-
-
-
-
-
-
- if (is_I_or_IDR_frame(access_unit))
- err = write_access_unit_as_TS_with_pts_dts(access_unit, video_context,
- output, DEFAULT_VIDEO_PID,
- TRUE, video_pts + 45000,
- TRUE, video_pts);
- else
- err = write_access_unit_as_TS_with_PCR(access_unit, video_context,
- output, DEFAULT_VIDEO_PID,
- video_pts, 0);
- if (err)
- {
- free_access_unit(&access_unit);
- fprintf(stderr, "### Error writing access unit (frame)\n");
- return 1;
- }
- free_access_unit(&access_unit);
-
-
- if (video_context->end_of_stream)
- {
- if (verbose)
- printf("Found End-of-stream NAL unit\n");
- got_video = FALSE;
- }
- }
-
-
- if (!got_audio || callbackfun == NULL)
- continue;
-
-
- while (audio_pts < video_pts || !got_video)
- {
-
- char aframe_buf[1024];
- int ret = callbackfun(aframe_buf, 1024);
- fprintf(stderr, "callbackfun audio data ret:%d\n", ret);
- if (ret <= 0)
- {
- got_audio = FALSE;
- break;
- }
-
- audio_time = audio_frame_count *
- audio_samples_per_frame / (double)audio_sample_rate;
- audio_pts += audio_pts_increment;
- audio_frame_count++;
- if (verbose)
- printf("** audio frame %5d (@ %.2fs, " LLU_FORMAT ")\n",
- audio_frame_count, audio_time, audio_pts);
-
- err = write_ES_as_TS_PES_packet_with_pts_dts(output, aframe_buf,
- ret,
- DEFAULT_AUDIO_PID,
- DEFAULT_AUDIO_STREAM_ID,
- TRUE, audio_pts,
- TRUE, audio_pts);
- if (err)
- {
- return 1;
- }
- }
- }
-
- if (!quiet)
- {
- uint32_t video_elapsed = 100 * video_frame_count / video_frame_rate;
- uint32_t audio_elapsed = 100 * audio_frame_count*
- audio_samples_per_frame / audio_sample_rate;
- printf("Read %d video frame%s, %.2fs elapsed (%dm %.2fs)\n",
- video_frame_count, (video_frame_count == 1 ? "" : "s"),
- video_elapsed / 100.0, video_elapsed / 6000, (video_elapsed % 6000) / 100.0);
- printf("Read %d audio frame%s, %.2fs elapsed (%dm %.2fs)\n",
- audio_frame_count, (audio_frame_count == 1 ? "" : "s"),
- audio_elapsed / 100.0, audio_elapsed / 6000, (audio_elapsed % 6000) / 100.0);
- }
-
- return 0;
-
- }
然后是几个读写方法
- #include <fcntl.h>
- int video_fd_r = -1;
- int video_fd_w = -1;
- int audio_fd_r = -1;
-
- char* read_file = NULL;
- char* write_file = NULL;
- char* audio_read_file = NULL;
-
- int video_write_func(char* data, int len)
- {
- if (video_fd_w == -1)
- {
- int flags = 0;
- flags = flags | O_WRONLY | O_CREAT | O_TRUNC;
- video_fd_w = open(write_file, flags, 00777);
-
- if (video_fd_w == -1)
- {
- fprintf(stderr, "### Error opening file %s %s\n", write_file, strerror(errno));
- }
- else
- {
- fprintf(stderr, "### opening file %s %d\n", write_file, video_fd_w);
- }
- }
-
- int _len = write(video_fd_w, data, len);
- if (_len == len)
- {
-
- }
- else
- {
-
- }
-
- }
-
- int audio_read_func(char* data, int len)
- {
- if (audio_fd_r == -1)
- {
- int flags = 0;
- flags = flags | O_RDONLY;
- audio_fd_r = open(audio_read_file, flags);
-
- if (audio_fd_r == -1)
- {
- fprintf(stderr, "###audio_read_func Error opening file %s %s\n", audio_read_file, strerror(errno));
- }
- else
- {
- fprintf(stderr, "###audio_read_func opening file %s %d\n", audio_read_file, audio_fd_r);
- }
- }
-
- unsigned char aac_header[7];
- int true_size = 0;
-
- true_size = read(audio_fd_r, aac_header, 7);
- if (true_size <= 0)
- {
- return 0;
- }
- else
- {
- int frame_length = ((aac_header[3] & 0x03) << 11) | (aac_header[4] << 3) |
- ((unsigned)(aac_header[5] & 0xE0) >> 5);
-
- int ii;
- for (ii=0; ii<6; ii++)
- data[ii] = aac_header[ii];
-
- true_size = read(audio_fd_r, &(data[7]), frame_length - 7);
- return frame_length;
- }
-
- }
-
- int video_read_func(char* data, int* len)
- {
- if (video_fd_r == -1)
- {
- int flags = 0;
- flags = flags | O_RDONLY;
- video_fd_r = open(read_file, flags);
-
- if (video_fd_r == -1)
- {
- fprintf(stderr, "### Error opening file %s %s\n", read_file, strerror(errno));
- }
- else
- {
- fprintf(stderr, "### opening file %s %d\n", read_file, video_fd_r);
- }
- }
- *len = read(video_fd_r, data, ES_READ_AHEAD_SIZE);
- if (*len > 0)
- {
-
- }
- else if (*len < 0)
- {
-
- }
- return *len;
- }
搞定!记得修改几个宏定义和初始化的值;
#define DEFAULT_VIDEO_FRAME_RATE 30
int audio_sample_rate = DAT_RATE; //我用的是48000Hz的
最后声明:本例只支持baseline/main profile的H264和-mp2adts AAC文件,读者自己根据源码做相应修改。
附上我的源码:http://download.csdn.net/download/u011298831/9596805
转自http://blog.csdn.net/u011298831/article/details/52135937