linux ios开发环境,Linux下搭建支持IOS http streaming的环境

#include

#include

#include

#include

#include

#include

#include

#include

#include "libavformat/avformat.h"

#include "libavutil/opt.h"

#define MAX_LENGTH 1024

typedef struct iputargs_t{

char input[MAX_LENGTH];

char output_prefix[MAX_LENGTH];

char output[MAX_LENGTH];

}iputargs_t;

static iputargs_t input_args;

//----------------------------------------------------------------

// fopen_utf8

//

static FILE *

fopen_utf8(const char * filename, const char * mode)

{

FILE * file = NULL;

file = fopen(filename, mode);

return file;

}

static AVStream *add_output_stream(AVFormatContext *output_format_context, AVStream *input_stream) {

AVCodecContext *input_codec_context;

AVCodecContext *output_codec_context;

AVStream *output_stream;

output_stream = avformat_new_stream(output_format_context, NULL);

if (!output_stream) {

fprintf(stderr, "Could not allocate stream\n");

exit(1);

}

output_stream->id = 0;

input_codec_context = input_stream->codec;

output_codec_context = output_stream->codec;

output_codec_context->codec_id = input_codec_context->codec_id;

output_codec_context->codec_type = input_codec_context->codec_type;

output_codec_context->codec_tag = input_codec_context->codec_tag;

output_codec_context->bit_rate = input_codec_context->bit_rate;

output_codec_context->extradata = input_codec_context->extradata;

output_codec_context->extradata_size = input_codec_context->extradata_size;

if(av_q2d(input_codec_context->time_base) * input_codec_context->ticks_per_frame > av_q2d(input_stream->time_base) && av_q2d(input_stream->time_base) < 1.0/1000) {

output_codec_context->time_base = input_codec_context->time_base;

output_codec_context->time_base.num *= input_codec_context->ticks_per_frame;

}

else {

output_codec_context->time_base = input_stream->time_base;

}

switch (input_codec_context->codec_type) {

case AVMEDIA_TYPE_AUDIO:

output_codec_context->channel_layout = input_codec_context->channel_layout;

output_codec_context->sample_rate = input_codec_context->sample_rate;

output_codec_context->channels = input_codec_context->channels;

output_codec_context->frame_size = input_codec_context->frame_size;

if ((input_codec_context->block_align == 1 && input_codec_context->codec_id == CODEC_ID_MP3) || input_codec_context->codec_id == CODEC_ID_AC3) {

output_codec_context->block_align = 0;

}

else {

output_codec_context->block_align = input_codec_context->block_align;

}

break;

case AVMEDIA_TYPE_VIDEO:

output_codec_context->pix_fmt = input_codec_context->pix_fmt;

output_codec_context->width = input_codec_context->width;

output_codec_context->height = input_codec_context->height;

output_codec_context->has_b_frames = input_codec_context->has_b_frames;

if (output_format_context->oformat->flags & AVFMT_GLOBALHEADER) {

output_codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;

}

break;

default:

break;

}

return output_stream;

}

typedef struct SMSegmentInfo

{

unsigned int index;

double duration;

char * filename;

} TSMSegmentInfo;

typedef struct SMPlaylist

{

/* a ring buffer of segments */

TSMSegmentInfo * buffer;

/* maximum number of segments that can be stored in the ring buffer */

unsigned int bufferCapacity;

/* index of the first segment on the ring buffer */

unsigned int first;

/* how many segments are currently in the ring buffer */

unsigned int count;

/* shortcuts */

unsigned int targetDuration;

char * httpPrefix;

/* playlist file used for non-live streaming */

FILE * file;

} TSMPlaylist;

static char *

duplicateString(const char * str)

{

/* unfortunately strdup isn't always available */

size_t strSize = strlen(str) + 1;

char * copy = (char *) malloc(strSize);

memcpy(copy, str, strSize);

return copy;

}

static TSMPlaylist *

createPlaylist(const unsigned int max_segments,

const unsigned int target_segment_duration,

const char * http_prefix)

{

TSMPlaylist * playlist = (TSMPlaylist *) malloc(sizeof(TSMPlaylist));

memset(playlist, 0, sizeof(TSMPlaylist));

if (max_segments)

{

playlist->buffer = (TSMSegmentInfo *) malloc(sizeof(TSMSegmentInfo) *

max_segments);

}

playlist->bufferCapacity = max_segments;

playlist->targetDuration = target_segment_duration;

playlist->httpPrefix = duplicateString(http_prefix);

return playlist;

}

