//MediaConverter.h
extern "C"
{
//@param in_format_name 输入流的格式名
//@param out_format_name 输出流的格式名
//@param in_callback 输入媒体数据的回调函数
//@param out_callback 输出媒体数据的回调函数
//@return 输出的数据结构指针,该数据结构包含了所有媒体信息
__declspec(dllexport) void *open_format(const char *in_format_name,
const char *out_format_name,
int(*in_callback)(void *opaque, uint8_t *buf, int buf_size),
int(*out_callback)(void *opaque, uint8_t *buf, int buf_size));
//@param fmt_ctx format_data_import()的返回值
__declspec(dllexport) int close_format(void *fmt_ctx);
}
// MediaConverter.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#define __STDC_CONSTANT_MACROS
extern "C"
{
#include "libavformat/avformat.h"
}
#include "MediaConverter.h"
#define IO_BUFFER_SIZE 32768
void *open_format(const char *in_format_name,
const char *out_format_name,
int(*in_callback)(void *opaque, uint8_t *buf, int buf_size),
int(*out_callback)(void *opaque, uint8_t *buf, int buf_size))
{
AVInputFormat *ifmt = NULL;
AVOutputFormat *ofmt = NULL;
AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
int ret, frame_index = 0;
av_register_all();
//用户传入的可能是扩展名,而扩展名和格式名可能不同,所以在此作转换。
if (!strcmp(in_format_name, "ts"))
in_format_name = "mpegts";
else if (!strcmp(in_format_name, "mpg"))
in_format_name = "mpeg";
if (!strcmp(out_format_name, "mkv"))
out_format_name = "matroska";
else if (!strcmp(out_format_name, "ts"))
out_format_name = "mpegts";
else if (!strcmp(out_format_name, "mpg"))
out_format_name = "mpeg";
ifmt_ctx = avformat_alloc_context();
uint8_t *inbuffer = (uint8_t *)av_malloc(IO_BUFFER_SIZE);
AVIOContext *in_avio = avio_alloc_context(inbuffer, IO_BUFFER_SIZE, 0, NULL, in_callback, NULL, NULL);
ifmt_ctx->pb = in_avio;
ifmt_ctx->flags = AVFMT_FLAG_CUSTOM_IO;
ifmt = av_find_input_format(in_format_name);
if ((ret = avformat_open_input(&ifmt_ctx, "nothing", ifmt, NULL)) < 0)
{
printf("Error occurred, avformat_open_input()\n");
goto end;
}
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0)
{
printf("Failed to retrieve input stream information\n");
goto end;
}
avformat_alloc_output_context2(&ofmt_ctx, NULL, out_format_name, NULL);
if (!ofmt_ctx)
{
printf("Could not create output context\n");
ret = AVERROR_UNKNOWN;
goto end;
}
ofmt = ofmt_ctx->oformat;
uint8_t *outbuffer = (uint8_t *)av_malloc(IO_BUFFER_SIZE);
AVIOContext *out_avio = avio_alloc_context(outbuffer, IO_BUFFER_SIZE, 1, NULL, NULL, out_callback, NULL);
ofmt_ctx->pb = out_avio;
ofmt_ctx->flags = AVFMT_FLAG_CUSTOM_IO;
AVStream *in_stream = ifmt_ctx->streams[0];
AVStream *out_stream = avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream)
{
printf("Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
goto end;
}
//Copy the settings of AVCodecContext
if (avcodec_copy_context(out_stream->codec, in_stream->codec) < 0) {
printf("Failed to copy context from input to output stream codec context\n");
goto end;
}
out_stream->codec->codec_tag = 0;
/* Some formats want stream headers to be separate. */
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
//Write file header
if (avformat_write_header(ofmt_ctx, NULL) < 0)
{
printf("Error occurred when opening output file\n");
goto end;
}
while (1)
{
AVPacket *pkt = av_packet_alloc();
av_init_packet(pkt);
//Get an AVPacket
if (av_read_frame(ifmt_ctx, pkt) >= 0)
{
do{
if (pkt->pts == AV_NOPTS_VALUE){
//Duration between 2 frames (μs)
int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
pkt->pts = (double)(frame_index*calc_duration) / (double)(av_q2d(in_stream->time_base)*AV_TIME_BASE);
pkt->dts = pkt->pts;
pkt->duration = (double)calc_duration / (double)(av_q2d(in_stream->time_base)*AV_TIME_BASE);
frame_index++;
}
break;
} while (av_read_frame(ifmt_ctx, pkt) >= 0);
}
else{
break;
}
//Convert PTS/DTS
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));
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));
pkt->duration = av_rescale_q(pkt->duration, in_stream->time_base, out_stream->time_base);
printf("Write 1 Packet. size:%5d\tpts:%lld\n", pkt->size, pkt->pts);
//Write
if (av_interleaved_write_frame(ofmt_ctx, pkt) < 0) {
printf("Error muxing packet\n");
break;
}
av_packet_free(&pkt);
}
avformat_close_input(&ifmt_ctx);
return ofmt_ctx;
end:
avformat_close_input(&ifmt_ctx);
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_close(ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
return NULL;
}
int close_format(void *fmt_ctx)
{
AVFormatContext *ofmt_ctx = (AVFormatContext *)fmt_ctx;
//Write file trailer
av_write_trailer(ofmt_ctx);
if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
avio_close(ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
return 0;
}
// test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <stdint.h>
#include <malloc.h>
#include "MediaConverter.h"
#define BUFFER_SIZE 32768
FILE *fp_read;
FILE *fp_write;
int in_callback(void *opaque, uint8_t *buf, int buf_size)
{
if (!feof(fp_read)){
int true_size = fread(buf, 1, buf_size, fp_read);
return true_size;
}
else{
return -1;
}
}
int out_callback(void *opaque, uint8_t *buf, int buf_size)
{
fwrite(buf, 1, buf_size, fp_write);
return buf_size;
}
int _tmain(int argc, _TCHAR* argv[])
{
const char *in_filename_v = "media files/no9.mpeg"; //Input file URL
//const char *out_filename = "media files/JINUSEAN_17s.mkv"; //Output file URL
fp_write = fopen("media files/mpeg2ts.ts", "wb+"); //输出文件
const char *in_format_name = "mpeg";
const char *out_format_name = "mpegts";
fp_read = fopen(in_filename_v, "rb+");
void *ofmt_ctx = open_format(in_format_name, out_format_name, in_callback, out_callback);
close_format(ofmt_ctx);
fclose(fp_read);
fclose(fp_write);
return 0;
}