static void

updateLivePlaylist(TSMPlaylist * playlist,

const char * playlistFileName,

const char * outputFileName,

const unsigned int segmentIndex,

const double segmentDuration)

{

unsigned int bufferIndex = 0;

TSMSegmentInfo * nextSegment = NULL;

TSMSegmentInfo removeMe;

memset(&removeMe, 0, sizeof(removeMe));

assert(!playlist->file);

if (playlist->count == playlist->bufferCapacity)

{

/* keep track of the segment that should be removed */

removeMe = playlist->buffer[playlist->first];

/* make room for the new segment */

playlist->first++;

playlist->first %= playlist->bufferCapacity;

}

else

{

playlist->count++;

}

/* store the new segment info */

bufferIndex = ((playlist->first + playlist->count - 1) %

playlist->bufferCapacity);

nextSegment = &playlist->buffer[bufferIndex];

nextSegment->filename = duplicateString(outputFileName);

nextSegment->duration = segmentDuration;

nextSegment->index = segmentIndex;

/* live streaming -- write full playlist from scratch */

playlist->file = fopen_utf8(playlistFileName, "w+b");

if (playlist->file)

{

unsigned int i, j;

const TSMSegmentInfo * first = &playlist->buffer[playlist->first];

char tmp[1024] = { 0 };

snprintf(tmp,

sizeof(tmp),

"#EXTM3U\n"

"#EXT-X-TARGETDURATION:%u\n"

"#EXT-X-MEDIA-SEQUENCE:%u\n",

playlist->targetDuration,

first->index);

fwrite(tmp, strlen(tmp), 1, playlist->file);

for ( i = 0; i < playlist->count; i++)

{

j = ((playlist->first + i) %

playlist->bufferCapacity);

const TSMSegmentInfo * segment = &playlist->buffer[j];

snprintf(tmp,

sizeof(tmp),

"#EXTINF:%u,\n%s%s\n",

(int)(segment->duration + 0.5),

playlist->httpPrefix,

segment->filename);

fwrite(tmp, strlen(tmp), 1, playlist->file);

}

// snprintf(tmp, sizeof(tmp), "#EXT-X-ENDLIST\n");

// fwrite(tmp, strlen(tmp), 1, playlist->file);

fclose(playlist->file);

playlist->file = NULL;

}

else

{

fprintf(stderr,

"Could not open m3u8 index file (%s), "

"no index file will be created\n",

playlistFileName);

}

if (removeMe.filename)

{

/* remove the oldest segment file */

remove(removeMe.filename);

free(removeMe.filename);

}

}

static void

updatePlaylist(TSMPlaylist * playlist,

const char * playlistFileName,

const char * segmentFileName,

const unsigned int segmentIndex,

const int segmentDuration)

{

if (playlist->bufferCapacity > 0)

{

/* create a live streaming playlist */

updateLivePlaylist(playlist,

playlistFileName,

segmentFileName,

segmentIndex,

segmentDuration);

}

else

{

/* append to the existing playlist */

char tmp[1024] = { 0 };

if (!playlist->file)

{

playlist->file = fopen_utf8(playlistFileName, "w+b");

snprintf(tmp,

sizeof(tmp),

"#EXTM3U\n"

"#EXT-X-TARGETDURATION:%u\n",

playlist->targetDuration);

fwrite(tmp, strlen(tmp), 1, playlist->file);

}

if (!playlist->file)

{

fprintf(stderr,

"Could not open m3u8 index file (%s), "

"no index file will be created\n",

playlistFileName);

}

snprintf(tmp,

sizeof(tmp),

"#EXTINF:%u,\n%s%s\n",

segmentDuration,

playlist->httpPrefix,

segmentFileName);

fwrite(tmp, strlen(tmp), 1, playlist->file);

fflush(playlist->file);

}

}

static void

closePlaylist(TSMPlaylist * playlist)

{

if (playlist->file)

{

/* append to the existing playlist */

char tmp[1024] = { 0 };

snprintf(tmp, sizeof(tmp), "#EXT-X-ENDLIST\n");

fwrite(tmp, strlen(tmp), 1, playlist->file);

fclose(playlist->file);

playlist->file = NULL;

}

}

static void

releasePlaylist(TSMPlaylist ** playlistRef)

{

TSMPlaylist * playlist = *playlistRef;

closePlaylist(playlist);

unsigned int i ;

for (i = 0; i < playlist->bufferCapacity; i++)

{

TSMSegmentInfo * segmentInfo = &playlist->buffer[i];

if (segmentInfo->filename)

{

free(segmentInfo->filename);

}

}

free(playlist->buffer);

free(playlist->httpPrefix);

free(playlist);

*playlistRef = NULL;

}

typedef struct SMPacketLink

{

/* packet start time in seconds */

double timeStamp;

/* the packet */

AVPacket packet;

/* a link to the next packet */

struct SMPacketLink * next;

} TSMPacketLink;

typedef struct SMPacketList

{

TSMPacketLink * head;

TSMPacketLink * tail;

unsigned int size;

} TSMPacketList;

typedef struct SMStreamLace

{

TSMPacketList ** streams;

unsigned int numStreams;

} TSMStreamLace;

static TSMPacketLink *

createLink(const AVPacket * packet, double timeStamp)

{

TSMPacketLink * link = (TSMPacketLink *) malloc(sizeof(TSMPacketLink));

link->timeStamp = timeStamp;

link->next = NULL;

memcpy(&link->packet, packet, sizeof(AVPacket));

return link;

}

static void

fifoPush(TSMPacketList * packets, const AVPacket * packet, double timeStamp)

{

TSMPacketLink * link = createLink(packet, timeStamp);

if (!packets->head)

{

assert(!packets->tail);

assert(!packets->size);

packets->head = link;

packets->tail = link;

packets->size = 1;

}

else

{

/* attach at the tail */

assert(packets->size > 0);

packets->tail->next = link;

packets->tail = link;

packets->size++;

}

}

static int

fifoPop(TSMPacketList * packets, AVPacket * packet)

{

TSMPacketLink * link = packets->head;

if (!link)

{

return 0;

}

memcpy(packet, &link->packet, sizeof(AVPacket));

packets->head = link->next;

packets->size--;

if (!packets->head)

{

packets->tail = NULL;

}

free(link);

return 1;

}

static TSMPacketList *

createPacketList()

{

TSMPacketList * packets = (TSMPacketList *)malloc(sizeof(TSMPacketList));

memset(packets, 0, sizeof(TSMPacketList));

return packets;

}

static TSMStreamLace *

createStreamLace(unsigned int numStreams)

{

unsigned int i;

TSMStreamLace * lace = (TSMStreamLace *)malloc(sizeof(TSMStreamLace));

lace->streams = (TSMPacketList **)malloc(sizeof(TSMPacketList *) * numStreams);

for ( i = 0; i < numStreams; i++)

{

lace->streams[i] = createPacketList();

}

lace->numStreams = numStreams;

return lace;

}

static void

insertPacket(TSMStreamLace * lace, const AVPacket * packet, double timeStamp)

{

fifoPush(lace->streams[packet->stream_index], packet, timeStamp);

}

static TSMPacketList *

chooseNextStream(TSMStreamLace * lace)

{

/* improve lacing so that that audio/video packets that should be

together do not get stuck into separate segments. */

TSMPacketList * nextStream = NULL;

double earliestTimeStamp = DBL_MAX;

unsigned int i;

for (i = 0; i < lace->numStreams; i++)

{

TSMPacketList * stream = lace->streams[i];

if (stream->size && stream->head->timeStamp < earliestTimeStamp)

{

nextStream = stream;

earliestTimeStamp = stream->head->timeStamp;

}

}

return nextStream;

}

static int

removePacket(TSMStreamLace * lace, AVPacket * packet)

{

TSMPacketList * stream = chooseNextStream(lace);

if (!stream)

{

return 0;

}

return fifoPop(stream, packet);

}

static unsigned int

countPackets(const TSMStreamLace * lace)

{

unsigned int numPackets = 0;

unsigned int i;

for ( i = 0; i < lace->numStreams; i++)

{

const TSMPacketList * stream = lace->streams[i];

numPackets += stream->size;

}

return numPackets;

}

static void

removeAllPackets(TSMStreamLace * lace)

{

unsigned int i;

AVPacket packet;

for ( i = 0; i < lace->numStreams; i++)

{

TSMPacketList * stream = lace->streams[i];

while (stream->size)

{

fifoPop(stream, &packet);

av_free_packet(&packet);

}

}

}

static int

loglevel(const char* arg)

{

const struct { const char *name; int level; } log_levels[] = {

{ "quiet" , AV_LOG_QUIET },

{ "panic" , AV_LOG_PANIC },

{ "fatal" , AV_LOG_FATAL },

{ "error" , AV_LOG_ERROR },

{ "warning", AV_LOG_WARNING },

{ "info" , AV_LOG_INFO },

{ "verbose", AV_LOG_VERBOSE },

{ "debug" , AV_LOG_DEBUG },

};

int i;

for (i = 0; i < FF_ARRAY_ELEMS(log_levels); i++) {

if (!strcmp(log_levels[i].name, arg)) {

av_log_set_level(log_levels[i].level);

return 0;

}

}

return 1;

}

//----------------------------------------------------------------

// usage3

//

static void usage3(char ** argv, const char * message, const char * details)

{

if (message)

{

fprintf(stderr, "ERROR: %s%s\n\n", message, details);

}

fprintf(stderr,

"USAGE: %s "

"-i input-MPEG-TS-file "

"-d seconds-per-segment "

"[-o segment-file-prefix] "

"-x output-playlist-m3u8 "

"[-p http-prefix] "

"[-w max-live-segments] "

"[-P pid-file] "

"[--watch-for-kill-file] "

"[--strict-segment-duration] "

"[--avformat-option opt value] "

"[--loglevel level] "

"\n\n",

argv[0]);

fprintf(stderr,

"Compiled by Daniel Espendiller - \n"

"build on %s %s with %s\n\n"

"Took some code from:\n"

" - source:\n"

" - iStreamdev:http://projects.vdr-developer.org/git/?p=istreamdev.git;a=tree;f=segmenter;hb=HEAD\n"

" - live_segmenter:\n",

__DATE__,

__TIME__,

__VERSION__);

exit(1);

}

//----------------------------------------------------------------

// usage

//

static void usage(char ** argv, const char * message)

{ usage3(argv, message, ""); }

//----------------------------------------------------------------

// main_utf8

//

int main_utf8(int argc, char **argv)

{

double target_segment_duration = 0.0;

char *segment_duration_check = NULL;

const char *playlist_filename = NULL;

const char *http_prefix = "";

long max_tsfiles = 0;

char *max_tsfiles_check = NULL;

double prev_segment_time = 0.0;

double segment_duration = 0.0;

unsigned int output_index = 0;

const AVClass *fc = avformat_get_class();

AVDictionary *format_opts = NULL;

AVOutputFormat *ofmt = NULL;

AVFormatContext *ic = NULL;

AVFormatContext *oc = NULL;

AVStream *video_st = NULL;

AVStream *audio_st = NULL;

AVCodec *codec = NULL;

char *pid_filename = NULL;

int video_index = -1;

int audio_index = -1;

int kill_file = 0;

int decode_done = 0;

int ret = 0;

int i = 0;

TSMStreamLace * streamLace = NULL;

TSMPlaylist * playlist = NULL;

const double segment_duration_error_tolerance = 0.05;

double extra_duration_needed = 0;

int strict_segment_duration = 0;

av_log_set_level(AV_LOG_INFO);

for (i = 1; i < argc; i++)

{

if (strcmp(argv[i], "-i") == 0)

{

if ((argc - i) <= 1)

usage(argv, "could not parse -i parameter");

i++;

strcpy(input_args.input, argv[i]);

}

else if (strcmp(argv[i], "-o") == 0)

{

if ((argc - i) <= 1)

usage(argv, "could not parse -i parameter");

i++;

strcpy(input_args.output_prefix, argv[i]);

}

else if (strcmp(argv[i], "-d") == 0)

{

if ((argc - i) <= 1) usage(argv, "could not parse -d parameter");

i++;

target_segment_duration = strtod(argv[i], &segment_duration_check);

if (segment_duration_check == argv[i] ||

target_segment_duration == HUGE_VAL ||

target_segment_duration == -HUGE_VAL)

{

usage3(argv, "invalid segment duration: ", argv[i]);

}

}

else if (strcmp(argv[i], "-x") == 0)

{

if ((argc - i) <= 1) usage(argv, "could not parse -x parameter");

i++;

playlist_filename = argv[i];

}

else if (strcmp(argv[i], "-p") == 0)

{

if ((argc - i) <= 1) usage(argv, "could not parse -p parameter");

i++;

http_prefix = argv[i];

}

else if (strcmp(argv[i], "-w") == 0)

{

if ((argc - i) <= 1) usage(argv, "could not parse -w parameter");

i++;

max_tsfiles = strtol(argv[i], &max_tsfiles_check, 10);

if (max_tsfiles_check == argv[i] ||

max_tsfiles < 0 ||

max_tsfiles >= INT_MAX)

{

usage3(argv, "invalid live stream max window size: ", argv[i]);

}

}

}

if (!input_args.input)

{

usage(argv, "-i input file parameter must be specified");

}

printf("+++input_file: %s\n", input_args.input);

if (!playlist_filename)

{

usage(argv, "-x m3u8 playlist file parameter must be specified");

}

printf("+++playlist_filename: %s\n", playlist_filename);

if (target_segment_duration == 0.0)

{

usage(argv, "-d segment duration parameter must be specified");

}

printf("+++target_segment_duration: %f\n", target_segment_duration);

av_register_all();

avformat_network_init();

if (!strcmp(input_args.input, "-")) {

strcpy(input_args.input, "pipe:");

}

playlist = createPlaylist(max_tsfiles,

target_segment_duration,

http_prefix);

if (!playlist)

{

fprintf(stderr, "Could not allocate space for m3u8 playlist structure\n");

goto error;

}

ic = avformat_alloc_context();

ret = avformat_open_input(&ic, input_args.input, NULL, NULL);

if (ret != 0) {

fprintf(stderr, "Could not open input file, make sure it is an mpegts or mp4 file: %d\n", ret);

goto error;

}

if (avformat_find_stream_info(ic, NULL) < 0) {

fprintf(stderr, "Could not read stream information\n");

goto error;

}

av_dump_format(ic, 0, input_args.input, 0);

ofmt = av_guess_format("mpegts", NULL, NULL);

if (!ofmt) {

fprintf(stderr, "Could not find MPEG-TS muxer\n");

goto error;

}

oc = avformat_alloc_context();

if (!oc) {

fprintf(stderr, "Could not allocated output context\n");

goto error;

}

oc->oformat = ofmt;

video_index = -1;

audio_index = -1;

for (i = 0; i < ic->nb_streams && (video_index < 0 || audio_index < 0); i++) {

switch (ic->streams[i]->codec->codec_type) {

case AVMEDIA_TYPE_VIDEO:

video_index = i;

ic->streams[i]->discard = AVDISCARD_NONE;

video_st = add_output_stream(oc, ic->streams[i]);

break;

case AVMEDIA_TYPE_AUDIO:

audio_index = i;

ic->streams[i]->discard = AVDISCARD_NONE;

audio_st = add_output_stream(oc, ic->streams[i]);

break;

default:

ic->streams[i]->discard = AVDISCARD_ALL;

break;

}

}

av_dump_format(oc, 0, input_args.output_prefix, 1);

if (video_index >=0) {

codec = avcodec_find_decoder(video_st->codec->codec_id);

if (!codec) {

fprintf(stderr, "Could not find video decoder, key frames will not be honored\n");

}

if (avcodec_open2(video_st->codec, codec, NULL) < 0) {

fprintf(stderr, "Could not open video decoder, key frames will not be honored\n");

}

}

sprintf(input_args.output, "%s-%u.ts", input_args.output_prefix, ++output_index);

if (avio_open(&oc->pb, input_args.output, AVIO_FLAG_WRITE) < 0) {

fprintf(stderr, "Could not open '%s'\n", input_args.output);

goto error;

}

if (avformat_write_header(oc, NULL)) {

fprintf(stderr, "Could not write mpegts header to first output file\n");

goto error;

}

prev_segment_time = (double)(ic->start_time) / (double)(AV_TIME_BASE);

streamLace = createStreamLace(ic->nb_streams);

do {

double segment_time = 0.0;

AVPacket packet;

double packetStartTime = 0.0;

double packetDuration = 0.0;

if (!decode_done)

{

decode_done = av_read_frame(ic, &packet);

if (!decode_done)

{

if (packet.stream_index != video_index &&

packet.stream_index != audio_index)

{

av_free_packet(&packet);

continue;

}

double timeStamp =

(double)(packet.pts) *

(double)(ic->streams[packet.stream_index]->time_base.num) /

(double)(ic->streams[packet.stream_index]->time_base.den);

if (av_dup_packet(&packet) < 0)

{

fprintf(stderr, "Could not duplicate packet\n");

av_free_packet(&packet);

break;

}

insertPacket(streamLace, &packet, timeStamp);

}

}

if (countPackets(streamLace) < 50 && !decode_done)

{

/* allow the queue to fill up so that the packets can be sorted properly */

continue;

}

if (!removePacket(streamLace, &packet))

{

if (decode_done)

{

/* the queue is empty, we are done */

break;

}

assert(decode_done);

continue;

}

packetStartTime =

(double)(packet.pts) *

(double)(ic->streams[packet.stream_index]->time_base.num) /

(double)(ic->streams[packet.stream_index]->time_base.den);

packetDuration =

(double)(packet.duration) *

(double)(ic->streams[packet.stream_index]->time_base.num) /

(double)(ic->streams[packet.stream_index]->time_base.den);

#if !defined(NDEBUG) && (defined(DEBUG) || defined(_DEBUG))

if (av_log_get_level() >= AV_LOG_VERBOSE)

fprintf(stderr,

"stream %i, packet [%f, %f)\n",

packet.stream_index,

packetStartTime,

packetStartTime + packetDuration);

#endif

segment_duration = packetStartTime + packetDuration - prev_segment_time;

// NOTE: segments are supposed to start on a keyframe.

// If the keyframe interval and segment duration do not match

// forcing the segment creation for "better seeking behavior"

// will result in decoding artifacts after seeking or stream switching.

if (packet.stream_index == video_index && (packet.flags & AV_PKT_FLAG_KEY || strict_segment_duration)) {

segment_time = packetStartTime;

}

else if (video_index < 0) {

segment_time = packetStartTime;

}

else {

segment_time = prev_segment_time;

}

printf("segment time: %f\n", segment_time);

if (segment_time - prev_segment_time + segment_duration_error_tolerance >

target_segment_duration + extra_duration_needed)

{

avio_flush(oc->pb);

avio_close(oc->pb);

// Keep track of accumulated rounding error to account for it in later chunks.

double segment_duration = segment_time - prev_segment_time;

int rounded_segment_duration = (int)(segment_duration + 0.5);

extra_duration_needed += (double)rounded_segment_duration - segment_duration;

updatePlaylist(playlist,

playlist_filename,

input_args.output,

output_index,

rounded_segment_duration);

sprintf(input_args.output, "%s-%u.ts", input_args.output_prefix, ++output_index);

if (avio_open(&oc->pb, input_args.output, AVIO_FLAG_WRITE) < 0) {

fprintf(stderr, "Could not open '%s'\n", input_args.output);

break;

}

// close when we find the 'kill' file

if (kill_file) {

FILE* fp = fopen("kill", "rb");

if (fp) {

fprintf(stderr, "user abort: found kill file\n");

fclose(fp);

remove("kill");

decode_done = 1;

removeAllPackets(streamLace);

}

}

prev_segment_time = segment_time;

}

ret = av_interleaved_write_frame(oc, &packet);

if (ret < 0) {

fprintf(stderr, "Warning: Could not write frame of stream\n");

}

else if (ret > 0) {

fprintf(stderr, "End of stream requested\n");

av_free_packet(&packet);

break;

}

av_free_packet(&packet);

} while (!decode_done || countPackets(streamLace) > 0);

av_write_trailer(oc);

if (video_index >= 0) {

avcodec_close(video_st->codec);

}

for(i = 0; i < oc->nb_streams; i++) {

av_freep(&oc->streams[i]->codec);

av_freep(&oc->streams[i]);

}

avio_close(oc->pb);

av_free(oc);

updatePlaylist(playlist,

playlist_filename,

input_args.output,

output_index,

segment_duration);

closePlaylist(playlist);

releasePlaylist(&playlist);

if (pid_filename)

{

remove(pid_filename);

}

return 0;

error:

if (pid_filename)

{

remove(pid_filename);

}

return 1;

}

int main(int argc, char ** argv)

{

return main_utf8(argc, argv);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值